摘要:为何有查阅了的文档,并没有找到字段的定义,直到才知道它是中最早就提出的概念。况且目前大部分仍是采用,所以便使用了另一个字段。所以目前主流的打包工具都是支持的,鉴于其优点,字段很有可能加入的规范之中。
引入
最近团队的一个同学在搞 npm library 源码的调试插件,因为内部的一个组件库含有大量的逻辑,在某个项目中不经意就出现一个磨人的 bug,但是组件库发布都是打包编译后的代码,而且没有 publish src 代码,不方便调试,每次还要 down 一下包的源码,再改下 webpack 的配置(比如 rule 中 exclude 去掉组件库, 改下 resolve ,在 dll 中去掉组件库)。被他们耳语目染了好几天,我就想,记得 npm 包是可以直接引源码的,大概改下 webpack 配置就可以了。然后便找到了 package.json 中 module 字段,并查漏 js 中 tree shaking 的知识,所以我并没有去研究怎么搞那样的一个插件?,而是由 package 中的 module 字段延伸出的一些知识。
为何有 module查阅了 package.json 的文档,并没有找到 module 字段的定义,直到 google 才知道它是 rollup 中最早就提出的概念 --- pkg.module。大概就是最早的 npm 包都是基于 CommonJS 规范的,package.json 形如:
"name": "package1", "version": "1.0.0", "main": "lib/index.js"
当 require("package1") 的时候,就会根据 main 字段去查找入口文件。
而 es2015 后,js 拥有了 ES Module,相较于之前的模块化方案更爽滑,更优雅,并且 ES 模块也是官方标准(JS 规范),而 CommonJS 模块是一种特殊的传统格式,在 ES 模块被提出之前做为暂时的解决方案。所以 rollup 去利用 ES Module 构建,就可以利用 ES Module 的很多特性,从而提高打包的性能,其中提升一个便是 tree shaking,这个我们后面去介绍。在这个构建思想的基础上,开发、产出的 npm 包同样使用 es6 的 module,即可同样受益于 tree shaking 等特性。
而 CommonJS 规范的包都是以 main 字段表示入口文件了,如果使用 ES Module 的也用 main 字段,就会对使用者造成困扰,假如他的项目不支持打包构建,比如大多数 node 项目(尽管 node9+ 支持 ES Module)。这就是库开发者的模块系统跟项目构建的模块系统的冲突,更像是一种规范上的问题。况且目前大部分仍是采用 CommonJS,所以 rollup 便使用了另一个字段:module。
像这样:
"name": "package1", "version": "1.0.0", "main": "lib/index.js", "module": "es/index.js"
webpack 从版本 2 开始也可以识别 pkg.module 字段。打包工具遇到 package1 的时候,如果存在 module 字段,会优先使用,如果没找到对应的文件,则会使用 main 字段,并按照 CommonJS 规范打包。所以目前主流的打包工具(webpack, rollup)都是支持 pkg.module 的,鉴于其优点,module 字段很有可能加入 package.json 的规范之中。另外,越来越多的 npm 包已经同时支持两种模块,使用者可以根据情况自行选择,并且实现也比较简单,只是模块导出的方式。
注意:虽然打包工具支持了 ES Module,但是并不意味着其他的 es6 代码可以正常使用,因为使用者可能并不会对你的 npm 包做编译处理,比如 webpack rules 中 exclude: /node_modules/,所以如果不是事先约定好后编译或者没有兼容性的需求,你仍需要用 babel 处理,从而产出兼容性更好的 npm 包。还好 rollup 在这方面做的不错,对于 library 开发者更友好一些。
同时支持的效果类似这样:
Tree-shaking
tree-shaking 是近两年才在 JS 中出现的,之前没有的,而模块化的概念是一直都有方案的,只不过直到 ES Module 才有统一的标准趋势。
前面提到 rollup 采用 ES Module,带来的一个优点便是 tree shaking,那什么是 tree-shaking 呢。
有一个图片很形象的解释了它的功能。
tree-shaking 的功能就是把我们 JS 中无用的代码,给去掉,如果把打包工具通过入口文件,产生的依赖树比作 tree,tree-shaking 就是把依赖树中用不到的代码 shaking 掉。
我们通过代码了解下,webpack3.x 下打包验证 tree-shaking。
// 入口文件 index.js import { func1 } from "./export1"; func1();
// export1 文件 export function func1() { console.log("func1"); } export function func2() { console.log("func2"); }
func2 方法虽然导出了,但是在 index.js 中是没有用到的,func2 就是无用代码,最终打包生成的 build 是应该去掉的。
使用最简单的 webpack 配置,不使用 babel,产出 build.js,export1 是这样的:
/* 2 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony export (immutable) */ __webpack_exports__["a"] = func1; /* unused harmony export func2 */ function func1() { console.log("func1"); } function func2() { console.log("func2"); } /***/ })
我们发现有两行注释,/* harmony export (immutable) 表明代码是有用的,unused harmony export func2表明 func2 是无用代码,说明 webpack 已经识别。不过 webpack 仅仅是做了“标记”,去掉这些代码的能力,是通过插件实现的,常用的便是 unglify。在 plugins 用启用 UglifyJsPlugin 后,查看下 build。
// webpack.config.js const UglifyJsPlugin = require("uglifyjs-webpack-plugin"); module.exports = { ... plugins: [ new UglifyJsPlugin(), ] }
上图即编译后 export1 模块的截图,可以看到 func2 已经被去掉了。不过在我开启 babel-loader 以后,babel 配置就是一个简单的 "presets: ["env"]",却发现 func2 又回来了,如下:
这是为什么呢。因为 tree-shaking 是依赖 ES Module 的静态加载,而 babel-presets-env 中是包含 ES2015 modules to CommonJS transform 的 plugin,也就是转化成 CommonJS,所以无法识别哪些代码是未引用的,也就是无法 tree-shaking,所以 babel transform 的时候应该保留 ES Module。
通过 presets 的 option 选择,设置 modules 为 false 即可。
另外,tree-shaking 并不是完美无缺的,有些情况还无法识别。比如你导入了一个模块,但是这个变量代码中未使用,是不会去掉的,细节可以看这篇文章
为什么是 ES ModuleES Module 之前的 JS 实现模块化,都是基于第三方依赖管理实现的,比如 requirejs,seajs,这都是在代码执行的时候,才知道依赖了哪些模块,常用的 node 中的 commonjs,也是如此
(function (exports, require, module, __filename, __dirname) { // YOUR CODE INJECTED HERE! });
所以,当 ES Module 在代码不执行的时候,就可以知道模块的依赖关系,就不难理解是为什么了。
思考我的本意是,可否利用 module 字段的特性,让我的 npm 包支持引入源码,从而可以实现源码调试、并且后编译的效果,不过从目前的规范看来,内部还是可以试一下的,开源的包最好不要这样做,除非你有自己的一套规范以及后编译生态。虽然没有达到目的,不过也后知后觉的了解到 module 的用意,以及 rollup 在开发包时候的妙用,以及 tree-shaking 并不是自己了解的那么美好。
相关推荐你的Tree-Shaking并没什么卵用
【译】如何在 Webpack 2 中使用 tree-shaking
手把手带你走进下一代的ES6模块打包工具—Rollup
原文:https://github.com/configu/bl...
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/107674.html
摘要:字段由脚本命令组成的字典,这些命令运行在包的各个生命周期中。在打包过程中,如果遇到字段会优先使用字段表示的路径下的文件,如果不存在,则用字段表示的作为入口,并按照的规范打包。其中还分析了文件中字段和字段的不同以及和两个字段的区别。 所有用npm下载的包或者要上传至npm的模块都会有一个package.json文件,这个文件总是存在于模块(或者包)的根目录下,这个文件到底是干嘛的,现在就...
摘要:但是,面对辣么多的配置文件,还是从开始自己来吧,毕竟我只想打包一个组件。这里想一下我们的需求,我们想要打包一个组件,使用,根据上面的说明,不难想到还应该需要一个可以用来识别并转换文件,一句话,就是把按下面格式的编写的组件转换为模块。 对于vue单文件组件的使用,我们知道使用vue-cli可以快速生成项目结构,进行.vue单文件组件的编写,使用 npm run build命令会从main...
摘要:但是,面对辣么多的配置文件,还是从开始自己来吧,毕竟我只想打包一个组件。这里想一下我们的需求,我们想要打包一个组件,使用,根据上面的说明,不难想到还应该需要一个可以用来识别并转换文件,一句话,就是把按下面格式的编写的组件转换为模块。 对于vue单文件组件的使用,我们知道使用vue-cli可以快速生成项目结构,进行.vue单文件组件的编写,使用 npm run build命令会从main...
摘要:但是,面对辣么多的配置文件,还是从开始自己来吧,毕竟我只想打包一个组件。这里想一下我们的需求,我们想要打包一个组件,使用,根据上面的说明,不难想到还应该需要一个可以用来识别并转换文件,一句话,就是把按下面格式的编写的组件转换为模块。 对于vue单文件组件的使用,我们知道使用vue-cli可以快速生成项目结构,进行.vue单文件组件的编写,使用 npm run build命令会从main...
摘要:同时虚拟主机管理系统还包含了财务管理功能功能。随着互联网的发展和技术的不断更新,虚拟主机管理系统的开发也越来越完善,功能也越来越强大,尤其值得一说的是虚拟主机管理系统更加智能化。点开网页出现虚拟主机已开通是什么意思?虚拟主机是把一台服务器分成很多虚拟的服务器,每一个虚拟主机都具有独立的域名和完整的Internet服务器(支持WWW、FTP、E-mail等)功能。缺点只有一个id,一旦有别的网...
阅读 1219·2021-11-22 13:54
阅读 1406·2021-11-22 09:34
阅读 2646·2021-11-22 09:34
阅读 3907·2021-10-13 09:39
阅读 3321·2019-08-26 11:52
阅读 3319·2019-08-26 11:50
阅读 1495·2019-08-26 10:56
阅读 1880·2019-08-26 10:44