摘要:所以你编译后的文件实际上应当只输出,这就需要在配置里用来控制这样上面的模块加载函数会在返回值后面加一个,这样就只返回的部分。
之前一篇文章分析了Webpack打包JS模块的基本原理,所介绍的案例是最常见的一种情况,即多个JS模块和一个入口模块,打包成一个bundle文件,可以直接被浏览器或者其它JavaScript引擎执行,相当于直接编译生成一个完整的可执行的文件。不过还有一种很常见的情况,就是我们要构建发布一个JavaScript的库,比如你在npm社区发布自己的库,这时Webpack就需要相应的配置,编译生成的代码也会略有不同。
和之前一篇文章一样,本文主要分析的是Webpack的生成代码,并结合它来说明编译库时Webpack那些关于library的配置选项的具体作用,相应的官方文档在这里。
编写JS的库我们还是从简单的案例开始,我们随便编写一个简单的库util.js:
import $ from "jquery" function sayHello() { console.log("Hello"); } function hideImages() { $("img").hide(); } export default { sayHello: sayHello, hideImages: hideImages }
提供了两个函数,当然它们之间毫无关系,也实际上没有任何卵用,纯粹是仅供教学参考。。。
接下来写Webpack的配置:
// 入口文件 entry: { util: "./util.js", } // 输出文件 output: { path: "./dist", filename: "[name].dist.js" }
但仅仅这样是不够的,这样输出的文件就是一个立即执行的函数,最后会返回util.js的exports,参照上一篇文章中分析,最后生成的bundle代码结构大致是这样的:
(function(modules) { var installedModules = {}; function webpack_require(moduleId) { // ... } return webpack_require("./util.js"); }) ({ "./util.js": generated_util, "/path/to/jquery.js": generated_jquery });
它如果执行完,那就结束了,将util.js的export部分返回而已,而我们需要的是将这个返回值交给编译后的文件的module.export,这样编译后的文件就成了一个可以被别人import的库。所以我们希望得到的编译文件应该是这样的:
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 });
要得到这样的结果,Webpack配置output部分需要加入library信息:
// 入口文件 output: { path: "./dist", filename: "[name].dist.js", library: "util", libraryTarget: commonjs2 }
这里最重要的就是libraryTarget,我们现在采用commonjs2的格式,就会得到上面的编译结果,也就是说Webpack会library把最后的输出以CommonJS的形式export出来,这样就实现了一个库的发布。
除了commonjs2,libraryTarget还有其它选项:
var (默认值,发布为全局变量) commonjs commonjs2 amd umd
使用不同的选项,编译出来的文件就能够在不同JavaScript执行环境中使用。在这里我们直接看万金油umd格式的输出是怎么样的:
(function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === "object" && typeof module === "object") // commonjs2 module.exports = factory(); else if(typeof define === "function" && define.amd) define("util", [], factory); // amd else if(typeof exports === "object") exports["util"] = factory(); // commonjs else root["util"] = factory(); // var }) (window, function() { return (function(modules) { var installedModules = {}; function webpack_require(moduleId) { // ... } return webpack_require("./util.js"); }) ({ "./util.js": generated_util, "/path/to/jquery.js": generated_jquery }); }
比之前的commonjs2的情况要复杂得多,因为它需要处理各种不同的case,但其实后面的部分都是一样的,最重要的就是最前面的几行,这是umd模块的标准写法。它运行传入的factory函数,实际上就是加载模块的函数,然后把返回的结果根据不同的运行环境交给相应的对象。例如var,那就会把结果设置为一个全局变量,这用于浏览器通过标签直接导入该JS文件;如果是CommonJS,它则交给exports对象;如果是AMD环境,它也是用标准的AMD的写法。这样这个发布出来的JS库就可以在任意的环境中都能被其他人使用。
如果用umd格式打包,可能会有一个坑需要注意,如果你的库的源代码是用ES6格式export default来输出的,正如上面的例子util.js,你直接把编译后的JS库文件放到浏览器中使用,可以是,或者RequireJS,可能得不到你想要的结果。这是因为你的JS文件返回给你的对象是这样的:
{ "default": { sayHello: sayHello, hideImages: hideImages } }
而不是你所期望的:
{ sayHello: sayHello, hideImages: hideImages }
不仅是浏览器,在不支持ES6的模块系统中同样会出这个问题,就是因为它们并不认识default。所以你编译后的JS文件实际上应当只输出default,这就需要在Webpack配置里用targetExport来控制:
library: "util", libraryTarget: umd, targetExport: "default"
这样上面的模块加载函数factory会在返回值后面加一个["default"],这样就只返回exports的default部分。
这个坑在umd格式下其实还是挺容易踩到的,例如你发布一个Vue组件,.vue文件中的JavaScript部分一般都是把Component对象以export default的格式导出的,就像这样:
export default { name: "xxx", data() { return // ... }, props: { // ... } methods: { // ... } }
如果你把编译后的JS文件直接放在浏览器里运行,并且用CDN的方式加载Vue,你会发现Vue无法识别这个Component,因为你得到的这个对象多了一层不必要的default。
你可能会问如果我把输出内容改成了default,会不会影响这个模块在ES6环境下的使用?一般来说是不会的。之前一篇文章里已经谈到,Webpack的生成代码在引入一个模块时,会通过一个叫__esModule的值来设置和判断它是不是ES6格式的export,现在如果只导出default部分,那么这个对象是被视为非ES6的,因为它不含__esModule。这样其它模块通过import来引入这个模块时,会引入整个对象,这实际上变相地就等价于只引入原模块的export default部分。
当然以上讨论的前提是,你所有需要export的内容全部都在export default里。如果你既有default,又有正常的export,那编译后的文件只导出default部分显然是不行的。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/93308.html
摘要:所以通常情况下当你的库需要依赖到例如,这样的通用模块时,我们可以不将它打包进,而是在的配置中声明这就是在告诉请不要将这个模块注入编译后的文件里,对于我源代码里出现的任何这个模块的语句,请将它保留。 这篇文章讨论Webpack打包library时经常需要用到的一个选项external,它用于避免将一些很通用的模块打包进你发布的library里,而是选择把它们声明成external的模块,...
摘要:每一个模块的源代码都会被组织在一个立即执行的函数里。接下来看的生成代码可以看到,的源代码中关于引入的模块的部分做了修改,因为无论是,或是风格的,都无法被解释器直接执行,它需要依赖模块管理系统,把这些抽象的关键词具体化。 现在前端用Webpack打包JS和其它文件已经是主流了,加上Node的流行,使得前端的工程方式和后端越来越像。所有的东西都模块化,最后统一编译。Webpack因为版本的...
摘要:一看就懂之基础配置一简介本质上,是一个现代应用程序的静态模块打包器。属性表示的是的上下文目录,配置入口文件的时候,如果入口文件使用的是相对路径,那么就是相对于所在的目录。通常用于指定以何种方式导出库,通常用于指定接收库的名称。 一看就懂之webpack基础配置 一、webpack 简介 本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module b...
摘要:配置以何种方式导出库。当检测文件不再发生变化,会先缓存起来,等等待一段时间后之后再通知监听者,这个等待时间通过配置。发布到线上给用户使用的运行环境。 一 缩小文件搜索范围 1 include & exclude 1) action 限制编译范围 2) useage module: { rules: [ { test...
摘要:基本配置项基本配置项。的插件架构主要基于实现的,这个就是专注于事件的广播和操作。开启多进程,加快打包速度。 这次我们主要研究的是webpack框架的相关知识,webpack是一个打包构建的前端框架,用于解决前端开发的模块化问题。 应用场景和纵向比较 说到webpack,肯定你还会想到gulp和grunt这些框架,那么webpack是做什么的呢?他和其他的框架有什么区别呢?我们一起来分析...
阅读 3857·2023-04-26 00:36
阅读 2667·2021-11-16 11:44
阅读 1082·2021-11-15 17:58
阅读 1665·2021-09-30 09:47
阅读 1208·2019-08-30 13:05
阅读 1538·2019-08-30 12:55
阅读 2408·2019-08-30 11:02
阅读 2715·2019-08-29 17:01