溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

webpack2中怎么實現(xiàn)異步加載

發(fā)布時間:2021-06-17 15:04:13 來源:億速云 閱讀:203 作者:Leah 欄目:web開發(fā)

本篇文章為大家展示了webpack2中怎么實現(xiàn)異步加載,內(nèi)容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。

webpack提供的一個非常強大的功能就是code spliting(代碼切割)。

在webpack 1.x中提供了

  require.ensure([], () => {
    let module = require('./page1/module');
    // do something
  }, 'module1')

利用require.ensure這個API使得webpack單獨將這個文件打包成一個可以異步加載的chunk.

具體的套路見我寫的另一篇blog: webpack分包及異步加載套路

一句話總結(jié)就是:

在輸出的runtime代碼中,包含了異步chunk的id及chunk name的映射關(guān)系。需要異步加載相應的chunk時,通過生成script標簽,然后插入到DOM中完成chunk的加載。通過JSONP,runtime中定義好函數(shù),chunk加載完成后即會立即執(zhí)行這個函數(shù)。

從編譯生成后的代碼來看,webpack 1.x從chunk的加載到執(zhí)行的過程處理的比較粗糙,僅僅是通過添加script標簽,異步加載chunk后,完成函數(shù)的執(zhí)行。

這個過程當中,如果出現(xiàn)了chunk加載不成功時,這種情況下應該如何去容錯呢?

在webpack2中相比于webpack1.x在這個點的處理上是將chunk的加載包裹在了promise當中,那么這個過程變的可控起來。具體的webpack2實現(xiàn)套路也是本文想要去說明的地方。

webpack提供的異步加載函數(shù)是

  /******/   // This file contains only the entry chunk.
/******/   // The chunk loading function for additional chunks
      // runtime代碼里面只包含了入口的chunk
      // 這個函數(shù)的主要作用:
      // 1. 異步加載chunk
      // 2. 提供對于chunk加載失敗或者處于加載中的處理
      // 其中chunk加載狀態(tài)的判斷是根據(jù)installedChunks對象chunkId是數(shù)字0還是數(shù)組來進行判斷的
/******/   __webpack_require__.e = function requireEnsure(chunkId) {
        // 數(shù)字0代表chunk加載成功
/******/     if(installedChunks[chunkId] === 0) 
/******/       return Promise.resolve();

/******/     // an Promise means "currently loading".
        // 如果installedChunks[chunkId]為一個數(shù)組
/******/     if(installedChunks[chunkId]) {
          // 返回一個promise對象
/******/       return installedChunks[chunkId][2];
/******/     }
/******/     // start chunk loading
        // 通過生成script標簽來異步加載chunk.文件名是根據(jù)接受的chunkId來確認的
/******/     var head = document.getElementsByTagName('head')[0];
/******/     var script = document.createElement('script');
/******/     script.type = 'text/javascript';
/******/     script.charset = 'utf-8';
/******/     script.async = true;
        // 超時時間為120s
/******/     script.timeout = 120000;

/******/     if (__webpack_require__.nc) {
/******/       script.setAttribute("nonce", __webpack_require__.nc);
/******/     }
        // 需要加載的文件名
/******/     script.src = __webpack_require__.p + "js/register/" + ({"2":"index"}[chunkId]||chunkId) + ".js";
        // 120s的定時器,超時后觸發(fā)onScriptComplete回調(diào)
/******/     var timeout = setTimeout(onScriptComplete, 120000);
        // chunk加載完畢后的回調(diào)
/******/     script.onerror = script.onload = onScriptComplete;
/******/     function onScriptComplete() {
/******/       // avoid mem leaks in IE.
/******/       script.onerror = script.onload = null;
          // 清空定時器
/******/       clearTimeout(timeout);
          // 獲取這個chunk的加載狀態(tài)
          // 若為數(shù)字0,表示加載成功
          // 若為一個數(shù)組, 調(diào)用數(shù)組的第2個元素(第二個元素為promise內(nèi)傳入的reject函數(shù)),使得promise捕獲拋出的錯誤。reject(new Error('xxx'))
/******/       var chunk = installedChunks[chunkId];
/******/       if(chunk !== 0) {
/******/         if(chunk) chunk[1](new Error('Loading chunk ' + chunkId + ' failed.'));
/******/         installedChunks[chunkId] = undefined;
/******/       }
/******/     };
        
        // 每次需要進行異步加載chunk時,會將這個chunk的加載狀態(tài)進行初始化為一個數(shù)組,并以key/value的形式保存在installedChunks里
        // 這個數(shù)組為[resolve, reject, promise];
/******/     var promise = new Promise(function(resolve, reject) {
/******/       installedChunks[chunkId] = [resolve, reject];
/******/     });
/******/     installedChunks[chunkId][2] = promise;

/******/     head.appendChild(script);
        //返回promise
/******/     return promise;
/******/   };

我們再來看看路由配置文件編譯后生成的代碼index.js, 特別注意下__webpack_require__.e這個異步加載函數(shù):

Router
.home('path2')
.addRoute({
  path: 'path2',
  animate: 'zoomIn',
  viewBox: '.public-path2-container',
  template: __webpack_require__(5),
  // 掛載controller
  pageInit: function pageInit() {
    var _this = this;

    console.time('route async path2');
    // 異步加載0.js(這個文件是webpack通過code spliting自己生成的文件名)
    // 具體異步加載代碼的封裝見?分析
    // 其中0.js包含了包含了path2這個路由下的業(yè)務代碼
    // __webpack_require__.e(0) 起的作用僅為加載chunk以及提供對于chunk加載失敗錯誤的拋出
    // 具體的業(yè)務代碼的觸發(fā)是通過__webpack_require_e(0).then(__webpack_require__.bind(null, 8)).then(function(module) { ... })進行觸發(fā)
    // __webpack_require__.bind(null, 8) 返回的是module[8]暴露出來的module
    // 這段代碼執(zhí)行時,首先初始化一個module對象
    // module = {
    //    i: moduleId, // 模塊id
    //    l: false,   // 加載狀態(tài)
    //    exports: {}  // 需要暴露的對象
    //  }
    // 通過異步加載的chunk最后暴露出來的對象是作為了module.exports.default屬性
    // 因此在第二個方法中傳入的對象的default屬性才是你模塊8真正所暴露的對象
    __webpack_require__.e/* import() */(0).then(__webpack_require__.bind(null, 8)).then(function (module) {
      var controller = module.default;
      Router.registerCtrl('path2', new controller(_this.viewBox));
    // 添加錯誤處理函數(shù),用以捕獲前面可能拋出的錯誤
    }).catch(function (e) {
      return console.log('chunk loading failed');
    });
  },

  // 進入路由跳轉(zhuǎn)之前
  beforeEnter: function beforeEnter() {},

  // 路由跳轉(zhuǎn)前
  beforeLeave: function beforeLeave() {}
})
.addRoute({
  path: 'path3',
  viewBox: '.public-path3-container',
  animate: 'zoomIn',
  template: __webpack_require__(6),
  pageInit: function pageInit() {
    var _this2 = this;

    __webpack_require__.e/* import() */(1).then(__webpack_require__.bind(null, 9)).then(function (module) {
      console.time('route async path3');
      var controller = module.default;
      Router.registerCtrl('path3', new controller(_this2.viewBox));
    }).catch(function (e) {
      return console.log('chunk loading failed');
    });
  },
  beforeEnter: function beforeEnter() {},
  beforeLeave: function beforeLeave() {}
});

Router.bootstrap();

上述內(nèi)容就是webpack2中怎么實現(xiàn)異步加載,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI