Tải javascript trong html

Một nguyên tắc khi chúng ta sắp xếp thứ tự các tệp JS [Javascript] thông thường cần phải theo thứ tự nhất định. Trình duyệt sau khi tải mã HTML về sẽ hiển thị các tập lệnh tệp theo thứ tự từ trên xuống. Vì vậy khi nhúng các tệp JS vào ta thường phải đặt chúng theo thứ tự như sau

VD. Đối chiếu với Jquery

Thư viện Jquery=>Các Plugin Jquery=>Script chứa đoạn mã Jquery

Theo thứ tự chúng ta đặt đúng giống như đoạn mã sau

  
    Defer Demo    
    
    
  
  
    ...
  

Vì vậy, nếu xảy ra sự cố khi đặt tập lệnh. js before file library thì sao?

Kết quả là chúng ta sẽ gặp lỗi trong bảng điều khiển của trình duyệt

tập lệnh. js. 1 Lỗi tham chiếu chưa bắt được. $ không được xác định
tại tập lệnh. js. 1

Cách load file js [Javascript]

Để khắc phục vấn đề này thì trong thẻ tag các bạn thêm thuộc tính defer. Khi sử dụng defer, nó sẽ báo cho trình duyệt là script này chỉ được load khi trang web đã tải và render hoàn thành.

Chú thích.  

  • Nó chỉ có tác dụng với thẻ tag sử dụng src để nhúng file js, chứ ko làm có tác dụng khi viết trực tiếp code javascript vào trong
  • Nó là một thuộc tính boolean và đứng 1 mình

Sau khi thêm defer chúng ta hoàn toàn có thể sử dụng đoạn mã mà không cần phải theo thứ tự thứ 1 nữa

Ngoài ra nếu bạn sử dụng jQuery thì còn 1 cách khác đó là sử dụng đoạn mã jQuery sau để tải tập lệnh. js sau khi trang web render thành công

Hai tệp này sẽ được tải lần thứ tự ở trên, khung tệp được tải trước, sau đó đến tệp ứng dụng

Sau khi 2 tập tin này được tải xong, Render sẽ khởi động tạo ra một Web Worker. Tương tự như Render, Worker cũng cần phải tải 2 tệp

  • file worker framework chứa các mã của framework để khởi động kết nối từ Worker tới Render và từ Worker tới Core
  • file worker app Chứa toàn bộ logic để điều khiển ứng dụng - tệp này được sinh ra từ mã của nhà phát triển

Với các yêu cầu như trên, chúng ta nên sắp xếp để tải các tệp Javascript như thế nào để tốc độ tải tệp là tốt nhất?

2. Chuẩn bị môi trường

Để giả lập thời gian tải các tệp JS, chúng tôi sẽ tạo ra một tệp

console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
6 có nội dung như sau

const express = require['express'];
const app = express[];
const port = 3000;

app.use[function [req, res, next] {
const timeout = req.query.timeout || 0;
setTimeout[next, timeout];
}];

app.get['/index.js', [req, res] => {
const env = req.query.env || 'web';
const timeout = req.query.timeout || 0;
res.header['Content-type', 'text/javascript'];
res.header['cache-control', 'max-age=604800'];
res.send[
`console.log["hello world ${timeout} from ${env} at", Date.now[] - self.START_TIME];`
];
}];

app.listen[port, [] => {
console.log[
`Example app listening at //localhost:${port}`
];
}];
Sao chép

Trong đoạn mã kể trên, chúng ta tạo ra một máy chủ chạy ở cổng 3000

Máy chủ này cho phép chúng ta truy vấn vào tệp

console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
7. Tệp
console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
7 nhận vào 2 tham số

  • console.log[
    'hello world ${timeout} from ${env}',
    Date.now[] - self.START_TIME
    ];
    9 là thời gian giả lập để máy chủ có thể trả về nội dung của tệp,
    console.log[
    'hello world ${timeout} from ${env}',
    Date.now[] - self.START_TIME
    ];
    9 được tính bằng giây. VD.
    console.log[
    'hello world 1 from web',
    Date.now[] - self.START_TIME
    ];
    1, tức là máy chủ sẽ mất 1 giây để trả về tệp nội dung
  • console.log[
    'hello world 1 from web',
    Date.now[] - self.START_TIME
    ];
    2 là môi trường mà kịch bản này sẽ được chạy, mặc định là
    console.log[
    'hello world 1 from web',
    Date.now[] - self.START_TIME
    ];
    3. on the value of
    console.log[
    'hello world 1 from web',
    Date.now[] - self.START_TIME
    ];
    2 that file
    console.log[
    'hello world ${timeout} from ${env}',
    Date.now[] - self.START_TIME
    ];
    7 will be return on the content as after

console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
Sao chép

Ví dụ. when request to

console.log[
'hello world 1 from web',
Date.now[] - self.START_TIME
];
6, server will return on content

console.log[
'hello world 1 from web',
Date.now[] - self.START_TIME
];
Sao chép

Ở đây, chúng ta giả vờ rằng các script trước đó đã khởi tạo sẵn một biến toàn cầu tên là

console.log[
'hello world 1 from web',
Date.now[] - self.START_TIME
];
7. Vì vậy, khi tập lệnh trên được chạy, tập lệnh có thể cho chúng ta biết, thời gian mà tập lệnh được trình duyệt đánh giá

3. Tải Javascript trên Chủ đề chính​

Để giả lập việc tải tệp trên Chủ đề chính, chúng ta sẽ tạo chỉ mục cho tệp. html as after

7Sao chép

Trong thẻ

console.log[
'hello world 1 from web',
Date.now[] - self.START_TIME
];
8, chúng ta chạy đoạn script để khởi động biến
console.log[
'hello world 1 from web',
Date.now[] - self.START_TIME
];
7. Sau đó, chúng ta đặt các thẻ
70 ở cuối thẻ 
71, mục đích là để trình duyệt khi tải tập lệnh thẻ này sẽ không chặn nội dung của 
71 trả về

Ở đây chúng ta load 2 script, script đầu tiên mất 2s để load, còn script tiếp theo chỉ tiền duy nhất 1s

Câu hỏi là, thời gian để trình duyệt tải xuống 2 tập lệnh trên sẽ là bao nhiêu?

Chúc mừng bạn, nếu câu trả lời của bạn là 2s. Bạn đã rất hiểu cách trình duyệt tải các tập lệnh đó. Trong giai đoạn phân tích cú pháp HTML, trình duyệt sẽ phát ra trang web này cần phải tải 2 tập lệnh. Và trình duyệt sẽ thực hiện tải 2 script này song song với nhau. Sau khi tải xong, trình duyệt mới thực hiện đánh giá các tập lệnh theo thứ tự mà chúng sắp xếp. Trong trường hợp này, mặc dù script với

console.log[
'hello world 1 from web',
Date.now[] - self.START_TIME
];
1 dù được tải xuống xong trước script với
74, tuy nhiên, do script với 
74 sắp xếp trước, nên code của script với 
74 vẫn được chạy trước

Browser will in ra console content as after

console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
7Sao chép

Chú thích. để kiểm tra hành vi đúng của trình duyệt, chúng ta cần chắc chắn rằng chỉ mục tải tệp là. html trên môi trường ẩn danh không có bộ nhớ cache của các yêu cầu, bởi vì sau khi các yêu cầu đã được lưu lại bộ nhớ cache rồi, thời gian tải xuống các yêu cầu sẽ không còn đúng nữa

4. Tải Javascript trên Worker​

Trên Chủ đề chính, trình duyệt sẽ tải các tập lệnh song song với nhau, vậy khi tải các tập lệnh trên Web Worker thì sao?

Để tải tập lệnh trên Web Worker, chúng ta có 2 tình huống

  • khởi tạo tập lệnh cho một Web Worker. Đây là tập lệnh được sử dụng để khởi động Web Worker, chúng ta sẽ tải tập lệnh này thông qua việc sử dụng hàm
    77
  • sau khi Web Worker được tạo ra, Web Worker có thể có nhu cầu muốn tải thêm các tập lệnh khác

2 tình huống này sẽ có những cách xử lý khác nhau

4. 1. Load scripts to start Web Worker​

Để khởi tạo một Web Worker, chúng ta sẽ sử dụng hàm

console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
9Sao chép

Để kiểm tra xem trình duyệt sẽ thực hiện việc tải url tập lệnh của Web Worker như thế nào, chúng ta chuẩn bị đoạn mã như sau

const express = require['express'];
const app = express[];
const port = 3000;

app.use[function [req, res, next] {
const timeout = req.query.timeout || 0;
setTimeout[next, timeout];
}];

app.get['/index.js', [req, res] => {
const env = req.query.env || 'web';
const timeout = req.query.timeout || 0;
res.header['Content-type', 'text/javascript'];
res.header['cache-control', 'max-age=604800'];
res.send[
`console.log["hello world ${timeout} from ${env} at", Date.now[] - self.START_TIME];`
];
}];

app.listen[port, [] => {
console.log[
`Example app listening at //localhost:${port}`
];
}];
0Sao chép

Tại đây, chúng ta giả lập tải một file worker. js ở cuối cùng, sau khi đã tải được các tệp js ở Chủ đề chính. File worker này mất 1s để tải xuống. Sau khi Web Worker được khởi động, Main Thread sẽ gửi một thông báo tới Worker để gửi thời gian mà Main Thread bắt đầu. Trên Worker, chúng ta sẽ kiểm tra thời gian mà Web Worker bắt đầu được chạy, từ khoảng thời gian này chúng ta có thể biết được trình duyệt đã thực hiện việc tải xuống Worker scripts ra sao

Tệp

78 có nội dung như sau

const express = require['express'];
const app = express[];
const port = 3000;

app.use[function [req, res, next] {
const timeout = req.query.timeout || 0;
setTimeout[next, timeout];
}];

app.get['/index.js', [req, res] => {
const env = req.query.env || 'web';
const timeout = req.query.timeout || 0;
res.header['Content-type', 'text/javascript'];
res.header['cache-control', 'max-age=604800'];
res.send[
`console.log["hello world ${timeout} from ${env} at", Date.now[] - self.START_TIME];`
];
}];

app.listen[port, [] => {
console.log[
`Example app listening at //localhost:${port}`
];
}];
2Sao chép

Nếu nhìn vào bảng điều khiển của trình duyệt, chúng ta sẽ thấy nội dung như sau

const express = require['express'];
const app = express[];
const port = 3000;

app.use[function [req, res, next] {
const timeout = req.query.timeout || 0;
setTimeout[next, timeout];
}];

app.get['/index.js', [req, res] => {
const env = req.query.env || 'web';
const timeout = req.query.timeout || 0;
res.header['Content-type', 'text/javascript'];
res.header['cache-control', 'max-age=604800'];
res.send[
`console.log["hello world ${timeout} from ${env} at", Date.now[] - self.START_TIME];`
];
}];

app.listen[port, [] => {
console.log[
`Example app listening at //localhost:${port}`
];
}];
3Sao chép

Điều này có nghĩa là. tại thời điểm trình duyệt nhận được chỉ thị

79, trình duyệt mới bắt đầu thực hiện việc tải xuống tập lệnh 
console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
70

Do đó, trình duyệt sẽ phải mất 3s để tải xuống cả 3 tập lệnh [bao gồm 2 tập lệnh

console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
71,
console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
72 và
console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
73]

Vì vậy, có cách nào để chúng tôi thông báo cho trình duyệt tải xuống tệp

78 trước khi chúng tôi khởi tạo Web Worker không?

Ở đây chúng ta có thể nghĩ đến 2 giải pháp, sử dụng thẻ

console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
75 để thông báo cho trình duyệt rằng chúng ta cần tải [download + parse] tệp
78 trước, hoặc sử dụng thẻ 
console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
77 để thông báo cho trình duyệt rằng chúng tôi cần tải xuống tệp
78 trước

Do

console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
79 không hỗ trợ loại tệp tải cho worker khởi tạo tệp, nên chúng tôi sử dụng thẻ
console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
90

Chúng ta sửa lại đoạn mã html như sau

console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
6Sao chép

Sau khi chạy xong, chúng ta có thể thấy giao diện điều khiển có nội dung như sau

console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
0Sao chép

Tuy nhiên, đáng buồn là khi sử dụng

console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
90 và
console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
79 để tải worker, thì cách làm này chỉ hoạt động với Chrome, còn không hoạt động với iOS. Trên iOS, tôi cũng không biết có cách nào tốt hơn để tải các tệp worker không. Nếu các bạn biết vui lòng chia sẻ cho mình với nhé

Một điểm rất hay của Chrome khi sử dụng

console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
90 đó là Chrome đủ thông minh để tận dụng lại các yêu cầu. Ví dụ, nếu thời gian để tải xuống
78 thay vì chỉ cần 1s, mà thành 4s, thì tại thời điểm trình duyệt nhận được chỉ thị 
console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
95, yêu cầu
console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
90 vẫn chưa hoàn thành. Dù vậy, Chrome sẽ không tạo yêu cầu mới nữa, mà đợi yêu cầu
console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
90 thực hiện xong rồi sử dụng kết quả của yêu cầu này để chạy tiếp

Do đó, bằng cách sử dụng

console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
90, chúng ta có thể chắc chắn rằng các tệp JS được tải xuống bài hát song song với nhau trên Chủ đề chính [tuy nhiên Trình duyệt vẫn giới hạn số lượng yêu cầu có thể tải xuống bài hát song song với nhau, mặc định chỉ Chrome

Do đó, nhìn chung để Web Worker có thể bắt đầu nhanh nhất có thể trên cả Android và iOS, cách làm tốt nhất vẫn là nên khởi tạo Web Worker càng sớm càng tốt

4. 2. Tải tập lệnh trong Web Worker​

Trong Web Worker, để có thể nhập được một tập lệnh, chúng tôi sử dụng hàm

console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
99

Ví dụ, với tệp

78 ở trên, chúng tôi muốn tải 2 tệp 
const express = require['express'];
const app = express[];
const port = 3000;

app.use[function [req, res, next] {
const timeout = req.query.timeout || 0;
setTimeout[next, timeout];
}];

app.get['/index.js', [req, res] => {
const env = req.query.env || 'web';
const timeout = req.query.timeout || 0;
res.header['Content-type', 'text/javascript'];
res.header['cache-control', 'max-age=604800'];
res.send[
`console.log["hello world ${timeout} from ${env} at", Date.now[] - self.START_TIME];`
];
}];

app.listen[port, [] => {
console.log[
`Example app listening at //localhost:${port}`
];
}];
01 và
const express = require['express'];
const app = express[];
const port = 3000;

app.use[function [req, res, next] {
const timeout = req.query.timeout || 0;
setTimeout[next, timeout];
}];

app.get['/index.js', [req, res] => {
const env = req.query.env || 'web';
const timeout = req.query.timeout || 0;
res.header['Content-type', 'text/javascript'];
res.header['cache-control', 'max-age=604800'];
res.send[
`console.log["hello world ${timeout} from ${env} at", Date.now[] - self.START_TIME];`
];
}];

app.listen[port, [] => {
console.log[
`Example app listening at //localhost:${port}`
];
}];
02, chúng tôi sẽ làm như sau

console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
1Sao chép

Ở đây chúng ta thực hiện việc tải các tập lệnh sau khi nhận được thông báo từ Main Thead

Chú ý rằng hàm

console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
99 là hàm đồng bộ, tức là khi hàm này chạy, trình duyệt phải đợi các script tải xong thì mới tiếp tục chạy các lệnh tiếp theo. Vì vậy với đoạn mã ở trên, chúng ta sẽ phải mất 700 mili giây để tải xuống tất cả các tập lệnh

Đoạn mã sẽ hiển thị trong màn hình nội dung như sau

console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
2Sao chép

Để giảm thời gian tải xuống, chúng ta có thể tiếp tục sử dụng

console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
90

console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
3Sao chép

Sau khi sử dụng

console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
90, bảng điều khiển sẽ có nội dung như sau

console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
4Sao chép

Tuy nhiên, cũng giống như ở phần 4. 1,

console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
90 không có tác dụng với iOS. Vì cách tốt hơn để nhập tập lệnh trong Worker đó là chúng ta tự viết một hàm nhập riêng

console.log[
'hello world ${timeout} from ${env}',
Date.now[] - self.START_TIME
];
5Sao chép

Tại đây, chúng ta thực hiện tải xuống các tập lệnh song song với nhau bằng cách khởi động các yêu cầu XHR riêng biệt. Sau khi download script xong, chúng ta mới đánh giá các script này. Bằng cách này, chúng ta có thể chắc chắn rằng trong một Web Worker, các tập lệnh có thể có thời gian tải xuống nhanh nhất có thể

Kết luận

Thông qua bài viết này, chúng tôi đã xem xét một số phương án để làm sao có thể tải các tệp Javascript trong Main Thread và trong Web Worker nhanh nhất có thể

Chủ Đề