CSS có nên luôn luôn ưu tiên Javascript?


897

Ở vô số nơi trực tuyến, tôi đã thấy đề xuất bao gồm CSS trước JavaScript. Lý do nói chung, của hình thức này :

Khi nói đến việc đặt hàng CSS và JavaScript của bạn, bạn muốn CSS của bạn đến trước. Lý do là luồng kết xuất có tất cả thông tin kiểu cần để hiển thị trang. Nếu JavaScript bao gồm trước, công cụ JavaScript phải phân tích tất cả trước khi tiếp tục với bộ tài nguyên tiếp theo. Điều này có nghĩa là chủ đề kết xuất không thể hiển thị hoàn toàn trang, vì nó không có tất cả các kiểu mà nó cần.

Thử nghiệm thực tế của tôi cho thấy một cái gì đó khá khác nhau:

Khai thác thử nghiệm của tôi

Tôi sử dụng tập lệnh Ruby sau để tạo độ trễ cụ thể cho các tài nguyên khác nhau:

require 'rubygems'
require 'eventmachine'
require 'evma_httpserver'
require 'date'

class Handler  < EventMachine::Connection
  include EventMachine::HttpServer

  def process_http_request
    resp = EventMachine::DelegatedHttpResponse.new( self )

    return unless @http_query_string

    path = @http_path_info
    array = @http_query_string.split("&").map{|s| s.split("=")}.flatten
    parsed = Hash[*array]

    delay = parsed["delay"].to_i / 1000.0
    jsdelay = parsed["jsdelay"].to_i

    delay = 5 if (delay > 5)
    jsdelay = 5000 if (jsdelay > 5000)

    delay = 0 if (delay < 0) 
    jsdelay = 0 if (jsdelay < 0)

    # Block which fulfills the request
    operation = proc do
      sleep delay 

      if path.match(/.js$/)
        resp.status = 200
        resp.headers["Content-Type"] = "text/javascript"
        resp.content = "(function(){
            var start = new Date();
            while(new Date() - start < #{jsdelay}){}
          })();"
      end
      if path.match(/.css$/)
        resp.status = 200
        resp.headers["Content-Type"] = "text/css"
        resp.content = "body {font-size: 50px;}"
      end
    end

    # Callback block to execute once the request is fulfilled
    callback = proc do |res|
        resp.send_response
    end

    # Let the thread pool (20 Ruby threads) handle request
    EM.defer(operation, callback)
  end
end

EventMachine::run {
  EventMachine::start_server("0.0.0.0", 8081, Handler)
  puts "Listening..."
}

Máy chủ mini ở trên cho phép tôi đặt độ trễ tùy ý cho các tệp JavaScript (cả máy chủ và máy khách) và độ trễ CSS tùy ý. Ví dụ, http://10.0.0.50:8081/test.css?delay=500cung cấp cho tôi độ trễ 500 ms khi chuyển CSS.

Tôi sử dụng trang sau để kiểm tra.

<!DOCTYPE html>
<html>
  <head>
      <title>test</title>
      <script type='text/javascript'>
          var startTime = new Date();
      </script>
      <link href="http://10.0.0.50:8081/test.css?delay=500" type="text/css" rel="stylesheet">
      <script type="text/javascript" src="http://10.0.0.50:8081/test2.js?delay=400&amp;jsdelay=1000"></script> 
  </head>
  <body>
    <p>
      Elapsed time is: 
      <script type='text/javascript'>
        document.write(new Date() - startTime);
      </script>
    </p>    
  </body>
</html>

Khi tôi đưa CSS vào trước, trang sẽ mất 1,5 giây để hiển thị:

CSS đầu tiên

Khi tôi đưa JavaScript vào trước, trang sẽ mất 1,4 giây để hiển thị:

JavaScript đầu tiên

Tôi nhận được kết quả tương tự trong Chrome, Firefox và Internet Explorer. Tuy nhiên, trong Opera, việc đặt hàng đơn giản không thành vấn đề.

Điều dường như đang xảy ra là trình thông dịch JavaScript từ chối bắt đầu cho đến khi tất cả CSS được tải xuống. Vì vậy, có vẻ như có JavaScript bao gồm đầu tiên sẽ hiệu quả hơn khi luồng JavaScript có nhiều thời gian chạy hơn.

Tôi có thiếu điều gì không, có phải đề xuất đặt CSS bao gồm trước JavaScript bao gồm không chính xác không?

Rõ ràng là chúng ta có thể thêm async hoặc sử dụng setTimeout để giải phóng chuỗi kết xuất hoặc đặt mã JavaScript ở chân trang hoặc sử dụng trình tải JavaScript. Vấn đề ở đây là về thứ tự các bit JavaScript và bit CSS cần thiết trong đầu.


120
1511 so với 1422 là một sự khác biệt có ý nghĩa thống kê? Đó là 6 phần trăm. Ngưỡng chung cho sự khác biệt hiệu suất đáng chú ý đến trung bình của con người là khoảng 20 phần trăm.
Jeff Atwood

15
vấn đề là việc sắp xếp lại giúp loại bỏ sự chậm trễ tùy ý này, bạn có thể đặt độ trễ thành bất cứ điều gì bạn muốn, đây chỉ là bản demo của vấn đề.
Sam Saffron

3
sự chậm trễ của bạn là 100ms? sự khác biệt trong ảnh chụp màn hình của bạn là 89ms. Trong URL của bạn, nó là delay=400&amp;jsdelay=1000delay=500không ở đâu gần 100ms hoặc 89ms. Tôi đoán tôi không rõ những con số bạn đang đề cập đến.
Jeff Atwood

4
"Nếu Javascript bao gồm trước, công cụ Javascript phải phân tích tất cả trước khi tiếp tục với bộ tài nguyên tiếp theo. Điều này có nghĩa là luồng kết xuất không thể hiển thị hoàn toàn trang, vì nó không có tất cả các kiểu mà nó cần . " - nếu bao gồm JS nằm trong phần đầu thì JS sẽ được thực thi trước khi trang được hiển thị bất kể CSS bao gồm trước hay sau.
nnnnnn

162
Không chắc chắn nếu bạn đã xem xét điều này, nhưng nhận thức về thời gian tải cũng quan trọng. Vì vậy, ví dụ, nếu việc tải CSS trước tiên cung cấp cho bạn thậm chí chỉ màu nền / kết cấu của trang, nó dường như sẽ nhanh hơn. Thời gian tải tuyệt đối có thể không phải là dấu hiệu của điều này.
Rakesh Pai

Câu trả lời:


712

Đây là một câu hỏi rất thú vị. Tôi đã luôn đặt CSS <link href="...">của mình trước mã JS <script src="...">của mình vì "Tôi đã đọc một lần rằng nó tốt hơn." Vì vậy, bạn đã đúng; đã đến lúc chúng ta thực hiện một số nghiên cứu thực tế!

Tôi thiết lập khai thác thử nghiệm của riêng tôi trong Node (mã bên dưới). Về cơ bản, tôi:

  • Đảm bảo không có bộ đệm HTTP để trình duyệt phải tải xuống đầy đủ mỗi khi trang được tải.
  • Để mô phỏng thực tế, tôi đã bao gồm jQuery và H5BP CSS (vì vậy có một lượng kịch bản / CSS kha khá để phân tích)
  • Thiết lập hai trang - một trang có CSS ​​trước tập lệnh, một trang có CSS ​​sau tập lệnh.
  • Ghi lại mất bao lâu cho tập lệnh bên ngoài <head>để thực thi
  • Ghi lại thời gian cần thiết cho tập lệnh nội tuyến <body>để thực thi, tương tự như vậy DOMReady.
  • Trì hoãn gửi CSS và / hoặc tập lệnh tới trình duyệt 500ms.
  • Chạy thử 20 lần trong 3 trình duyệt chính.

Các kết quả

Đầu tiên, với tệp CSS bị trễ 500ms:

     Browser: Chrome 18    | IE 9         | Firefox 9
         CSS: first  last  | first  last  | first last
=======================================================
Header Exec |              |              |
Average     | 583ms  36ms  | 559ms  42ms  | 565ms 49ms
St Dev      | 15ms   12ms  | 9ms    7ms   | 13ms  6ms
------------|--------------|--------------|------------
Body Exec   |              |              |
Average     | 584ms  521ms | 559ms  513ms | 565ms 519ms
St Dev      | 15ms   9ms   | 9ms    5ms   | 13ms  7ms

Tiếp theo, tôi đặt jQuery để trì hoãn 500ms thay vì CSS:

     Browser: Chrome 18    | IE 9         | Firefox 9
         CSS: first  last  | first  last  | first last
=======================================================
Header Exec |              |              |
Average     | 597ms  556ms | 562ms  559ms | 564ms 564ms
St Dev      | 14ms   12ms  | 11ms   7ms   | 8ms   8ms
------------|--------------|--------------|------------
Body Exec   |              |              |
Average     | 598ms  557ms | 563ms  560ms | 564ms 565ms
St Dev      | 14ms   12ms  | 10ms   7ms   | 8ms   8ms

Cuối cùng, tôi đặt cả jQuery và CSS để trì hoãn 500ms:

     Browser: Chrome 18    | IE 9         | Firefox 9
         CSS: first  last  | first  last  | first last
=======================================================
Header Exec |              |              |
Average     | 620ms  560ms | 577ms  577ms | 571ms 567ms
St Dev      | 16ms   11ms  | 19ms   9ms   | 9ms   10ms
------------|--------------|--------------|------------
Body Exec   |              |              |
Average     | 623ms  561ms | 578ms  580ms | 571ms 568ms
St Dev      | 18ms   11ms  | 19ms   9ms   | 9ms   10ms

Kết luận

Đầu tiên, điều quan trọng cần lưu ý là tôi đang hoạt động theo giả định rằng bạn có các tập lệnh nằm trong <head>tài liệu của bạn (trái ngược với phần cuối của tài liệu <body>). Có nhiều tranh luận liên quan đến lý do tại sao bạn có thể liên kết đến các tập lệnh của mình ở <head>phần cuối của tài liệu, nhưng điều đó nằm ngoài phạm vi của câu trả lời này. Đây là nghiêm túc về việc <script>s nên đi trước <link>s trong <head>.

Trong các trình duyệt DESKTOP hiện đại, có vẻ như liên kết với CSS trước tiên không bao giờ cung cấp hiệu suất. Đặt CSS sau tập lệnh sẽ mang lại cho bạn một mức tăng không đáng kể khi cả CSS và tập lệnh bị trì hoãn, nhưng mang lại cho bạn mức tăng lớn khi CSS bị trì hoãn. (Được hiển thị bởi các lastcột trong tập kết quả đầu tiên.)

Vì liên kết đến CSS lần cuối dường như không ảnh hưởng đến hiệu suất nhưng có thể mang lại lợi ích trong một số trường hợp nhất định, bạn nên liên kết với các bảng định kiểu bên ngoài sau khi bạn chỉ liên kết với các tập lệnh bên ngoài trên trình duyệt máy tính để bàn nếu hiệu suất của trình duyệt cũ không phải là vấn đề đáng lo ngại. Đọc về tình hình di động.

Tại sao?

Trước đây, khi một trình duyệt gặp phải một <script>thẻ trỏ đến một tài nguyên bên ngoài, trình duyệt sẽ dừng phân tích cú pháp HTML, truy xuất tập lệnh, thực thi nó, sau đó tiếp tục phân tích cú pháp HTML. Ngược lại, nếu trình duyệt gặp phải một biểu <link>định kiểu bên ngoài, nó sẽ tiếp tục phân tích cú pháp HTML trong khi nó tìm nạp tệp CSS (song song).

Do đó, lời khuyên được lặp đi lặp lại rộng rãi để đặt biểu định kiểu lên hàng đầu - chúng sẽ tải xuống trước và tập lệnh đầu tiên để tải xuống có thể được tải song song.

Tuy nhiên, các trình duyệt hiện đại (bao gồm tất cả các trình duyệt tôi đã thử nghiệm ở trên) đã triển khai phân tích cú pháp đầu cơ , trong đó trình duyệt "nhìn về phía trước" trong HTML và bắt đầu tải xuống tài nguyên trước khi tập lệnh tải xuống và thực thi.

Trong các trình duyệt cũ không có phân tích cú pháp đầu cơ, việc đặt tập lệnh lên trước sẽ ảnh hưởng đến hiệu suất do chúng sẽ không tải xuống song song.

Hỗ trợ trình duyệt

Phân tích cú pháp đầu cơ được triển khai lần đầu tiên trong: (cùng với tỷ lệ người dùng trình duyệt máy tính để bàn trên toàn thế giới sử dụng phiên bản này hoặc cao hơn kể từ tháng 1 năm 2012)

  • Chrome 1 (WebKit 525) (100%)
  • IE 8 (75%)
  • Firefox 3.5 (96%)
  • Safari 4 (99%)
  • Opera 11,60 (85%)

Tổng cộng, khoảng 85% trình duyệt máy tính để bàn đang sử dụng hiện nay hỗ trợ tải đầu cơ. Đặt các tập lệnh trước CSS sẽ có hiệu suất phạt đối với 15% người dùng trên toàn cầu ; YMMV dựa trên đối tượng cụ thể của trang web của bạn. (Và hãy nhớ rằng con số đó đang bị thu hẹp.)

Trên các trình duyệt di động, khó hơn một chút để có được các số chính xác chỉ đơn giản là do trình duyệt di động và bối cảnh hệ điều hành không đồng nhất như thế nào. Do kết xuất đầu cơ được triển khai trong WebKit 525 (phát hành tháng 3 năm 2008) và gần như mọi trình duyệt di động đáng giá đều dựa trên WebKit, chúng tôi có thể kết luận rằng "hầu hết" các trình duyệt di động nên hỗ trợ nó. Theo quirksmode , iOS 2.2 / Android 1.0 sử dụng WebKit 525. Tôi không biết Windows Phone trông như thế nào.

Tuy nhiên, tôi đã chạy thử nghiệm trên thiết bị Android 4 của mình và trong khi tôi thấy các con số tương tự với kết quả trên máy tính để bàn, tôi đã kết nối nó với trình gỡ lỗi từ xa mới tuyệt vời trong Chrome dành cho Android và tab Mạng cho thấy trình duyệt thực sự đang chờ để tải xuống CSS cho đến khi JavaScripts được tải hoàn toàn - nói cách khác, ngay cả phiên bản WebKit mới nhất cho Android cũng không xuất hiện để hỗ trợ phân tích cú pháp đầu cơ. Tôi nghi ngờ nó có thể bị tắt do CPU, bộ nhớ và / hoặc các ràng buộc mạng vốn có của thiết bị di động.

Tha thứ cho sự chậm chạp - đây là Q & D.

app.js

var express = require('express')
, app = express.createServer()
, fs = require('fs');

app.listen(90);

var file={};
fs.readdirSync('.').forEach(function(f) {
    console.log(f)
    file[f] = fs.readFileSync(f);
    if (f != 'jquery.js' && f != 'style.css') app.get('/' + f, function(req,res) {
        res.contentType(f);
        res.send(file[f]);
    });
});


app.get('/jquery.js', function(req,res) {
    setTimeout(function() {
        res.contentType('text/javascript');
        res.send(file['jquery.js']);
    }, 500);
});

app.get('/style.css', function(req,res) {
    setTimeout(function() {
        res.contentType('text/css');
        res.send(file['style.css']);
    }, 500);
});


var headresults={
    css: [],
    js: []
}, bodyresults={
    css: [],
    js: []
}
app.post('/result/:type/:time/:exec', function(req,res) {
    headresults[req.params.type].push(parseInt(req.params.time, 10));
    bodyresults[req.params.type].push(parseInt(req.params.exec, 10));
    res.end();
});

app.get('/result/:type', function(req,res) {
    var o = '';
    headresults[req.params.type].forEach(function(i) {
        o+='\n' + i;
    });
    o+='\n';
    bodyresults[req.params.type].forEach(function(i) {
        o+='\n' + i;
    });
    res.send(o);
});

css.html

<!DOCTYPE html>
<html>
    <head>
        <title>CSS first</title>
        <script>var start = Date.now();</script>
        <link rel="stylesheet" href="style.css">
        <script src="jquery.js"></script>
        <script src="test.js"></script>
    </head>
    <body>
        <script>document.write(jsload - start);bodyexec=Date.now()</script>
    </body>
</html>

js.html

<!DOCTYPE html>
<html>
    <head>
        <title>CSS first</title>
        <script>var start = Date.now();</script>
        <script src="jquery.js"></script>
        <script src="test.js"></script>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <script>document.write(jsload - start);bodyexec=Date.now()</script>
    </body>
</html>

test.js

var jsload = Date.now();


$(function() {
    $.post('/result' + location.pathname.replace('.html','') + '/' + (jsload - start) + '/' + (bodyexec - start));
});

jquery.js là jquery-1.7.1.min.js


137
Đây là một câu trả lời tuyệt vời, cảm ơn vì đã sử dụng khoa học! Theo kết quả của bạn "trong các trình duyệt hiện đại, có vẻ như liên kết với CSS trước tiên không bao giờ mang lại hiệu suất" , tôi nghĩ rằng câu trả lời cho tiêu đề câu hỏi là , lời khuyên cũ về CSS trước tiên rõ ràng là không hợp lệ.
Jeff Atwood

Về cập nhật của @ josh3736 về nghịch đảo trên thiết bị di động ... đây là một trường hợp để không nhảy súng về sự thay đổi đáng kể này. Tôi tò mò về cách thức hoạt động của trình duyệt di động khác (webkit, tắc kè, uy tín, đinh ba, v.v.) vì hiệu suất trong thiết bị di động thường quan trọng hơn.
scunliffe

1
Bạn cũng nên thử thêm một số chậm để in ra css / js, để mô phỏng tốc độ của một máy chủ chậm.
kirb

1
"Đầu tiên, điều quan trọng cần lưu ý là tôi đang hoạt động theo giả định rằng bạn có các tập lệnh nằm trong <head>tài liệu của bạn (trái ngược với phần cuối của tài liệu <body>)." Tôi sẽ nhấn mạnh điều đó sớm hơn nhiều trong câu trả lời, như ở trên cùng. Bao gồm một scripttrong headđó đề cập đến một tập tin bên ngoài gần như không bao giờ chính xác, từ hầu hết mọi quan điểm (chắc chắn không phải là một hiệu suất). Tôi không nhớ là đã từng phải làm điều đó trong cuộc sống thực. Dòng lẻ hoặc hai của tập lệnh nội tuyến có thể, nhưng đó là tất cả. Mặc định, không có lý do trái ngược rất tốt, nên là kết thúc của cơ thể.
TJ Crowder

1
Được bình chọn xuống, jQuery không phải là JavaScript và nó chỉ làm mờ thêm trang do đó kết quả sử dụng nó là không khách quan.
Giăng

303

Có hai lý do chính để đặt CSS trước JavaScript.

  1. Các trình duyệt cũ (Internet Explorer 6-7, Firefox 2, v.v.) sẽ chặn tất cả các lần tải xuống tiếp theo khi chúng bắt đầu tải xuống một tập lệnh. Vì vậy, nếu bạn đã a.jstheo dõi b.csshọ sẽ được tải xuống tuần tự: đầu tiên a sau đó b. Nếu bạn đã b.csstheo dõi a.jshọ sẽ được tải xuống song song để trang tải nhanh hơn.

  2. Không có gì được hiển thị cho đến khi tất cả các bảng định kiểu được tải xuống - điều này đúng trong tất cả các trình duyệt. Các tập lệnh khác nhau - chúng chặn kết xuất tất cả các thành phần DOM dưới thẻ tập lệnh trong trang. Nếu bạn đặt tập lệnh của mình vào ĐẦU thì có nghĩa là toàn bộ trang bị chặn hiển thị cho đến khi tất cả các bảng định kiểu và tất cả tập lệnh được tải xuống. Mặc dù việc chặn tất cả kết xuất cho các bảng định kiểu là điều hợp lý (vì vậy bạn sẽ có được kiểu dáng chính xác ngay lần đầu tiên và tránh đèn flash của nội dung chưa được chỉnh sửa FOUC), việc chặn kết xuất toàn bộ trang cho các tập lệnh là không hợp lý. Thông thường các tập lệnh không ảnh hưởng đến bất kỳ thành phần DOM nào hoặc chỉ một phần của các thành phần DOM. Tốt nhất là tải các tập lệnh càng thấp trong trang càng tốt hoặc thậm chí tốt hơn là tải chúng không đồng bộ.

Thật thú vị khi tạo các ví dụ với Cuzillion . Ví dụ: trang này có một tập lệnh trong CHÍNH vì vậy toàn bộ trang trống cho đến khi tải xuống xong. Tuy nhiên, nếu chúng ta di chuyển tập lệnh đến cuối khối BODY, thì tiêu đề trang sẽ hiển thị do các thành phần DOM đó xuất hiện phía trên thẻ SCRIPT, như bạn có thể thấy trên trang này .


10
Steve vinh dự đánh bại tôi để trả lời, nhưng tôi sẽ thêm một bài viết liên quan đến những gì anh ấy đề cập: stevesouders.com/blog/2009/04/27/ Kẻ
Juan Pablo Buritica

4
xem trình duyệt nào hỗ trợ asyncthuộc tính, mà Steve đang đề xuất ở đây khi anh ta nói "thậm chí tải chúng không đồng bộ tốt hơn" - stackoverflow.com/questions/1834077/ Lỗi
Jeff Atwood

Này, bạn có thể cho tôi biết lý do tại sao mọi người sẽ liên kết các tệp CSS với các @importchỉ thị không?
Josh Stodola

6
Nguồn nào cho 2) và nếu nó là sự thật, bạn có thể giải thích tại sao đôi khi một trang sẽ hoàn thành tải nội dung, thì CSS sẽ được áp dụng một hoặc hai giây sau không? (Điều này đã xảy ra, hiếm khi, trên các trang của riêng tôi nơi CSS nằm trong thẻ <head>)
Izkata

2
Vậy chúng ta nên đặt jQuery+ jQuery UI+ $(document).ready(function () { });ở cuối trang? Nó sẽ luôn làm việc như mong đợi?
Olivier Pons

42

Tôi sẽ không nhấn mạnh quá nhiều vào kết quả mà bạn có được, tôi tin rằng đó là chủ quan, nhưng tôi có lý do để giải thích cho bạn rằng tốt hơn là nên đưa CSS vào trước js.

Trong quá trình tải trang web của bạn, có hai kịch bản bạn sẽ thấy:

TRƯỜNG HỢP 1: màn hình trắng> trang web chưa được chỉnh sửa> trang web theo kiểu> tương tác> trang web theo kiểu và tương tác

CASE 2: màn hình trắng> trang web không được trang bị> tương tác> trang web theo kiểu> trang web theo kiểu và tương tác


tôi thành thật không thể tưởng tượng bất cứ ai chọn Trường hợp 2. Điều này có nghĩa rằng khách truy cập sử dụng các kết nối internet chậm sẽ phải đối mặt với một trang web không bị chặn, cho phép họ tương tác với nó bằng Javascript (vì đã được tải). Hơn nữa, lượng thời gian dành cho việc xem xét một trang web chưa được chỉnh sửa sẽ được tối đa hóa theo cách này. Tại sao mọi người muốn điều đó?

Nó cũng hoạt động tốt hơn khi các trạng thái jQuery

"Khi sử dụng các tập lệnh dựa trên giá trị của các thuộc tính kiểu CSS, điều quan trọng là phải tham chiếu các biểu định kiểu bên ngoài hoặc các thành phần kiểu nhúng trước khi tham chiếu các tập lệnh".

Khi các tệp được tải theo thứ tự sai (đầu tiên là JS, sau đó là CSS), mọi mã Javascript dựa trên các thuộc tính được đặt trong các tệp CSS (ví dụ: chiều rộng hoặc chiều cao của div) sẽ không được tải chính xác. Có vẻ như với thứ tự tải sai, các thuộc tính chính xác được 'đôi khi' biết đến Javascript (có lẽ điều này là do điều kiện cuộc đua?). Hiệu ứng này có vẻ lớn hơn hoặc nhỏ hơn tùy thuộc vào trình duyệt được sử dụng.


2
Làm thế nào bạn sẽ đi về việc đảm bảo tất cả các css đã được tải trước khi javascript thực thi? Bạn có thể? hoặc javascript của bạn phải đủ mạnh để xử lý tình huống trong đó các kiểu có thể không nhất thiết phải được tải.
Jonnio

@Jonnio Nếu JS của bạn có một phụ thuộc, thì bạn nên làm cho sự phụ thuộc đó rõ ràng. Nếu không, bạn sẽ luôn luôn có vấn đề thời gian hiếm. Các mô-đun ES6 là một cách tốt để thực hiện điều đó, nhưng có nhiều thư viện cũng có thể được sử dụng.
kmkemp

26

Các bài kiểm tra của bạn được thực hiện trên máy tính cá nhân của bạn hoặc trên máy chủ web? Đó là một trang trống, hay nó là một hệ thống trực tuyến phức tạp với hình ảnh, cơ sở dữ liệu, v.v.? Các tập lệnh của bạn đang thực hiện một hành động sự kiện di chuột đơn giản hay chúng là một thành phần cốt lõi để làm thế nào trang web của bạn hiển thị và tương tác với người dùng? Có một số điều cần xem xét ở đây và sự liên quan của các khuyến nghị này hầu như luôn trở thành quy tắc khi bạn tham gia vào phát triển web có năng lực cao.

Mục đích của quy tắc "đặt biểu định kiểu ở trên cùng và tập lệnh ở dưới cùng" là, nói chung, đó là cách tốt nhất để đạt được kết xuất tiến bộ tối ưu , điều này rất quan trọng đối với trải nghiệm người dùng.

Tất cả các mặt khác: giả sử thử nghiệm của bạn là hợp lệ và bạn thực sự đang tạo ra kết quả trái với các quy tắc phổ biến, thực sự sẽ không có gì đáng ngạc nhiên. Mỗi trang web (và mọi thứ cần thiết để làm cho toàn bộ nội dung xuất hiện trên màn hình của người dùng) đều khác nhau và Internet không ngừng phát triển.


1
Tôi đánh giá cao điểm bạn đang in đậm, nhưng OP đang nói về những gì xảy ra khi bạn thay đổi thứ tự với cả ở phía trên, không ở phía dưới.
nnnnnn

1
Do đó, "giả sử thử nghiệm [của anh ấy] là hợp lệ."
Skippr

21

Tôi bao gồm các tệp CSS trước Javascript vì một lý do khác.

Nếu Javascript của tôi cần thực hiện định cỡ động cho một số phần tử trang (đối với các trường hợp góc trong đó CSS ​​thực sự là chính ở phía sau) thì việc tải CSS sau khi JS bị lỗi có thể dẫn đến các điều kiện chạy đua, trong đó phần tử được thay đổi kích thước trước các kiểu CSS được áp dụng và do đó trông có vẻ kỳ lạ khi các kiểu cuối cùng cũng xuất hiện. Nếu tôi tải CSS trước đó, tôi có thể đảm bảo rằng mọi thứ chạy theo thứ tự dự định và bố cục cuối cùng là thứ tôi muốn.


2
Điều này sẽ phá vỡ một ngày trên một số trình duyệt. Tôi không đoán được.
jcolebrand

1
jcolebrand: Vâng, tôi nghĩ rằng tôi đã uống đủ cà phê khi tôi viết bài này. (Nhìn lại, tôi đoán những điều quan trọng chỉ là để tránh tải CSS động và đưa JS vào một sự kiện domReady nếu bạn cần thực hiện kích thước động)
hugomg

Các kịch bản không nên thay đổi bất kỳ màn hình. Đó là công việc CSS. HTML = content, CSS = Cách hiển thị nội dung, javascript thay đổi nội dung một cách linh hoạt. Ngoài ra js chỉ nên hành động sau (hoặc trong khi) DOMContentLoaded được kích hoạt với một số tình huống nhỏ nhưng rất cụ thể.
brunoais

@brunoais: Một số bố cục chỉ có thể được tạo bằng Javascript. Ví dụ: mọi thứ cần được thay đổi kích thước động phải được thực hiện thông qua Javascript và một số thứ (như có kích thước 100% - 20px) cần Javascript để được thực hiện một cách có thể di chuyển trong các trình duyệt cũ.
hugomg

@missingno Trong những trường hợp đó, chỉ cần sử dụng sự kiện DOMContentLoaded. Nhưng tôi hiểu ý của bạn. (IE ngu ngốc!)
brunoais

10

Đề xuất bao gồm CSS trước JavaScript không hợp lệ?

Không nếu bạn coi nó đơn giản là một khuyến nghị. Nhưng nếu bạn coi nó như một quy tắc cứng và nhanh?, Thì, nó không hợp lệ.

Từ https://developer.mozilla.org/en-US/docs/Web/Reference/Events/DOMContentLoaded

Biểu định kiểu tải thực thi tập lệnh chặn, vì vậy nếu bạn có <script> sau một <link rel="stylesheet" ...>trang sẽ không hoàn thành phân tích cú pháp - và DOMContentLoaded sẽ không kích hoạt - cho đến khi biểu định kiểu được tải.

Dường như bạn cần biết mỗi tập lệnh dựa vào cái gì và đảm bảo rằng việc thực thi tập lệnh bị trì hoãn cho đến sau khi sự kiện hoàn thành đúng. Nếu tập lệnh chỉ dựa vào DOM, nó có thể tiếp tục trong ondom yet / domcontentloaded, nếu nó dựa vào hình ảnh được tải hoặc các bảng định kiểu được áp dụng, thì nếu tôi đọc tham chiếu ở trên một cách chính xác, mã đó phải được hoãn lại cho đến khi sự kiện onload.

Tôi không nghĩ rằng một cỡ tất phù hợp với tất cả, mặc dù đó là cách chúng được bán và tôi biết rằng một cỡ giày không phù hợp với tất cả. Tôi không nghĩ rằng có một câu trả lời dứt khoát để tải trước, kiểu hoặc tập lệnh. Đây là một trường hợp tùy theo từng trường hợp quyết định những gì phải được tải theo thứ tự và những gì có thể được hoãn lại cho đến sau này khi không nằm trong "con đường quan trọng".

Để nói chuyện với người quan sát nhận xét rằng tốt hơn là trì hoãn khả năng tương tác của người dùng cho đến khi trang tính đẹp. Có rất nhiều bạn ngoài kia và bạn làm phiền các đối tác của mình mà cảm thấy ngược lại. Họ đã đến một trang web để thực hiện mục đích và trì hoãn khả năng tương tác với một trang web trong khi chờ đợi những thứ không quan trọng để tải xong là rất bực bội. Tôi không nói rằng bạn sai, chỉ có điều bạn nên biết rằng có một phe khác tồn tại không chia sẻ ưu tiên của bạn.

Câu hỏi này đặc biệt áp dụng cho tất cả các quảng cáo được đặt trên các trang web. Tôi rất thích nếu các tác giả trang web chỉ hiển thị div giữ chỗ cho nội dung quảng cáo và đảm bảo rằng trang web của họ đã được tải và tương tác trước khi đưa quảng cáo vào một sự kiện tải. Thậm chí sau đó tôi muốn thấy các quảng cáo được tải một cách thanh thản thay vì tất cả cùng một lúc vì chúng ảnh hưởng đến khả năng của tôi thậm chí cuộn nội dung trang web trong khi quảng cáo cồng kềnh đang tải. Nhưng đó chỉ là quan điểm của một người.

  • Biết người dùng của bạn và những gì họ đánh giá cao.
  • Biết người dùng của bạn và môi trường duyệt họ sử dụng.
  • Biết mỗi tệp làm gì và các điều kiện tiên quyết của nó là gì. Làm cho mọi thứ hoạt động sẽ được ưu tiên hơn cả tốc độ và đẹp.
  • Sử dụng các công cụ hiển thị cho bạn dòng thời gian mạng khi phát triển.
  • Kiểm tra trong từng môi trường mà người dùng của bạn sử dụng. Có thể cần phải tự động (phía máy chủ, khi tạo trang) thay đổi thứ tự tải dựa trên môi trường người dùng.
  • Khi nghi ngờ, thay đổi thứ tự và đo lại.
  • Có thể các kiểu và tập lệnh xen kẽ theo thứ tự tải sẽ là tối ưu; không phải tất cả cái này rồi cái kia
  • Thử nghiệm không chỉ là thứ tự để tải các tập tin mà còn ở đâu. Cái đầu? Trong cơ thể? Sau cơ thể? DOM đã sẵn sàng / được tải chưa? Nạp vào?
  • Xem xét các tùy chọn không đồng bộ và trì hoãn khi thích hợp để giảm độ trễ ròng mà người dùng sẽ trải nghiệm trước khi có thể tương tác với trang. Kiểm tra để xác định nếu họ giúp đỡ hoặc làm tổn thương.
  • Sẽ luôn có sự đánh đổi để xem xét khi đánh giá thứ tự tải tối ưu. Khá so với Responsive chỉ là một.

1
Bài viết được liên kết không còn tuyên bố "Biểu định kiểu tải thực thi tập lệnh khối". Điều đó không còn đúng nữa sao?
Greg

@Greg - Nó vẫn đúng. Các tập lệnh cần có khả năng truy vấn các thuộc tính DOM .style, vì vậy các bảng định kiểu vẫn chặn thực thi tập lệnh. Họ có thể không chặn tải tập lệnh , nếu họ thông minh, nhưng họ sẽ chặn các sự kiện script.onLoad.
Jimmy Breck-McKye

10

Cập nhật 2017-12-16

Tôi không chắc chắn về các bài kiểm tra trong OP. Tôi quyết định thử nghiệm một chút và cuối cùng đã phá vỡ một số huyền thoại.

Đồng bộ <script src...>sẽ chặn tải xuống các tài nguyên bên dưới cho đến khi được tải xuống và thực thi

Điều này không còn đúng nữa . Hãy xem thác nước được tạo bởi Chrome 63:

<head>
<script src="//alias-0.redacted.com/payload.php?type=js&amp;delay=333&amp;rand=1"></script>
<script src="//alias-1.redacted.com/payload.php?type=js&amp;delay=333&amp;rand=2"></script>
<script src="//alias-2.redacted.com/payload.php?type=js&amp;delay=333&amp;rand=3"></script>
</head>

Thanh tra mạng Chrome -> thác nước

<link rel=stylesheet> sẽ không chặn tải xuống và thực thi các tập lệnh bên dưới nó

Điều này là không chính xác . Biểu định kiểu sẽ không chặn tải xuống nhưng nó sẽ chặn thực thi tập lệnh ( giải thích nhỏ ở đây ). Hãy xem biểu đồ hiệu suất được tạo bởi Chrome 63:

<link href="//alias-0.redacted.com/payload.php?type=css&amp;delay=666" rel="stylesheet">
<script src="//alias-1.redacted.com/payload.php?type=js&amp;delay=333&amp;block=1000"></script>

Công cụ phát triển Chrome -> hiệu suất


Hãy ghi nhớ những điều trên, kết quả trong OP có thể được giải thích như sau:

CSS đầu tiên:

CSS Download  500ms:<------------------------------------------------>
JS Download   400ms:<-------------------------------------->
JS Execution 1000ms:                                                  <-------------------------------------------------------------------------------------------------->
DOM Ready   @1500ms:                                                                                                                                                      

Đầu tiên:

JS Download   400ms:<-------------------------------------->
CSS Download  500ms:<------------------------------------------------>
JS Execution 1000ms:                                        <-------------------------------------------------------------------------------------------------->
DOM Ready   @1400ms:                                                                                                                                            

Đó cũng là lý do tại sao document.write () là một trong những ý tưởng tồi tệ nhất từng được tạo ra cho HTMLDOM.
brunoais

1
The reason is that the script may want to get coordinates and other style-dependent properties of elements, like in the example above. Naturally, it has to wait for styles to load. javascript.info/ Nhật Tại sao không áp dụng giả định tương tự trong trường hợp JS trước? Không có ý nghĩa gì với tôi, thứ tự thực hiện JS không nói gì về mục đích của nó.
Thorsten Schöning

4

Tôi không chắc chắn chính xác thời gian thử nghiệm 'kết xuất' của bạn như sử dụng tập lệnh java. Tuy nhiên hãy xem xét điều này

Một trang trên trang web của bạn là 50k không phải là không có lý. Người dùng ở bờ đông trong khi máy chủ của bạn ở phía tây. MTU chắc chắn không phải là 10k nên sẽ có một vài chuyến đi qua lại. Có thể mất 1/2 giây để nhận trang và bảng định kiểu của bạn. Thông thường (đối với tôi) javascript (thông qua plugin jquery và như vậy) nhiều hơn CSS. Cũng có những gì xảy ra khi kết nối internet của bạn bị kẹt giữa chừng trên trang nhưng hãy bỏ qua điều đó (đôi khi nó xảy ra với tôi và tôi tin rằng css ám nhưng tôi không chắc chắn 100%).

Vì css nằm trong đầu nên có thể có các kết nối bổ sung để có được nó, điều đó có nghĩa là nó có khả năng có thể kết thúc trước khi trang này hoạt động. Dù sao trong quá trình nhập phần còn lại của trang và các tệp javascript (có nhiều byte hơn), trang này không bị chặn khiến trang web / kết nối xuất hiện chậm.

NGAY CẢ NẾU Trình thông dịch JS từ chối khởi động cho đến khi CSS hoàn thành thời gian để tải xuống mã javascript, đặc biệt là khi máy chủ đang cắt vào thời gian css sẽ khiến trang web trông không đẹp.

Đó là một tối ưu hóa nhỏ nhưng đó là lý do cho nó.


1
Máy chủ ở bờ biển phía đông, fwiw. Rõ ràng, bạn cũng không biết thực tế là bây giờ họ sử dụng CDN.
jcolebrand

3

Đây là một TÓM TẮT tất cả các câu trả lời chính ở trên (hoặc có thể bên dưới sau :)

Đối với các trình duyệt hiện đại, đặt css bất cứ nơi nào bạn thích nó. Họ sẽ phân tích tệp html của bạn (mà họ gọi là phân tích cú pháp đầu cơ ) và bắt đầu tải xuống css song song với phân tích cú pháp html.

Đối với các trình duyệt cũ, hãy đặt css lên hàng đầu (nếu bạn không muốn hiển thị trang trần trụi nhưng tương tác trước).

Đối với tất cả các trình duyệt, hãy đặt javascript càng xa trang càng tốt, vì nó sẽ dừng phân tích cú pháp html của bạn. Tốt nhất là tải xuống không đồng bộ (ví dụ: cuộc gọi ajax)

Ngoài ra, một số kết quả thử nghiệm cho một trường hợp cụ thể tuyên bố đặt javascript lên trước (trái với trí tuệ truyền thống đặt CSS lên trước) cho hiệu suất tốt hơn nhưng không có lý do logic nào được đưa ra và thiếu xác thực về khả năng áp dụng rộng rãi, vì vậy bạn có thể bỏ qua nó bây giờ

Vì vậy, để trả lời câu hỏi: Có. Đề xuất bao gồm CSS trước khi JS không hợp lệ đối với các trình duyệt hiện đại. Đặt CSS bất cứ nơi nào bạn thích và đặt JS vào cuối, càng tốt.


1

Steve Souder đã đưa ra một câu trả lời dứt khoát nhưng ...

Tôi tự hỏi liệu có vấn đề gì với cả bài kiểm tra gốc của Sam và sự lặp lại của Josh không.

Cả hai thử nghiệm dường như đã được thực hiện trên các kết nối có độ trễ thấp trong đó thiết lập kết nối TCP sẽ có chi phí không đáng kể.

Làm thế nào điều này ảnh hưởng đến kết quả của bài kiểm tra Tôi không chắc chắn và tôi muốn xem xét các thác nước cho các bài kiểm tra qua kết nối độ trễ 'bình thường' nhưng ...

Tệp đầu tiên được tải xuống sẽ nhận được kết nối được sử dụng cho trang html và tệp thứ hai được tải xuống sẽ có kết nối mới. (Xua tan những thay đổi đầu tiên mà năng động, nhưng nó không được thực hiện ở đây)

Trong các trình duyệt mới hơn, kết nối TCP thứ hai được mở một cách đặc biệt để giảm chi phí kết nối, trong các trình duyệt cũ hơn, điều này không đúng và kết nối thứ hai sẽ có chi phí mở.

Tôi không chắc chắn điều này sẽ ảnh hưởng đến kết quả của các bài kiểm tra như thế nào.


không tuân theo, trừ khi bạn có đường ống cực kỳ hiếm, bạn rất khó có thể giảm thiết lập kết nối ... đồng ý kiểm tra nên được lặp lại ở độ trễ thấp
Sam Saffron

Nếu bạn nhìn vào thác nước này, bạn có thể thấy Chrome mở ra một kết nối thứ hai trước khi webpagetest.org/result/iêu (IE9 thực hiện tương tự) ... Tôi đã nghĩ độ trễ bình thường cho các mục đích TCP thay vì thấp - sắp xếp môi trường đã được thử nghiệm trong?
Andy Davies

2
Re: "Steve Souder đã đưa ra một câu trả lời dứt khoát nhưng ..." Điều với sự phát triển của web là không có câu trả lời dứt khoát. :) Có 3-4 cách để tải tập lệnh và mọi thứ thay đổi. Ngữ nghĩa chính xác thực sự đáng lẽ phải dành cho Steve để nói "Đặt CSS trước JavaScript đồng bộ" Nếu không, mọi người đã hiểu sai bằng cách khái quát hóa vì đó là quy tắc cho tất cả các tập lệnh ...
hexalys

Có nhưng hầu hết mọi người chỉ bao gồm các kịch bản một cách đồng bộ vì vậy lời khuyên của Steve là tốt cho những người không quen biết.
Andy Davies

1

Tôi nghĩ điều này sẽ không đúng với tất cả các trường hợp. Vì css sẽ tải song song nhưng js không thể. Xem xét cho trường hợp tương tự,

Thay vì có một css đơn, hãy lấy 2 hoặc 3 tệp css và thử theo những cách này,

1) css..css..js 2) css..js..css 3) js..css..css

Tôi chắc chắn css..css..js sẽ cho kết quả tốt hơn tất cả những người khác.


0

Chúng tôi phải lưu ý rằng các trình duyệt mới đã hoạt động trên các công cụ Javascript, trình phân tích cú pháp của họ, v.v., tối ưu hóa các vấn đề về mã và đánh dấu phổ biến theo cách mà các vấn đề gặp phải trong các trình duyệt cổ như <= IE8 không còn phù hợp, không chỉ với liên quan đến đánh dấu mà còn sử dụng các biến JavaScript, bộ chọn thành phần, v.v. Tôi có thể thấy trong tương lai không xa, một tình huống mà công nghệ đã đạt đến điểm mà hiệu suất không thực sự là vấn đề nữa.


Hiệu suất luôn là một vấn đề. Tôi gần như bỏ qua rằng các trình duyệt không tuân theo thông số kỹ thuật tồn tại. Tôi chỉ cần chuẩn bị mã của mình sao cho mã theo sau thông số kỹ thuật hoạt động hết tốc độ và các mã khác tôi chỉ làm như vậy để nó hoạt động. Vì vậy, ví dụ, nếu nó chỉ hoạt động trong IE8, tất cả đều ổn.
brunoais

-5

Cá nhân, tôi sẽ không nhấn mạnh quá nhiều vào "trí tuệ dân gian" như vậy. Những gì có thể là sự thật trong quá khứ có thể không phải là sự thật bây giờ. Tôi cho rằng tất cả các hoạt động liên quan đến diễn giải và kết xuất của một trang web hoàn toàn không đồng bộ ("tìm nạp" một cái gì đó và "hành động theo nó" là hai điều hoàn toàn khác nhau có thể được xử lý bởi các luồng khác nhau, v.v. ) và trong mọi trường hợp hoàn toàn nằm ngoài tầm kiểm soát của bạn hoặc mối quan tâm của bạn.

Tôi muốn đặt các tham chiếu CSS trong phần "đầu" của tài liệu, cùng với mọi tham chiếu đến các tập lệnh bên ngoài. (Một số tập lệnh có thể yêu cầu được đặt trong cơ thể, và nếu vậy, bắt buộc chúng.)

Ngoài ra ... nếu bạn quan sát rằng "điều này dường như nhanh hơn / chậm hơn thế, trên trình duyệt này / trình duyệt đó", hãy coi quan sát này là một sự tò mò thú vị nhưng không liên quan và đừng để nó ảnh hưởng đến quyết định thiết kế của bạn. Quá nhiều thứ thay đổi quá nhanh. (Bất cứ ai cũng muốn đặt cược vào bao nhiêu phút trước khi nhóm Firefox ra mắt với một bản phát hành tạm thời khác của sản phẩm của họ? Yup, tôi cũng vậy.)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.