【问题标题】:Async Loading of non-critical assets (Javascript, CSS)异步加载非关键资产(Javascript、CSS)
【发布时间】:2016-08-11 06:29:50
【问题描述】:

为了更快地加载网站,我们需要先让 DOM 加载,然后再获取网站的非关键资产。 场景是:

1) 优化 CSS 交付(DOM 加载后加载)

2) 移除阻止渲染的 JavaScript(各种库)并稍后加载它们。

3) 我们可能有多个页面,其中我们有依赖于我们正在使用的库的自定义代码。

4) 我们不需要使用requirejswebpack,因为我们唯一的要求是异步加载。

因此,我们需要一个纯 JavaScript 代码,它可以异步加载资产并在所有资产加载时触发事件。该事件可以被我们使用库函数的自定义代码监听。

【问题讨论】:

    标签: javascript performance asynchronous design-patterns


    【解决方案1】:

    我们的布局或页眉/页脚可以包含加载指定资产并在完成后触发事件的代码。

    <body>
    ...
    <script>
    // Callback after window is loaded
    if (window.addEventListener) {
      window.addEventListener("load", downloadFilesAtOnload, false);
    } else if (window.attachEvent) {
      window.attachEvent("onload", downloadFilesAtOnload);
    } else {
      window.onload = downloadFilesAtOnload;
    }
    
    function downloadFilesAtOnload() {
      // List of Javascript files to be loaded
      var jsFiles = ["js/library1.min.js", "js/library2.min.js", "CDN.library3.min.js"];
      loadScriptArray(jsFiles, function() {
        triggerEvent(document, 'scripts_loaded');
      });
    
      // List of CSS files to be loaded
      var robotoFonts = "https://fonts.googleapis.com/css?family=Roboto:400,300,300italic,400italic,500,500italic,700";
      var cssFiles = ["css/cssfile1.min.css", "css/cssfile2.min.css", robotoFonts];
      for (var i = 0; i < cssFiles.length; i++) {
        var l = document.createElement('link');
        l.rel = 'stylesheet';
        l.href = cssFiles[i];
        var h = document.getElementsByTagName('head')[0];
        h.parentNode.insertBefore(l, h);
      }
    }
    
    function loadScriptArray(contentArray, contentLoadedCallback){
      var contentQuantity = contentArray.length;      //Number of Files that needs to be loaded
      var contentCompleted = 0;                       //Number of Files, that have already been loaded
      for (var i = 0; i < contentQuantity; i++) {
        loadScript(contentArray[i], function(success, path){   //This anonymous function is called everytime a script is finished
          //The only way to know which script finished, is to pass the path as an parameter
          contentCompleted++;
          if (contentCompleted == contentQuantity){    //this was the last file
            if (typeof contentLoadedCallback == 'function'){
              contentLoadedCallback();
            }
          }
        });
      }
    }
    
    function loadScript(path, scriptLoadedCallback){
      element = document.createElement("script");
      element.src = path;
      element.async = false;
      if (typeof(scriptLoadedCallback) == 'function') {  //makes the callback-function optional
        element.onload = function() {
          return scriptLoadedCallback(true, path); //true = successfull; the path is needed later
        };
        element.onerror = function() {               //you might also call the cb on error
          this.parentNode.removeChild(this);       //remove faulty node from DOM
          return scriptLoadedCallback(false, path);      //false = error; the path is needed later
        };
      }
      document.body.appendChild(element);              //insert the node in DOM (end of <head>), and load the script
    }
    
    function processEvent(el, eventName, handler) {
      if (el.addEventListener) {
        el.addEventListener(eventName, handler);
      } else if (el.attachEvent) {
        el.attachEvent('on' + eventName, function() {
          handler.call(el);
        });
      }
    }
    
    function triggerEvent(el, eventName) {
      var event;
      if (window.CustomEvent) {
        event = new CustomEvent(eventName);
      } else {
        event = document.createEvent('CustomEvent');
        event.initCustomEvent(eventName);
      }
      el.dispatchEvent(event);
    }
    </script>
    </body>
    

    然后,我们可以在任何页面中使用依赖于外部文件/库的 javascript 代码,如下所示:

    <script>
    processEvent(document, 'scripts_loaded', function(e) {
        // Code here
    });
    </script>
    

    这个答案的灵感来自https://stackoverflow.com/a/36063024/3113599

    【讨论】:

      【解决方案2】:

      这是使用Javascript Promises的更好解决方案

      您的布局或页眉/页脚文件可以包含此代码。

      // Load css files
      var cssFiles = [{
          type: "CDN",
          src: "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css",
          integrity: "sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u",
      }, {
          type: "local",
          src: "css/cssfile1.min.css"
      }, {
          type: "local",
          src: "css/cssfile2.min.css"
      }, {
          type: "local",
          src: "https://fonts.googleapis.com/css?family=Roboto:400,300,300italic,400italic,500,500italic,700"
      }];
      var loadcssFiles = function() {
          cssFiles.forEach(function(file) {
              var style = document.createElement('link');
              style.rel = 'stylesheet';
              style.href = file.src;
              if (file.type == 'CDN') {
                  style.integrity = file.integrity;
                  style.crossOrigin = "anonymous";
              }
              document.head.appendChild(style);
          });
      }
      var raf = requestAnimationFrame || mozRequestAnimationFrame || webkitRequestAnimationFrame || msRequestAnimationFrame;
      if (raf) {
          raf(loadcssFiles);
      } else {
          window.addEventListener('load', loadcssFiles);
      }
      
      // Load Javascript files
      var jsFiles = [{
          index: 1,
          type: "CDN",
          src: "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js",
          integrity: "sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa",
      }, {
          index: 2,
          type: "local",
          src: "js/library1.min.js"
      }, {
          index: 3,
          type: "local",
          src: "js/library2.min.js"
      }];
      var loadjsFiles = new Promise(function(resolve, reject) {
          var failed;
          jsFiles.forEach(function(file) {
              var script = document.createElement('script');
              script.src = file.src;
              script.onerror = function() {
                  failed = true;
                  reject(this);
              }
              script.async = false;
              if (file.type == 'CDN') {
                  script.integrity = file.integrity;
                  script.crossOrigin = "anonymous";
              }
              document.head.appendChild(script);
              script.onload = script.onreadystatechange = function() {
                  if (!failed && (!this.readyState || this.readyState == "complete") && file.index == jsFiles.length ) {
                      resolve(this);
                  }
              }
          });
      });
      

      然后,我们可以在这样的任何页面中使用依赖于外部文件/库的 javascript 代码:

      loadjsFiles.then(function() {
          // Code here
      });
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-09-12
        • 1970-01-01
        • 2011-06-05
        • 1970-01-01
        相关资源
        最近更新 更多