资讯专栏INFORMATION COLUMN

webpack组织模块的原理 - external模块

Lavender / 2418人阅读

摘要:所以通常情况下当你的库需要依赖到例如,这样的通用模块时,我们可以不将它打包进,而是在的配置中声明这就是在告诉请不要将这个模块注入编译后的文件里,对于我源代码里出现的任何这个模块的语句,请将它保留。

这篇文章讨论Webpack打包library时经常需要用到的一个选项external,它用于避免将一些很通用的模块打包进你发布的library里,而是选择把它们声明成external的模块,在你的library被上层使用后,在最后阶段由Webpack统一把这个external的依赖模块打包进来。

external选项一般都是用在打包library上面,如果不是library而是一个最终的app的发布JS文件,那external也没有什么意义。关于Webpack打包library的分析和一些选项的作用,我在前一篇文章做了讨论。

external选项

我们仍然使用前一篇文章的例子,定义一个库util.js

import $ from "jquery"

function hideImages() {
  $("img").hide();
}

export default {
  "hideImages": hideImages
}

我们使用Webpack打包发布这个库:

// 入口文件
entry: {
  util: "./util.js",
}

// 输出文件
output: {
  path: "./dist",
  filename: "[name].dist.js"

  library: "util",
  libraryTarget: commonjs2,
  targetExport: "default"
}

这样打包出来的util.dist.js文件会把jquery的代码完整地注入进去,因为你的源代码使用到了它。但是这往往并不是我们希望的,因为jquery是很通用的模块,在一个app中,很可能其它的库也会用到它,最顶层的入口文件app也可能用到它,如果每一个库模块的发布版本都将jquery原封不动地打包进了自己的bundle,最后拼到一起,在最终的app发布代码里就会有很多份jquery的复制,当然这可能并不会影响它的正常功能,但是会占据很大的代码体积。

所以通常情况下当你的库需要依赖到例如jquerybootstrap这样的通用JS模块时,我们可以不将它打包进bundle,而是在Webpack的配置中声明external

externals: {
  jquery: {
    root: "jquery",
    commonjs: "jquery",
    commonjs2: "jquery",
    amd: "jquery",
  },
},

这就是在告诉Webpack:请不要将这个模块注入编译后的JS文件里,对于我源代码里出现的任何import/require这个模块的语句,请将它保留

我们可以看一下编译后的bundle文件的结构:

module.exports = (function(modules) {
  var installedModules = {};
  function webpack_require(moduleId) {
     // ...
  }
  return webpack_require("./util.js");
}) ({
  "./util.js": generated_util,
  // "/path/to/jquery.js": generated_jquery 原本有这一行,现在被删去。
});

可以看到jquery模块没有被打包进bundle文件,而对于util,它的生成代码即generated_util函数中关于import jquery相关的语句也被保留了原意:

function generated_util(module, exports, webpack_require) {
  var $ = require("jquery");
  // util的其它源代码
  // ...
}

当然也并非完全没有修改,例如将import的改回了传统的require关键词,因为我们这里用的是CommonJS风格的打包方式。不过这些都是次要的,关键是它保留了require这个关键词,而没有使用webpack_require将jquery真的引入进来。这就是说,当前的这个JS文件的模块管理系统中是没有jquery的,它是一个external的模块,需要在这个JS文件被其它人引用并且在上层编译时,jquery才可能被真的引入进来,到那个时候这里的require关键词才会被替换为webpack_require

对于external的依赖模块,通常你可以这样做,例如你使用npm发布你的库,你可以将jquery在package.json文件中添加到dependencies,这样别人npm install你发布的库时,jquery也会被自动下载到node_modules供别人打包使用。

umd格式下的打包

如果我们使用umd格式打包,我们可以看到在不同环境中,external模块是如何发挥作用的:

(function webpackUniversalModuleDefinition(root, factory) {
  if(typeof exports === "object" && typeof module === "object")  // commonjs2
    module.exports = factory(require("jquery"));
  else if(typeof define === "function" && define.amd)
    define("util", ["jquery"], factory);  // amd
  else if(typeof exports === "object")
    exports["util"] = factory(require("jquery"));  // commonjs
  else
    root["util"] = factory(root["jquery"]);  // var
}) (window, function(__webpack_external_module_jquery__) {
  return (function(modules) {
    var installedModules = {};
    function webpack_require(moduleId) {
       // ...
    }
    return webpack_require("./util.js");
  }) ({
    "./util.js": generated_util,
  });
}

generated_util也相应地增加一个参数__webpack_external_module_jquery__

function generated_util(module, exports, webpack_require,
                        __webpack_external_module_jquery__) {
  var $ = __webpack_external_module_jquery__;
  // util的其它源代码
  // ...
}

这样的写法似乎结构和上面的CommonJS的编译版本不太一样,但实际上本质是一样的。因为现在umd要照顾到不同的运行环境,所以它把require("jquery")提前了,作为factory的参数传入。对于每种运行环境,各有各的做法:

CommonJS:保留require("jquery")语句。

AMD:在define中将jquery定义为依赖模块。

Var:从全局域中取出jquery变量,这需要jquery在该模块之前就已经被加载。

然后不管是哪种情况,它们都将载入后的jquery模块作为参数传入factory函数,这样就能正确加载util模块了。

以上涉及到Webpack生成代码的部分可能有点绕,需要你比较了解Webpack打包模块的机制和原理,关于这部分我在这篇文章里做了详细讨论。

总结

以上就是关于Webpack的external选项的使用,并且从编译后的JS代码分析了它到底是如何起作用的。我想阅读Webpack相关的生成代码还是很重要的,这样才算是真正地理解了external的机制,在碰到一些坑时才能知道怎么去debug。

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

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

相关文章

  • webpack 构建性能优化策略小结

    摘要:但是,随者工程开发的复杂程度和代码规模不断地增加,暴露出来的各种性能问题也愈发明显,极大的影响着开发过程中的体验。对应的资源也可以直接由页面外链载入,有效地减小了资源包的体积。 背景 如今前端工程化的概念早已经深入人心,选择一款合适的编译和资源管理工具已经成为了所有前端工程中的标配,而在诸多的构建工具中,webpack以其丰富的功能和灵活的配置而深受业内吹捧,逐步取代了grunt和gu...

    hiYoHoo 评论0 收藏0
  • webpack组织模块原理 - 基础篇

    摘要:每一个模块的源代码都会被组织在一个立即执行的函数里。接下来看的生成代码可以看到,的源代码中关于引入的模块的部分做了修改,因为无论是,或是风格的,都无法被解释器直接执行,它需要依赖模块管理系统,把这些抽象的关键词具体化。 现在前端用Webpack打包JS和其它文件已经是主流了,加上Node的流行,使得前端的工程方式和后端越来越像。所有的东西都模块化,最后统一编译。Webpack因为版本的...

    leiyi 评论0 收藏0
  • webpack组织模块原理 - 打包Library

    摘要:所以你编译后的文件实际上应当只输出,这就需要在配置里用来控制这样上面的模块加载函数会在返回值后面加一个,这样就只返回的部分。 之前一篇文章分析了Webpack打包JS模块的基本原理,所介绍的案例是最常见的一种情况,即多个JS模块和一个入口模块,打包成一个bundle文件,可以直接被浏览器或者其它JavaScript引擎执行,相当于直接编译生成一个完整的可执行的文件。不过还有一种很常见的...

    legendmohe 评论0 收藏0
  • 我他喵到底要怎样才能在生产环境中用上 ES6 模块化?

    摘要:因此,你还是需要各种各样杂七杂八的工具来转换你的代码噢,我可去你妈的吧,这些东西都是干嘛的我就是想用个模块化,我到底该用啥子本文正旨在列出几种可用的在生产环境中放心使用模块化的方法,希望能帮到诸位后来者这方面的中文资源实在是忒少了。 原文发表在我的博客上。最近捣鼓了一下 ES6 的模块化,分享一些经验 :) Python3 已经发布了九年了,Python 社区却还在用 Python 2...

    KaltZK 评论0 收藏0
  • webpack实战

    摘要:和类似的预处理器还有等。的用处非常多,包括给自动加前缀使用下一代语法等,目前越来越多的人开始用它,它很可能会成为预处理器的最终赢家。 webpack实战 查看所有文档页面:全栈开发,获取更多信息。快马加鞭,加班加点,终于把这个文档整理出来了,顺便深入地学习一番,巩固知识,就是太累人,影响睡眠时间和质量。极客就是想要把事情做到极致,开始了就必须到达终点。 原文链接:webpack实战,原...

    cyrils 评论0 收藏0

发表评论

0条评论

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