tải các tập lệnh không đồng bộ


125

Tôi đang sử dụng một số plugin, widget tùy chỉnh và một số thư viện khác từ JQuery. kết quả là tôi có một số tệp .js và .css. Tôi cần tạo trình tải cho trang web của mình vì cần một thời gian để tải. sẽ rất tuyệt nếu tôi có thể hiển thị trình nạp trước khi nhập tất cả:

<script type="text/javascript" src="js/jquery-1.6.2.min.js"></script>
<script type="text/javascript" src="js/myFunctions.js"></script>
<link type="text/css" href="css/main.css" rel="stylesheet" />
... 
....
 etc

Tôi đã tìm thấy một số hướng dẫn cho phép tôi nhập thư viện JavaScript không đồng bộ. Ví dụ, tôi có thể làm điều gì đó như:

  (function () {
        var s = document.createElement('script');
        s.type = 'text/javascript';
        s.async = true;
        s.src = 'js/jquery-ui-1.8.16.custom.min.js';
        var x = document.getElementsByTagName('script')[0];
        x.parentNode.insertBefore(s, x);
    })();

vì một số lý do khi tôi làm điều tương tự cho tất cả các tệp của mình, các trang không hoạt động. Tôi đã cố gắng rất lâu để cố gắng tìm ra vấn đề ở đâu nhưng tôi không thể tìm ra nó. Đầu tiên tôi nghĩ rằng có thể là do một số hàm javascript phụ thuộc vào các hàm khác. nhưng tôi đã tải chúng theo đúng thứ tự bằng cách sử dụng chức năng hết thời gian khi một trang hoàn thành, tôi tiếp tục với trang tiếp theo và trang vẫn hoạt động kỳ lạ. ví dụ: tôi không thể nhấp vào các liên kết, v.v ... hoạt ảnh vẫn hoạt động mặc dù ..

Dù sao

Đây là những gì tôi đã suy nghĩ ... Tôi tin rằng các trình duyệt có bộ nhớ cache, đó là lý do tại sao phải mất nhiều thời gian để tải trang lần đầu tiên và những lần tiếp theo thì rất nhanh. vì vậy những gì tôi đang nghĩ đến là thay thế trang index.html của tôi bằng một trang tải tất cả các tệp này một cách không đồng bộ. khi ajax tải xong tất cả các tệp đó sẽ chuyển hướng đến trang mà tôi định sử dụng. khi sử dụng trang đó, sẽ không mất nhiều thời gian để tải vì các tệp sẽ được đưa vào bộ nhớ đệm của trình duyệt. trên trang chỉ mục của tôi (trang mà tệp .js và .css được tải không đồng bộ) Tôi không lo gặp lỗi. Tôi sẽ chỉ hiển thị trình tải và chuyển hướng trang khi hoàn tất ...

Ý tưởng này có phải là một sự thay thế tốt? hay tôi nên tiếp tục cố gắng triển khai các phương thức không đồng bộ?


BIÊN TẬP

cách tôi tải mọi thứ không đồng bộ giống như:

importScripts();

function importScripts()
{
    //import: jquery-ui-1.8.16.custom.min.js
    getContent("js/jquery-1.6.2.min.js",function (code) {
                var s = document.createElement('script');
                s.type = 'text/javascript';
                //s.async = true;
                s.innerHTML=code;
                var x = document.getElementsByTagName('script')[0];
                x.parentNode.insertBefore(s, x);
                setTimeout(insertNext1,1);
            });


    //import: jquery-ui-1.8.16.custom.min.js
    function insertNext1()
    {
        getContent("js/jquery-ui-1.8.16.custom.min.js",function (code) {
                    var s = document.createElement('script');
                    s.type = 'text/javascript';
                    s.innerHTML=code;
                    var x = document.getElementsByTagName('script')[0];
                    x.parentNode.insertBefore(s, x);
                    setTimeout(insertNext2,1);
                });
    }

    //import: jquery-ui-1.8.16.custom.css
    function insertNext2()
    {

        getContent("css/custom-theme/jquery-ui-1.8.16.custom.css",function (code) {
                    var s = document.createElement('link');
                    s.type = 'text/css';
                    s.rel ="stylesheet";
                    s.innerHTML=code;
                    var x = document.getElementsByTagName('script')[0];
                    x.parentNode.insertBefore(s, x);
                    setTimeout(insertNext3,1);
                });
    }

    //import: main.css
    function insertNext3()
    {

        getContent("css/main.css",function (code) {
                    var s = document.createElement('link');
                    s.type = 'text/css';
                    s.rel ="stylesheet";
                    s.innerHTML=code;
                    var x = document.getElementsByTagName('script')[0];
                    x.parentNode.insertBefore(s, x);
                    setTimeout(insertNext4,1);
                });
    }

    //import: jquery.imgpreload.min.js
    function insertNext4()
    {
        getContent("js/farinspace/jquery.imgpreload.min.js",function (code) {
                    var s = document.createElement('script');
                    s.type = 'text/javascript';
                    s.innerHTML=code;
                    var x = document.getElementsByTagName('script')[0];
                    x.parentNode.insertBefore(s, x);
                    setTimeout(insertNext5,1);
                });
    }


    //import: marquee.js
    function insertNext5()
    {
        getContent("js/marquee.js",function (code) {
                    var s = document.createElement('script');
                    s.type = 'text/javascript';
                    s.innerHTML=code;
                    var x = document.getElementsByTagName('script')[0];
                    x.parentNode.insertBefore(s, x);
                    setTimeout(insertNext6,1);
                });
    }


    //import: marquee.css
    function insertNext6()
    {

        getContent("css/marquee.css",function (code) {
                    var s = document.createElement('link');
                    s.type = 'text/css';
                    s.rel ="stylesheet";
                    s.innerHTML=code;
                    var x = document.getElementsByTagName('script')[0];
                    x.parentNode.insertBefore(s, x);
                    setTimeout(insertNext,1);
                });
    }



    function insertNext()
    {
        setTimeout(pageReadyMan,10);        
    }
}


// get the content of url and pass that content to specified function
function getContent( url, callBackFunction )
{
     // attempt to create the XMLHttpRequest and make the request
     try
     {
        var asyncRequest; // variable to hold XMLHttpRequest object
        asyncRequest = new XMLHttpRequest(); // create request object

        // register event handler
        asyncRequest.onreadystatechange = function(){
            stateChange(asyncRequest, callBackFunction);
        } 
        asyncRequest.open( 'GET', url, true ); // prepare the request
        asyncRequest.send( null ); // send the request
     } // end try
     catch ( exception )
     {
        alert( 'Request failed.' );
     } // end catch
} // end function getContent

// call function whith content when ready
function stateChange(asyncRequest, callBackFunction)
{
     if ( asyncRequest.readyState == 4 && asyncRequest.status == 200 )
     {
           callBackFunction(asyncRequest.responseText);
     } // end if
} // end function stateChange

và phần kỳ lạ là tất cả công việc của phong cách cộng với tất cả các hàm javascript. trang bị đóng băng vì một số lý do ...

Câu trả lời:


176

Một số giải pháp để tải không đồng bộ:

//this function will work cross-browser for loading scripts asynchronously
function loadScript(src, callback)
{
  var s,
      r,
      t;
  r = false;
  s = document.createElement('script');
  s.type = 'text/javascript';
  s.src = src;
  s.onload = s.onreadystatechange = function() {
    //console.log( this.readyState ); //uncomment this line to see which ready states are called.
    if ( !r && (!this.readyState || this.readyState == 'complete') )
    {
      r = true;
      callback();
    }
  };
  t = document.getElementsByTagName('script')[0];
  t.parentNode.insertBefore(s, t);
}

Nếu bạn đã có jQuery trên trang, chỉ cần sử dụng:

$.getScript(url, successCallback)*

Ngoài ra, có thể các tập lệnh của bạn đang được tải / thực thi trước khi tải xong tài liệu, nghĩa là bạn cần phải đợi document.readytrước khi các sự kiện có thể được liên kết với các phần tử.

Không thể nói cụ thể vấn đề của bạn là gì nếu không nhìn thấy mã.

Giải pháp đơn giản nhất là giữ tất cả các tập lệnh của bạn nội dòng ở cuối trang, theo cách đó chúng không chặn tải nội dung HTML trong khi thực thi. Nó cũng tránh vấn đề phải tải không đồng bộ từng tập lệnh bắt buộc.

Nếu bạn có một tương tác đặc biệt ưa thích không phải lúc nào cũng được sử dụng mà yêu cầu một tập lệnh lớn hơn thuộc một số loại, thì có thể hữu ích để tránh tải tập lệnh cụ thể đó cho đến khi cần (tải chậm).

* các tập lệnh được tải có $.getScriptthể sẽ không được lưu vào bộ nhớ đệm


Đối với bất kỳ ai có thể sử dụng các tính năng hiện đại như Promiseđối tượng, loadScriptchức năng đã trở nên đơn giản hơn đáng kể:

function loadScript(src) {
    return new Promise(function (resolve, reject) {
        var s;
        s = document.createElement('script');
        s.src = src;
        s.onload = resolve;
        s.onerror = reject;
        document.head.appendChild(s);
    });
}

Lưu ý rằng phiên bản này không còn chấp nhận một callbackđối số nữa vì lời hứa được trả về sẽ xử lý lệnh gọi lại. Những gì trước đây sẽ là loadScript(src, callback)bây giờ sẽ là loadScript(src).then(callback).

Điều này có thêm phần thưởng là có thể phát hiện và xử lý các lỗi, ví dụ như người ta có thể gọi ...

loadScript(cdnSource)
    .catch(loadScript.bind(null, localSource))
    .then(successCallback, failureCallback);

... và nó sẽ xử lý sự cố CDN một cách duyên dáng.


Tôi sắp bắt đầu thử điều đó. nhưng nếu tôi tải các tập lệnh đó trên một trang khác khi tôi chuyển hướng trang đó đến một trang khác và trang đó xảy ra có các tập lệnh giống nhau thì chúng sẽ không mất thời gian để tải. chúng phải ở trong bộ nhớ cache phải không? vậy tôi có nên thực hiện kỹ thuật đó không?
Tono Nam

1
Tôi đã thay đổi và nối con vào thẻ head thay vì body ... ..var x = document.getElementsByTagName ('head') [0]; x.appendChild (các);
Tono Nam

1
@TonoNam, bạn có thể chỉ cần sử dụng document.head.appendChild, tuy nhiên có một số vấn đề thích hợp khi thêm tập lệnh vào <head>; không có gì có thể ngăn các tập lệnh tải và thực thi.
zzzzBov

1
Tôi đã phải sử dụng t.parentNode.insertBefore (trái ngược với t.parent).
Phil LaNasa

1
@MuhammadUmer, nếu bạn đang viết thẻ tập lệnh bổ sung document.writetrước khi tải tài liệu, tập lệnh sẽ được thực thi đồng bộ như một phần của quá trình tải trang và sẽ không bao gồm bất kỳ hành vi gọi lại không đồng bộ nào. Nếu bạn đang tạo phần tử tập lệnh và thêm phần tử đó vào trang, thì bạn sẽ có quyền truy cập để thêm trình xử lý sự kiện để kích hoạt không đồng bộ. tl; dr: nó phụ thuộc vào cách bạn đang tải tập lệnh với JS.
zzzzBov

33

Thuộc tính 'async' mới của HTML5 được cho là có tác dụng. 'defer' cũng được hỗ trợ trong hầu hết các trình duyệt nếu bạn quan tâm đến IE.

async - HTML

<script async src="siteScript.js" onload="myInit()"></script>

trì hoãn - HTML

<script defer src="siteScript.js" onload="myInit()"></script>

Trong khi phân tích mã đơn vị quảng cáo adsense mới, tôi nhận thấy thuộc tính và tìm kiếm dẫn tôi đến đây: http://davidwalsh.name/html5-async


theo hiểu biết của tôi sau khi đọc stackoverflow.com/questions/2774373/… , asyn không phải để tải mà để xác định thời điểm chạy javascript có trong đó.
JackDev

9
Thuộc tính defer khiến trình duyệt trì hoãn việc thực thi tập lệnh cho đến khi tài liệu đã được tải và phân tích cú pháp và sẵn sàng được thao tác. Thuộc tính async làm cho trình duyệt để chạy các kịch bản càng sớm càng tốt nhưng không phải để phân tích tài liệu khối trong khi kịch bản đang được tải xuống
shuseel

1
"asyn không phải để tải, mà để xác định thời điểm chạy javascript chứa" Điều này không mâu thuẫn; Trong mọi trường hợp, trình duyệt sẽ bắt đầu tải tập lệnh càng sớm càng tốt. Nhưng không có async hoặc defernó sẽ chặn kết xuất trong khi tải. Cài đặt asyncsẽ cho biết nó không chặn và chạy tập lệnh càng sớm càng tốt. Cài đặt defersẽ yêu cầu nó không chặn nhưng hãy đợi chạy tập lệnh cho đến khi trang hoàn tất. Thông thường, không có vấn đề gì khi chạy các tập lệnh càng sớm càng tốt miễn là chúng không có bất kỳ phụ thuộc nào (ví dụ: jQuery). Nếu một tập lệnh có phụ thuộc vào tập lệnh khác, hãy sử dụng onLoadđể đợi nó.
Stijn de Witt

@StijndeWitt Điều gì xảy ra nếu Không đồng bộ hoặc hoãn lại được chỉ định?
Mohammed Shareef C

@MohammedShareefC Nếu bạn không chỉ định, bạn sẽ nhận được một cái gì đó gần giống với hành vi đồng bộ hóa; trình duyệt chặn xử lý thêm trong khi tải xuống và chạy tập lệnh. Vì vậy, hành vi mặc định hiệu quả là đồng bộ hóa. Nhưng vì điều này có hại cho hiệu suất, các trình duyệt sẽ làm việc xung quanh nó nhiều nhất có thể. Họ có thể tiếp tục phân tích cú pháp, tải và áp dụng CSS, v.v. Tuy nhiên, họ không thể bắt đầu chạy đoạn tập lệnh tiếp theo. Vì vậy, công cụ sẽ chặn. Bằng cách thiết lập không đồng bộ, bạn đang nói với trình duyệt rằng có thể tiếp tục và bạn sẽ đảm bảo rằng bạn tự làm cho nó hoạt động.
Stijn de Witt

8

Tôi đã tải các tập lệnh không đồng bộ (html 5 có tính năng đó) khi tất cả các tập lệnh được tải xong, tôi đã chuyển hướng trang đến index2.html trong đó index2.html sử dụng cùng các thư viện. Bởi vì trình duyệt có bộ nhớ cache khi trang chuyển hướng đến index2.html, index2.html tải trong vòng chưa đầy một giây vì nó có tất cả những gì cần thiết để tải trang. Trong trang index.html của mình, tôi cũng tải những hình ảnh mà tôi định sử dụng để trình duyệt đặt những hình ảnh đó vào bộ nhớ đệm. vì vậy index.html của tôi trông giống như:

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
    <title>Project Management</title>

    <!-- the purpose of this page is to load all the scripts on the browsers cache so that pages can load fast from now on -->

    <script type="text/javascript">

        function stylesheet(url) {
            var s = document.createElement('link');
            s.type = 'text/css';
            s.async = true;
            s.src = url;
            var x = document.getElementsByTagName('head')[0];
            x.appendChild(s);
        }

        function script(url) {
            var s = document.createElement('script');
            s.type = 'text/javascript';
            s.async = true;
            s.src = url;
            var x = document.getElementsByTagName('head')[0];
            x.appendChild(s);
        }

        //load scritps to the catche of browser
        (function () {            
                stylesheet('css/custom-theme/jquery-ui-1.8.16.custom.css');
                stylesheet('css/main.css');
                stylesheet('css/marquee.css');
                stylesheet('css/mainTable.css');

                script('js/jquery-ui-1.8.16.custom.min.js');
                script('js/jquery-1.6.2.min.js');
                script('js/myFunctions.js');
                script('js/farinspace/jquery.imgpreload.min.js');
                script('js/marquee.js');            
        })();

    </script>

    <script type="text/javascript">
       // once the page is loaded go to index2.html
        window.onload = function () {
            document.location = "index2.html";
        }
    </script>

</head>
<body>

<div id="cover" style="position:fixed; left:0px; top:0px; width:100%; height:100%; background-color:Black; z-index:100;">Loading</div>

<img src="images/home/background.png" />
<img src="images/home/3.png"/>
<img src="images/home/6.jpg"/>
<img src="images/home/4.png"/>
<img src="images/home/5.png"/>
<img src="images/home/8.jpg"/>
<img src="images/home/9.jpg"/>
<img src="images/logo.png"/>
<img src="images/logo.png"/>
<img src="images/theme/contentBorder.png"/>

</body>
</html>

Một điều thú vị khác về điều này là tôi có thể đặt một trình tải trong trang và khi trang tải xong, trình tải sẽ biến mất và trong một khoảng thời gian mili giây, trang mới sẽ chạy.


1
@MohammedShareefC Thật trớ trêu. Một trang đặc biệt chỉ để tải trước nội dung nghe có vẻ không phải là một ý tưởng hay khi SEO. Tải trước là rất tốt, nhưng bạn không cần phải chuyển hướng để đạt được nó. Chỉ cần gắn các liên kết vào một div ẩn và trình duyệt sẽ tự động xử lý nó cho bạn. Sau đó, không chuyển hướng mà chỉ hiển thị trang thực tế. NHỮNG NGƯỜI SAO CHÉP ĐỌC NÀY : Vui lòng thay đổi mã hóa ISO-88591 thành UTF-8 trước khi sử dụng bất kỳ HTML nào trong số này để những người còn lại trong chúng ta có thể sớm thoát khỏi địa ngục mã hóa bất cứ lúc nào. Cảm ơn!
Stijn de Witt

Vui lòng xem câu trả lời mới của tôi: stackoverflow.com/questions/7718935/load-scripts-asynchronously/…
asmmahmud

8

Ví dụ từ google

<script type="text/javascript">
  (function() {
    var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
    po.src = 'https://apis.google.com/js/plusone.js?onload=onLoadCallback';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
  })();
</script>

8

Một số lưu ý:

  • s.async = truekhông đúng lắm đối với loại tài liệu HTML5, đúng là s.async = 'async'(thực sự sử dụng true là đúng , cảm ơn amn đã chỉ ra trong bình luận ngay bên dưới)
  • Sử dụng thời gian chờ để kiểm soát đơn hàng không tốt và an toàn, và bạn cũng làm cho thời gian tải lớn hơn nhiều, bằng tổng tất cả thời gian chờ!

Vì gần đây có lý do để tải tệp không đồng bộ, nhưng theo thứ tự, tôi khuyên bạn nên console.logsử dụng cách theo hướng chức năng hơn một chút đối với ví dụ của bạn (xóa để sử dụng sản xuất :)):

(function() {
    var prot = ("https:"===document.location.protocol?"https://":"http://");

    var scripts = [
        "path/to/first.js",
        "path/to/second.js",
        "path/to/third.js"
    ];

    function completed() { console.log('completed'); }  // FIXME: remove logs

    function checkStateAndCall(path, callback) {
        var _success = false;
        return function() {
            if (!_success && (!this.readyState || (this.readyState == 'complete'))) {
                _success = true;
                console.log(path, 'is ready'); // FIXME: remove logs
                callback();
            }
        };
    }

    function asyncLoadScripts(files) {
        function loadNext() { // chain element
            if (!files.length) completed();
            var path = files.shift();
            var scriptElm = document.createElement('script');
            scriptElm.type = 'text/javascript';
            scriptElm.async = true;
            scriptElm.src = prot+path;
            scriptElm.onload = scriptElm.onreadystatechange = \
                checkStateAndCall(path, loadNext); // load next file in chain when
                                                   // this one will be ready 
            var headElm = document.head || document.getElementsByTagName('head')[0];
            headElm.appendChild(scriptElm);
        }
        loadNext(); // start a chain
    }

    asyncLoadScripts(scripts);
})();

4
s.async = trueđúng . Thuộc tính asyncđược chỉ định rõ ràng dưới dạng boolean trong đề xuất HTML 5 của W3C tại w3.org/TR/html5/scripting-1.html#attr-script-async . Bạn đang nhầm lẫn async thuộc tính với thuộc tính được asynchiển thị bởi các đối tượng triển khai HTMLScriptElementgiao diện DOM. Thật vậy, khi thao tác thuộc tính tương ứng của phần tử thông qua một cái gì đó giống như Element.setAttribute, bạn nên sử dụng element.setAttribute("async", "async")vì tất cả các thuộc tính HTML trước hết là văn bản.
amn

"Vì gần đây có lý do để tải tệp không đồng bộ, nhưng theo thứ tự, tôi khuyên bạn nên sử dụng cách theo hướng chức năng hơn một chút so với ví dụ của bạn [...]". Tôi biết async thường là "tốt hơn", nhưng lý do "gần đây" bạn đang nói đến là gì?
BluE

Tôi đã thay đổi 'if (! Files.length) complete ();' thêm 'return': 'if (! files.length) {complete (); return;} '
Fil

4

Nhờ HTML5, giờ đây bạn có thể khai báo các tập lệnh mà bạn muốn tải không đồng bộ bằng cách thêm "async" vào thẻ:

<script async>...</script>

Lưu ý: Thuộc tính async chỉ dành cho các tập lệnh bên ngoài (và chỉ nên được sử dụng nếu có thuộc tính src).

Lưu ý: Có một số cách mà một tập lệnh bên ngoài có thể được thực thi:

  • Nếu không đồng bộ xuất hiện: Tập lệnh được thực thi không đồng bộ với phần còn lại của trang (tập lệnh sẽ được thực thi trong khi trang tiếp tục phân tích cú pháp)
  • Nếu không có đồng bộ và hiện tại là trì hoãn: Tập lệnh được thực thi khi trang đã hoàn tất quá trình phân tích cú pháp
  • Nếu cả không đồng bộ hoặc trì hoãn đều không xuất hiện: Tập lệnh được tìm nạp và thực thi ngay lập tức, trước khi trình duyệt tiếp tục phân tích trang

Xem cái này: http://www.w3schools.com/tags/att_script_async.asp


2

Tôi sẽ hoàn thành câu trả lời của zzzzBov với sự kiểm tra sự hiện diện của callback và cho phép truyền các đối số:

    function loadScript(src, callback, args) {
      var s, r, t;
      r = false;
      s = document.createElement('script');
      s.type = 'text/javascript';
      s.src = src;
      if (typeof(callback) === 'function') {
        s.onload = s.onreadystatechange = function() {
          if (!r && (!this.readyState || this.readyState === 'complete')) {
            r = true;
            callback.apply(args);
          }
        };
      };
      t = document.getElementsByTagName('script')[0];
      t.parent.insertBefore(s, t);
    }

2

Đây là một giải pháp hiện đại tuyệt vời để tải tập lệnh không đồng bộ mặc dù nó chỉ giải quyết tập lệnh js với async false .

Có một bài báo tuyệt vời được viết trên www.html5rocks.com - Đi sâu vào vùng nước âm u của quá trình tải tập lệnh .

Sau khi xem xét nhiều giải pháp khả thi, tác giả kết luận rằng thêm js scripts vào cuối phần tử body là cách tốt nhất có thể để tránh việc chặn hiển thị trang bởi js scripts.

Đồng thời, tác giả đã thêm một giải pháp thay thế tốt khác cho những người đang muốn tải và thực thi các tập lệnh không đồng bộ.

Xem xét bạn đã đặt tên bốn tập lệnh script1.js, script2.js, script3.js, script4.jsthì bạn có thể làm điều đó với việc áp dụng async = false :

[
  'script1.js',
  'script2.js',
  'script3.js',
  'script4.js'
].forEach(function(src) {
  var script = document.createElement('script');
  script.src = src;
  script.async = false;
  document.head.appendChild(script);
});

Bây giờ, Spec nói : Tải xuống cùng nhau, thực thi theo thứ tự ngay khi tất cả tải xuống.

Firefox <3.6, Opera nói: Tôi không biết thứ “không đồng bộ” này là gì, nhưng nó cứ thế xảy ra khi tôi thực thi các tập lệnh được thêm qua JS theo thứ tự chúng được thêm vào.

Safari 5.0 cho biết: Tôi hiểu “async”, nhưng không hiểu việc đặt nó thành “false” với JS. Tôi sẽ thực thi các tập lệnh của bạn ngay khi chúng hạ cánh, theo bất kỳ thứ tự nào.

IE <10 cho biết: Không có ý tưởng về "async", nhưng có một giải pháp khác là sử dụng "onreadystatechange".

Mọi thứ khác nói: Tôi là bạn của bạn, chúng tôi sẽ làm điều này bằng cuốn sách.

Bây giờ, mã đầy đủ với IE <10 giải pháp:

var scripts = [
  'script1.js',
  'script2.js',
  'script3.js',
  'script4.js'
];
var src;
var script;
var pendingScripts = [];
var firstScript = document.scripts[0];

// Watch scripts load in IE
function stateChange() {
  // Execute as many scripts in order as we can
  var pendingScript;
  while (pendingScripts[0] && ( pendingScripts[0].readyState == 'loaded' || pendingScripts[0].readyState == 'complete' ) ) {
    pendingScript = pendingScripts.shift();
    // avoid future loading events from this script (eg, if src changes)
    pendingScript.onreadystatechange = null;
    // can't just appendChild, old IE bug if element isn't closed
    firstScript.parentNode.insertBefore(pendingScript, firstScript);
  }
}

// loop through our script urls
while (src = scripts.shift()) {
  if ('async' in firstScript) { // modern browsers
    script = document.createElement('script');
    script.async = false;
    script.src = src;
    document.head.appendChild(script);
  }
  else if (firstScript.readyState) { // IE<10
    // create a script and add it to our todo pile
    script = document.createElement('script');
    pendingScripts.push(script);
    // listen for state changes
    script.onreadystatechange = stateChange;
    // must set src AFTER adding onreadystatechange listener
    // else we’ll miss the loaded event for cached scripts
    script.src = src;
  }
  else { // fall back to defer
    document.write('<script src="' + src + '" defer></'+'script>');
  }
}

1

Một lý do tại sao các tập lệnh của bạn có thể tải quá chậm là nếu bạn đang chạy tất cả các tập lệnh của mình trong khi tải trang, như sau:

callMyFunctions();

thay vì:

$(window).load(function() {
      callMyFunctions();
});

Bit thứ hai của tập lệnh này đợi cho đến khi trình duyệt đã tải hoàn toàn tất cả mã Javascript của bạn trước khi bắt đầu thực thi bất kỳ tập lệnh nào của bạn, làm cho người dùng hiển thị rằng trang đã tải nhanh hơn.

Nếu bạn đang tìm cách nâng cao trải nghiệm của người dùng bằng cách giảm thời gian tải, tôi sẽ không chọn tùy chọn "tải màn hình". Theo ý kiến ​​của tôi, điều đó sẽ khó chịu hơn nhiều so với việc trang tải chậm hơn.


1

Tôi khuyên bạn nên xem qua Modernizr . Đây là một thư viện nhỏ gọn nhẹ mà bạn có thể tải javascript của mình một cách không đồng bộ với các tính năng cho phép bạn kiểm tra xem tệp đã được tải chưa và thực thi tập lệnh trong tập lệnh khác mà bạn chỉ định.

Đây là một ví dụ về tải jquery:

Modernizr.load([
  {
    load: '//ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.js',
    complete: function () {
      if ( !window.jQuery ) {
            Modernizr.load('js/libs/jquery-1.6.1.min.js');
      }
    }
  },
  {
    // This will wait for the fallback to load and
    // execute if it needs to.
    load: 'needs-jQuery.js'
  }
]);

1

Tôi đã viết một bài đăng nhỏ để trợ giúp vấn đề này, bạn có thể đọc thêm ở đây https://timber.io/snippets/asynchronously-load-a-script-in-the-browser-with-javascript/ , nhưng tôi đã đính kèm lớp người trợ giúp bên dưới. Nó sẽ tự động đợi một tập lệnh tải và trả về một thuộc tính cửa sổ được chỉ định khi nó thực hiện.

export default class ScriptLoader {
  constructor (options) {
    const { src, global, protocol = document.location.protocol } = options
    this.src = src
    this.global = global
    this.protocol = protocol
    this.isLoaded = false
  }

  loadScript () {
    return new Promise((resolve, reject) => {
      // Create script element and set attributes
      const script = document.createElement('script')
      script.type = 'text/javascript'
      script.async = true
      script.src = `${this.protocol}//${this.src}`

      // Append the script to the DOM
      const el = document.getElementsByTagName('script')[0]
      el.parentNode.insertBefore(script, el)

      // Resolve the promise once the script is loaded
      script.addEventListener('load', () => {
        this.isLoaded = true
        resolve(script)
      })

      // Catch any errors while loading the script
      script.addEventListener('error', () => {
        reject(new Error(`${this.src} failed to load.`))
      })
    })
  }

  load () {
    return new Promise(async (resolve, reject) => {
      if (!this.isLoaded) {
        try {
          await this.loadScript()
          resolve(window[this.global])
        } catch (e) {
          reject(e)
        }
      } else {
        resolve(window[this.global])
      }
    })
  }
}

Cách sử dụng như thế này:

const loader = new Loader({
    src: 'cdn.segment.com/analytics.js',
    global: 'Segment',
})

// scriptToLoad will now be a reference to `window.Segment`
const scriptToLoad = await loader.load()


0

Vâng, x.parentNodetrả về phần tử HEAD, vì vậy bạn đang chèn tập lệnh ngay trước thẻ head. Có lẽ đó là vấn đề.

Hãy thử x.parentNode.appendChild()thay thế.



0

Bạn có thể sử dụng LABJS hoặc RequreJS

Script bộ tải thích LABJS, RequireJSsẽ cải thiện tốc độ và chất lượng của mã của bạn.


0

Bạn đã cân nhắc sử dụng Fetch Injection chưa? Tôi đã triển khai một thư viện mã nguồn mở có tên là tìm nạp-tiêm để xử lý các trường hợp như thế này. Đây là giao diện của trình tải của bạn khi sử dụng lib:

fetcInject([
  'js/jquery-1.6.2.min.js',
  'js/marquee.js',
  'css/marquee.css',
  'css/custom-theme/jquery-ui-1.8.16.custom.css',
  'css/main.css'
]).then(() => {
  'js/jquery-ui-1.8.16.custom.min.js',
  'js/farinspace/jquery.imgpreload.min.js'
})

Để phát hiện tính tương thích ngược, hãy sử dụng tính năng phát hiện và quay lại XHR Injection hoặc Script DOM Elements, hoặc đơn giản chỉ cần nội tuyến các thẻ vào trang bằng cách sử dụng document.write.


0

Đây là giải pháp tùy chỉnh của tôi để loại bỏ JavaScript chặn hiển thị:

// put all your JS files here, in correct order
const libs = {
  "jquery": "https://code.jquery.com/jquery-2.1.4.min.js",
  "bxSlider": "https://cdnjs.cloudflare.com/ajax/libs/bxslider/4.2.5/jquery.bxslider.min.js",
  "angular": "https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0-beta.2/angular.min.js",
  "ngAnimate": "https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0-beta.2/angular-animate.min.js"
}
const loadedLibs = {}
let counter = 0

const loadAsync = function(lib) {
  var http = new XMLHttpRequest()
  http.open("GET", libs[lib], true)
  http.onload = () => {
    loadedLibs[lib] = http.responseText
    if (++counter == Object.keys(libs).length) startScripts()
  }
  http.send()
}

const startScripts = function() {
  for (var lib in libs) eval(loadedLibs[lib])
  console.log("allLoaded")
}

for (var lib in libs) loadAsync(lib)

Nói tóm lại, nó tải tất cả các tập lệnh của bạn một cách không đồng bộ và sau đó thực thi chúng.

Github repo : https://github.com/mudroljub/js-async-loader


1
Không hoạt độngNo 'Access-Control-Allow-Origin' header is present on the requested resource
Abram


0

Đây là một hàm nhỏ của ES6 nếu ai đó muốn sử dụng nó trong React chẳng hạn

import {uniqueId} from 'lodash' // optional
/**
 * @param {String} file The path of the file you want to load.
 * @param {Function} callback (optional) The function to call when the script loads.
 * @param {String} id (optional) The unique id of the file you want to load.
 */
export const loadAsyncScript = (file, callback, id) => {
  const d = document
  if (!id) { id = uniqueId('async_script') } // optional
  if (!d.getElementById(id)) {
    const tag = 'script'
    let newScript = d.createElement(tag)
    let firstScript = d.getElementsByTagName(tag)[0]
    newScript.id = id
    newScript.async = true
    newScript.src = file
    if (callback) {
      // IE support
      newScript.onreadystatechange = () => {
        if (newScript.readyState === 'loaded' || newScript.readyState === 'complete') {
          newScript.onreadystatechange = null
          callback(file)
        }
      }
      // Other (non-IE) browsers support
      newScript.onload = () => {
        callback(file)
      }
    }
    firstScript.parentNode.insertBefore(newScript, firstScript)
  } else {
    console.error(`The script with id ${id} is already loaded`)
  }
}


-3

Tôi khuyên bạn nên xem xét việc thu nhỏ các tệp trước và xem liệu điều đó có mang lại cho bạn tốc độ đủ lớn hay không. Nếu máy chủ của bạn chạy chậm, có thể thử đưa nội dung tĩnh đó lên CDN.

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.