package.json 中的 Module 字段是干嘛的

发布时间:2019-08-20 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了package.json 中的 Module 字段是干嘛的脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

引入

最近团队的一个同学在搞 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 开发者更友好一些。

同时支持的效果类似这样:

<img width="693" alt="lib" src="https://user-images.githubuse...;>

<img width="663" alt="es" src="https://user-images.githubuse...;>

package.json 只需要对应相应的文件就可以了。

"name": "Drag-list",
"version": "1.0.0",
"main": "lib/drag-list/index.js",
"module": "es/drag-list/index.js"

Tree-shaking

tree-shaking 是近两年才在 JS 中出现的,之前没有的,而模块化的概念是一直都有方案的,只不过直到 ES Module 才有统一的标准趋势。
前面提到 rollup 采用 ES Module,带来的一个优点便是 tree shaking,那什么是 tree-shaking 呢。

有一个图片很形象的解释了它的功能。

package.json 中的 Module 字段是干嘛的

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.jsexport1 是这样的:

/* 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(),
  ]
}

package.json 中的 Module 字段是干嘛的

上图即编译后 export1 模块的截图,可以看到 func2 已经被去掉了。不过在我开启 babel-loader 以后,babel 配置就是一个简单的 "presets: ["env"]",却发现 func2 又回来了,如下:

package.json 中的 Module 字段是干嘛的

这是为什么呢。因为 tree-shaking 是依赖 ES Module 的静态加载,而 babel-presets-env 中是包含 ES2015 modules to CommonJS transformplugin,也就是转化成 CommonJS,所以无法识别哪些代码是未引用的,也就是无法 tree-shaking,所以 babel transform 的时候应该保留 ES Module。

package.json 中的 Module 字段是干嘛的

通过 PResets 的 option 选择,设置 modules 为 false 即可。

另外,tree-shaking 并不是完美无缺的,有些情况还无法识别。比如你导入了一个模块,但是这个变量代码中未使用,是不会去掉的,细节可以看这篇文章

为什么是 ES Module

ES 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...

脚本宝典总结

以上是脚本宝典为你收集整理的package.json 中的 Module 字段是干嘛的全部内容,希望文章能够帮你解决package.json 中的 Module 字段是干嘛的所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。