zoukankan      html  css  js  c++  java
  • 【读书笔记】《深入浅出Webpack》

    Webpack版本

    分析版本为3.6.0
    4.0为最近升级的版本,与之前版本变化较大,编译输出的文件与3.0版本会不一致,目前项目中使用的版本3.0版本,所以基于3.0版本进行分析学习。

    Webpack构建流程

    • 初始化:启动构建,读取与合并配置参数,加载Plugin,实例化Complier
    • 编译:从Entry发出,针对每个Module串行调用对应的Loader去转换文件内容,再找到该Module依赖的Module,递归进行编译处理。
    • 输出:对编译后对Module组合成Chunk,把Chunk转换成文件,输出到文件系统。

    输出文件分析

    原输出文件结构:

    简化后的文件结构:

    (function(modules) {
      // 模拟 require 语句
      function __webpack_require__() {
      }
      // 执行存放所有模块数组中的第0个模块
      __webpack_require__(0);
    })([/*存放所有模块的数组*/])
    

    分割代码时输出

    当使用按需加载加载文件时,Webpack的输出文件会发生变化。

    // 异步加载 show.js
    import('./show').then((show) => {
      // 执行 show 函数
      show('Webpack');
    });
    

    重新构建后会输出两个文件,分别是执行入口文件bundle.js和异步加载文件0.bundle.js
    异步加载文件默认输入的文件名为 [id].js,可以在Webpack配置文件的output项中配置输出文件名
    其中0.bundle.js内容如下:

    // 加载在本文件(0.bundle.js)中包含的模块
    webpackJsonp(
      // 在其它文件中存放着的模块的 ID
      [0],
      // 本文件所包含的模块
      [
        // show.js 所对应的模块
        (function (module, exports) {
          function show(content) {
            window.document.getElementById('app').innerText = 'Hello,' + content;
          }
    
          module.exports = show;
        })
      ]
    );
    

    bundle.js内容如下:

    (function (modules) {
      /***
       * webpackJsonp 用于从异步加载的文件中安装模块。
       * 把 webpackJsonp 挂载到全局是为了方便在其它文件中调用。
       *
       * @param chunkIds 异步加载的文件中存放的需要安装的模块对应的 Chunk ID
       * @param moreModules 异步加载的文件中存放的需要安装的模块列表
       * @param executeModules 在异步加载的文件中存放的需要安装的模块都安装成功后,需要执行的模块对应的 index
       */
      window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {
        // 把 moreModules 添加到 modules 对象中
        // 把所有 chunkIds 对应的模块都标记成已经加载成功 
        var moduleId, chunkId, i = 0, resolves = [], result;
        for (; i < chunkIds.length; i++) {
          chunkId = chunkIds[i];
          if (installedChunks[chunkId]) {
            resolves.push(installedChunks[chunkId][0]);
          }
          installedChunks[chunkId] = 0;
        }
        for (moduleId in moreModules) {
          if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
            modules[moduleId] = moreModules[moduleId];
          }
        }
        while (resolves.length) {
          resolves.shift()();
        }
      };
    
      // 缓存已经安装的模块
      var installedModules = {};
    
      // 存储每个 Chunk 的加载状态;
      // 键为 Chunk 的 ID,值为0代表已经加载成功
      var installedChunks = {
        1: 0
      };
    
      // 模拟 require 语句,和上面介绍的一致
      function __webpack_require__(moduleId) {
        // ... 省略和上面一样的内容
      }
    
      /**
       * 用于加载被分割出去的,需要异步加载的 Chunk 对应的文件
       * @param chunkId 需要异步加载的 Chunk 对应的 ID
       * @returns {Promise}
       */
      __webpack_require__.e = function requireEnsure(chunkId) {
        // 从上面定义的 installedChunks 中获取 chunkId 对应的 Chunk 的加载状态
        var installedChunkData = installedChunks[chunkId];
        // 如果加载状态为0表示该 Chunk 已经加载成功了,直接返回 resolve Promise
        if (installedChunkData === 0) {
          return new Promise(function (resolve) {
            resolve();
          });
        }
    
        // installedChunkData 不为空且不为0表示该 Chunk 正在网络加载中
        if (installedChunkData) {
          // 返回存放在 installedChunkData 数组中的 Promise 对象
          return installedChunkData[2];
        }
    
        // installedChunkData 为空,表示该 Chunk 还没有加载过,去加载该 Chunk 对应的文件
        var promise = new Promise(function (resolve, reject) {
          installedChunkData = installedChunks[chunkId] = [resolve, reject];
        });
        installedChunkData[2] = promise;
    
        // 通过 DOM 操作,往 HTML head 中插入一个 script 标签去异步加载 Chunk 对应的 JavaScript 文件
        var head = document.getElementsByTagName('head')[0];
        var script = document.createElement('script');
        script.type = 'text/javascript';
        script.charset = 'utf-8';
        script.async = true;
        script.timeout = 120000;
    
        // 文件的路径为配置的 publicPath、chunkId 拼接而成
        script.src = __webpack_require__.p + "" + chunkId + ".bundle.js";
    
        // 设置异步加载的最长超时时间
        var timeout = setTimeout(onScriptComplete, 120000);
        script.onerror = script.onload = onScriptComplete;
    
        // 在 script 加载和执行完成时回调
        function onScriptComplete() {
          // 防止内存泄露
          script.onerror = script.onload = null;
          clearTimeout(timeout);
    
          // 去检查 chunkId 对应的 Chunk 是否安装成功,安装成功时才会存在于 installedChunks 中
          var chunk = installedChunks[chunkId];
          if (chunk !== 0) {
            if (chunk) {
              chunk[1](new Error('Loading chunk ' + chunkId + ' failed.'));
            }
            installedChunks[chunkId] = undefined;
          }
        };
        head.appendChild(script);
    
        return promise;
      };
    
      // 加载并执行入口模块,和上面介绍的一致
      return __webpack_require__(__webpack_require__.s = 0);
    })
    (
      // 存放所有没有经过异步加载的,随着执行入口文件加载的模块
      [
        // main.js 对应的模块
        (function (module, exports, __webpack_require__) {
          // 通过 __webpack_require__.e 去异步加载 show.js 对应的 Chunk
          __webpack_require__.e(0).then(__webpack_require__.bind(null, 1)).then((show) => {
            // 执行 show 函数
            show('Webpack');
          });
        })
      ]
    );
    

    这里的 bundle.js 和上面所讲的 bundle.js 非常相似,区别在于:

    • 多了一个 webpack_require.e 用于加载被分割出去的,需要异步加载的 Chunk 对应的文件;
    • 多了一个 webpackJsonp 函数用于从异步加载的文件中安装模块。

    参考

    深入浅出WebPack

  • 相关阅读:
    day01--计算机硬件基础笔记
    22 Jun 18 Django,ORM
    21 Jun 18 Django,ORM
    20 Jun 18 复习, mysql
    20 Jun 18 Django,ORM
    19 Jun 18 复习, 正则表达式
    19 Jun 18 Django
    15 Jun 18 复习, shutil模块
    15 Jun 18 Django
    14 Jun 18 复习, form表单
  • 原文地址:https://www.cnblogs.com/GeniusLyzh/p/8823749.html
Copyright © 2011-2022 走看看