资讯专栏INFORMATION COLUMN

解析 Webpack中import、require、按需加载的执行过程

caozhijian / 2325人阅读

摘要:但是浏览器是不识别这个关键词的所以会对的代码进行解释首先给设定导出的值如果是会直接赋值给如果是其他形式则给的导出的设定一个该的返回值就是导出的结果而对于来说整个执行过程其实过程和是一样的。

最近由于一篇分享手淘过年项目中采用到的前端技术的影响,重新研究了一下项目中CSS的架构.本来打算写一篇文章,但是写到一半突然发现自己像在写文档介绍一样,所以后来就放弃了。但是觉得过程中研究的 Webpack 倒是可以多带带拿出来讲一讲

在这里非常感谢印记中文 团队翻译的 Webpack 文档.
搭建一个简单环境

npm init

npm install css-loader html-webpack-plugin style-loader webpack webpack-cli

// Webpack 4.0
const htmlPlugin = require("html-webpack-plugin");

module.exports = {
  mode: "development",
  entry: "./src/index.js",
  output: {
    filename: "[name].js",
    path: __dirname + "/dist"
  },
  module: {
    rules: [
      {
        test: /.css$/,
        use: [
          {
            loader: "style-loader"
          },
          {
            loader: "css-loader",
          },
        ]
      }
    ]
  },
  plugins: [
    new htmlPlugin({
      title: "Test Webpack",
      filename: "index.html"
    })
  ]
};

一个基本的配置就搭建好了,详细的配置内容我就不介绍了, 然后我们在 src/index.js 上面写我们的测试代码, 在 dist/main.js 看一下 webpack 实现的原理,那么目前我们的项目结构是这样子的

|-- project
    |-- dist
    |-- src
        |-- index.js
    |-- node_modules
    |-- webpack.config.js
webpack 中 require 和 import 的执行过程

在进入按需加载的讲解之前,我们需要看一个问题 requireimportwebpack 的执行过程是怎样的呢 ?现在我们在 src建立两个文件 index.jsmodule-es6.jsmodule-commonjs.js。我们通过这三个文件解析 requireimport 的执行过程

首先我们要区分的是 CommonJSES6 模块导出之间的区别,在 CommonJS 中你导出模块方式是改变 module.exports,但是对于 ES6 来说并不存在 module 这个变量,他的导出方式是通过一个关键词 export来实现的。在我们书写 JS文件的时候,我们发现无论是以 CommomJS 还是 ES6 的形式导出都可以实现,这是因为 Webpack做了一个兼容处理

我们建立一个小 DEMO 来查看一下,我们现在上面建立的三个文件的代码如下

// index.js
// import moduleDefault, { moduleValue } from "./module-es6.js";
// import moduleDefault, { moduleValue1, moduleValue2 } from "./module-commanjs.js";
// module-es6.js
export let moduleValue = "moduleValue" //ES6模块导出
export default "ModuleDefaultValue"
// module-commonjs.js
exports.moduleValue1 = "moduleValue1"
exports.moduleValue2 = "moduleValue2"

现在我们打开 index.js 中加载 module-commonjs.js 的代码,首先会先给当前模块打上 ES6模块的标识符,在 index 则会产生两个变量 AB. A 保存 module-commonjs 的导出的结果,B 则是兼容 CommonJs中没有 ES6通过 export default导出的结果,其值跟 A一样. 用B来兼容 export default 的结果

然后我们重新注释代码,再打开 index.js 中加载 module-es6.js 的代码

这次和上面一样会先给当前模块打上 ES6模块的标识符,然后去加载 module-es6,获取他的导出值。但是浏览器是不识别 export 这个关键词的所以 Webpack 会对的代码进行解释,首先给 module.exports 设定导出的值,如果是 export default 会直接赋值给 module.exports,如果是其他形式,则给module.exports的导出的key设定一个 getter,该 getter 的返回值就是导出的结果

而对于require来说整个执行过程其实过程和import是一样的。

对于 webpack 来说只要你使用了 import 或者 export等关键字, 他就会给 module.exports添加一个__esModule : true 来识别这是一个 ES6的模块,通过这个值来做一些特殊处理

如果觉得我上面讲的不太明白 那可以看看下面这些代码

let commonjs = {
  "./src/index.js": function(module, __webpack_exports__, __webpack_require__) {
    "use strict";
    //给当前模块打上 `ES6`模块的标识符
    __webpack_require__.r(__webpack_exports__); //给当前模块打上 `ES6`模块的标识符

    // 执行 ./src/module-commonjs.js 的代码 获取导出值
    var A = __webpack_require__("./src/module-commonjs.js");

    // 根据 ./src/module-commonjs.js 是否为ES6模块 给返回值增加不同的 getter函数
    var B = __webpack_require__.n(A);
  },
  "./src/module-commonjs.js": function(module, exports) {
    exports.moduleValue1 = "moduleValue1";
    exports.moduleValue2 = "moduleValue2";
  }
};

let es6 = {
  "./src/index.js": function(module, __webpack_exports__, __webpack_require__) {
    "use strict";
    //给当前模块打上 `ES6`模块的标识符
    __webpack_require__.r(__webpack_exports__);

    // 执行 ./src/module-commonjs.js 的代码 获取导出值
    var A = __webpack_require__("./src/module-es6.js");
  },

  "./src/module-es6.js": function(module, __webpack_exports__, __webpack_require__) {
    //给当前模块打上 `ES6`模块的标识符
    __webpack_require__.r(__webpack_exports__);

    // 设置 __webpack_exports__.moduleValue 的 getter
    __webpack_require__.d(__webpack_exports__, "moduleValue", function() {
      return moduleValue;z
    });

    __webpack_exports__["default"] = "ModuleDefaultValue";

    let moduleValue = "moduleValue";
  }
};
按需加载的执行过程

看完上面的 requireimport,我们回到 按需加载 这个执行过程. webpack 的按需加载是通过 import() 或者 require.ensure()来实现的,有些读者可能对于 require.ensure 比较熟悉,所以我们先看看 require.ensure 的执行过程,
现在我们修改建立一个 module-dynamic.js文件,然后修改 index.js文件

这里吐槽一个问题,require.ensure 第一个参数是一个尴尬的存在,写和不写根本没差,如果你填了的这个参数,webpack 会帮你把文件加载近来,但是不执行。一堆不执行的代码是没有意义的,你想让他执行就必须 require() 一遍,但是执行力 require 也会帮你加载文件。所以根本没差
// index.js
setTimeout(function() {
  require.ensure([], function() {
    let d = require("./module2")
  });
}, 1000);

// module2.js
module.exports = {
  name : "Jason"
}

执行 require.ensure(dependencies,callback,errorCallback,chunkName) 实际上会返回一个 promise , 里面的实现逻辑是 先判断 dependencies 是否已经被加载过,如果加载过则取缓存值的 promise, 如果没有被加载过 则生成一个 promise 并将 promise 里面的 resolve,rejectpromise本身 存入一个数组,然后缓存起来.接着生成一个 script 标签,填充完信息之后添加到HTML文件上,其中的 scriptsrc属性 就是我们按需加载的文件(module2),webpack 会对这个 script 标签监听 errorload时间,从而做相应的处理。

webpack打包过程中会给 module2 添加一些代码,主要就是主动触发 window["webpackJsonp"].push这个函数,这个函数会传递
两个参数 文件ID文件内容对象,其中 文件标示如果没有配置的话,会按载入序号自动增长,文件内容对象实际上就是上文说的 require.ensure第一个参数dependencies的文件内容,或者是 callback,errorCallback里面需要加载的文件,以 key(文件路径) --- value(文件内容)的形式出现.里面执行的事情其实就是执行上面创建的promiseresolve函数,让require.ensure里面的callback执行,之后的执行情况就跟我上面将 requirimport 一样了

当然其实讲了那么长的 require.ensure并没有什么用,因为这个函数已经被 import() 取代了,但是考虑到之前的版本应该有很多人都是用 require.ensure 方法去加载的,所以还是讲一下,而且其实 import 的执行过程跟 require.ensure 是一样的,只不过用了更友好的语法而已,所以关于 import 的执行流程我也没啥好讲的了,感兴趣的人看一下两者的 API介绍就好了。

到这里就正式讲完了,如果有大牛路过看到有不对的地方,希望能帮我指出来.非常谢谢!!!

然后再次感谢印记中文 团队翻译的 Webpack 文档

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/93287.html

相关文章

  • 9102年:手写一个React脚手架 【优化极致版】

    摘要:马上要出了,完全手写一个优化后的脚手架是不可或缺的技能。每个依赖项随即被处理,最后输出到称之为的文件中,我们将在下一章节详细讨论这个过程。的事件流机制保证了插件的有序性,使得整个系统扩展性很好。 webpack马上要出5了,完全手写一个优化后的脚手架是不可或缺的技能。 本文书写时间 2019年5月9日 , webpack版本 4.30.0最新版本 本人所有代码均手写,亲自试验过可...

    bingo 评论0 收藏0
  • 9102年:手写一个React脚手架 【优化极致版】

    摘要:马上要出了,完全手写一个优化后的脚手架是不可或缺的技能。每个依赖项随即被处理,最后输出到称之为的文件中,我们将在下一章节详细讨论这个过程。的事件流机制保证了插件的有序性,使得整个系统扩展性很好。 webpack马上要出5了,完全手写一个优化后的脚手架是不可或缺的技能。 本文书写时间 2019年5月9日 , webpack版本 4.30.0最新版本 本人所有代码均手写,亲自试验过可...

    Kylin_Mountain 评论0 收藏0
  • 9102年:手写一个React脚手架 【优化极致版】

    摘要:马上要出了,完全手写一个优化后的脚手架是不可或缺的技能。每个依赖项随即被处理,最后输出到称之为的文件中,我们将在下一章节详细讨论这个过程。的事件流机制保证了插件的有序性,使得整个系统扩展性很好。 webpack马上要出5了,完全手写一个优化后的脚手架是不可或缺的技能。 本文书写时间 2019年5月9日 , webpack版本 4.30.0最新版本 本人所有代码均手写,亲自试验过可...

    whatsns 评论0 收藏0
  • 浅谈webpack4.0 性能优化

    摘要:中在性能优化所做的努力,也大抵围绕着这两个大方向展开。因此,将依赖模块从业务代码中分离是性能优化重要的一环。大型库是否可以通过定制功能的方式减少体积。这又违背了性能优化的基础。接下来可以抓住一些细节做更细的优化。中,为默认启动这一优化。 前言:在现实项目中,我们可能很少需要从头开始去配置一个webpack 项目,特别是webpack4.0发布以后,零配置启动一个项目成为一种标配。正因为...

    leanxi 评论0 收藏0
  • 【Vue项目总结】webpack常规打包优化方案

    摘要:由于新建项目发版打包时间大概需要分钟,发版时严重拖慢下班时间,所以特意查看了相关文档来优化打包速度,争取早点下班,。分析打包文件要优化,先分析。 由于新建项目发版打包时间大概需要30分钟,发版时严重拖慢下班时间,所以特意查看了相关文档来优化打包速度,争取早点下班,^_^。 分析打包文件 要优化,先分析。我们先要知道到底是哪里拖慢我们的打包速度呢? 打包后生成文件分析 可以利用webpa...

    andong777 评论0 收藏0

发表评论

0条评论

caozhijian

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<