package.json中的browser/module/main字段与mjs杂谈
Author:zhoulujun Date:
npm 包其实又分为:
只允许在客户端使用的,
只允许造服务端使用的,
浏览器/服务端都可以使用。
如果我们需要开发一个 npm 包同时兼容支持 web端 和 server 端,需要在不同环境下加载npm包不同的入口文件,显然一个 main 字段已经不能够满足我们的需求,这就衍生出来了 module 与 browser 字段。
browser、module 、 main字段定义
main : 定义了 npm 包的入口文件,browser 环境和 node 环境均可使用
module : 定义 npm 包的 ESM 规范的入口文件,browser 环境和 node 环境均可使用
browser : 定义 npm 包在 browser 环境下的入口文件
我们将 Node 原始的模块方式 CommonJS简称为CJS,而 EcmaScript Module称为ESM。现在主流的webpack2及以上采用esm。
CJS 采用的是动态同步加载,也就是说运行的时候确定加载的文件,很明显这样做有一个好处就是灵活,但是缺点就是无法很好的处理循环引用的问题。而且是同步加载,这会导致加载速度过慢。
ESM 采用的是静态异步加载,最大的区别便是采用了静态分析。大家都知道 import 必须要写在文件的顶层,这也就是为了能够静态分析你需要加载的模块。首先他能很好的解决循环依赖的问题。
其次是异步加载。在 CJS 中,JS 的加载是同步进行的,也就是说我必须要等待上一个 JS 加载完成,才能够加载下一个 JS,大家也懂得,这样很明显浪费了 Node 异步的有点。这也就会导致如果 JS 文件过多,系统的启动时间会被大大加长。
我们npm的package.json文件里面
{ "main": "lib/index.js", // main "module": "lib/index.mjs", // module // browser 可定义成和 main/module 字段一一对应的映射对象,也可以直接定义为字符串 "browser": { "./lib/index.js": "./lib/index.browser.js", // browser+cjs "./lib/index.mjs": "./lib/index.browser.mjs" // browser+mjs }, // "browser": "./lib/index.browser.js" // browser }
当我们在不同环境下 import 一个 npm 包时,到底加载的是 npm 包的哪个文件?
文件优先级
JS模块规范有 ESM 和 commonJS 两种,为了能在 node 环境下原生执行 ESM 规范的脚本文件,.mjs 文件就应运而生。
当存在 index.mjs 和 index.js 这种同名不同后缀的文件时,import './index' 或者 require('./index') 是会优先加载 index.mjs 文件的。
也就是说,优先级 mjs > js
现在的包,一般有很多个版本,比如
vue.js : vue.js则是直接用在<script>标签中的,完整版本,直接就可以通过script引用。
vue.common.js :预编译调试时,CommonJS规范的格式,可以使用require("")引用的NODEJS格式。
vue.esm.js:预编译调试时, EcmaScript Module(ES MODULE),支持import from 最新标准的。
预编译+运行时,也就是模板字符串和现在最常用的单文件组件.vue文件,需要经过它预编译转化成纯javascrit,然后再运行,适用于开发环境。
运行是版本
vue.runtime.js :生产的运行时,需要预编译,比完整版小30%左右,前端性能最优。适用于生产环境,需要经过预编译。
官方说法是用来创建 Vue 实例,渲染并处理 virtual DOM 等行为的代码。基本上就是除去编译器的其他一切。
vue.runtime.esm.js:生产运行时,esm标准。
vue.runtime.common.js:生产运行时,commonJS标准。
webpack + web + ESM
通过 webpack 打包构建我们的 web 应用,模块语法使用 ESM
import test from 'test'
实际上的加载优先级是 browser = browser+mjs > module > browser+cjs > main
也就是说 webpack 会根据这个顺序去寻找字段指定的文件,直到找到为止。
然而实际上的情况可能比这个更加复杂,具体可以参考流程图
webpack + web + commonJS
const test = require('test')
事实上,构建 web 应用时,使用 ESM 或者 commonJS 模块规范对于加载优先级并没有任何影响
优先级依然是 browser = browser+mjs > module > browser+cjs > main
webpack + node + ESM/commonJS
我们清楚,使用 webpack 构建项目的时候,有一个 target 选项,默认为 web,即进行 web 应用构建。
当我们需要进行一些 同构项目,或者其他 node 项目的构建的时候,我们需要将 webpack.config.js 的 target 选项设置为 node 进行构建。
import test from 'test' // 或者 const test = require('test')
优先级是: module > main
node + commonJS
通过 node test.js 直接执行脚本
const test = require('test')
只有 main 字段有效。
node + ESM
通过 --experimental-modules 可以让 node 执行 ESM 规范的脚本(必须是 mjs 文件后缀)
`node --experimental-modules test.mjs
import test from 'test'
只有 main 字段有效。
总结
如果 npm 包导出的是 ESM 规范的包,使用 module
如果 npm 包只在 web 端使用,并且严禁在 server 端使用,使用 browser。
如果 npm 包只在 server 端使用,使用 main
如果 npm 包在 web 端和 server 端都允许使用,使用 browser 和 main
其他更加复杂的情况,如npm 包需要提供 commonJS 与 ESM 等多个规范的多个代码文件,请参考上述使用场景或流程图
参考文章:
ES6 模块化的时代真的来临了么?Using MJS https://www.jianshu.com/p/fa54a2e6e168
commonjs 与 esm 的区别 https://juejin.im/post/5cf8e159f265da1b94213a63
browser VS module VS main https://github.com/SunshowerC/blog/issues/8
转载本站文章《package.json中的browser/module/main字段与mjs杂谈》,
请注明出处:https://www.zhoulujun.cn/html/webfront/ECMAScript/nodejs/8413.html
延伸阅读:
- gulp插件browser-sync热更新导致浏览器卡死
- package.json中browser/module/main加载顺序
- module.js:440 throw err;
- npm link的使用:本地npm包修改不发布同步更新
- nodejs npm安装本地node-sass失败,ruby gem 安装sass失败总结
- nodejs v4.4.2 LTS和 v5.10.1 Stable 有什么区别?
- 如何在mac上彻底卸载nodejs:Mac下彻底卸载node和npm
- npm WARN deprecated [email protected]: Please update to minimatch 3.0.2 or higher
- yeoman创建项目,nodejs报错Error: EACCES: permission denied, open
- npm 常用命令详解
- nodejs查看npm安装所有包,并卸载多余的包
- 系统环境变量:Node.js环境变量在前端工程配置上的作用
- nvm使用详解——命令解析大全
- mac与nodejs升级后:npm8安装git+https://github.com/包报错
- 彻底锁定项目环境:package-lock包依赖于nodejs环境/npm版本锁
- nodejs升级到最新LTS版本方法汇总:linux/mac/window—npm/yum/ssh
- pm2托管npm命令:linux持久运行nodejs npm run服务(开机启动)
- koa服务器后台:Node.js 通过进程、线程优化的性能
- nodejs多进程处理方案:pm2中的cluster与fork
- window powerShell node.js npm很多命令无法使用,如yarn vue
- sass安装:webpack sass编译失败,node-sass安装失败的终极解决方