前端工程化cjs umd esm 打包差异详解
作者:前舟
模块
先简单说一下模块这玩意:
一个模块(module)就是一个文件。一个脚本就是一个模块。就这么简单。
内部有自己的局部作用域以及程序,外部可以通过模块暴露的接口进行调用、执行模块内的程序
为什么要模块
我们从另一个角度出发,如果没有模块,会怎么样?
- 污染全局作用域
- script 标签自己插入自己手动排好顺序
- ....
总之就是,难以复用、难以维护!
所以很明显,我们需要模块化。
但是不同的环境是有不同的模块话机制的~
就目前来说,常用常见的模块化打包方式有这么三种:
- common js
- ES module
- UMD
commonjs
cjs 是 Node 中的模块规范,导入和导出的 API 为
- require
- exports
它的 require 是同步的,因为 node 模块系统是需要同步读取模块文件内容并编译执行以得到模块接口的。
而在浏览器端,一般都以 script 标签引入 —— script 标签可是天生异步的。
so 它本身只可以运行在 node 环境下,比如ms
他就是只支持 cjs,如果你想用 CDN 直接在浏览器中使用是不行的
但是,webpack 是支持 cjs 的,通过 webpack 就可以将其运行在浏览器中。所以,通过 webpack 打包你就可以在浏览器环境中使用 ms。
也就是说,在 webpack 环境下,cjs 即支持 node 环境,也支持浏览器环境~
除此之外,他是在运行时加载的,模块输出的也只是拷贝
UMD
全称 Universal Module Definition —— “通用模块定义”
这也算是应运而生,它可以在运行或者编译时让同一个代码模块使用 cjs 或者 amd
amd : Asynchronous ModuleDefinition
异步模块定义,采用异步方式加载模块。所有依赖模块的语句,都定义在一个回调函数中,等到模块加载完成之后,这个回调函数才会运行
也就是 集它们于一身,所以它既可以在 node 或者 webpack 环境下用 require 引用使用,也可以 在浏览器中 直接用 script 引用 CDN 使用
实现大概如下:
((root, factory) => { if (typeof define === 'function' && define.amd) { //AMD define(['jquery'], factory); } else if (typeof exports === 'object') { //CommonJS var $ = requie('jquery'); module.exports = factory($); } else { root.testModule = factory(root.jQuery); } })(this, ($) => { //todo });
也就是在定义模块的时候检测当前环境和模块定义的方式,将各种模块方法转换为同一种写法
有一些包就是用的这种打包方法
有些包是多种打包方法都有,比如
antd:
es/index.js
下就是 es6 的打包方式(下面会说)
dist/antd.js
下就是 umd 的打包方式
lib/index.js
下就是 cjs 的打包方式
es moudle
上面说的可能都是过去一段时间里最好的打包方式,但是 在未来 ES6 的打包方式 应该还是会全面替代它们
esm 是基于 ESMAScript 模块化规范的 —— 他的爹就代表着 他的前途一片光明
node 环境和浏览器环境下都支持~
另外他模块输出的是值的引用。
还是在编译时加载,这意味着可以在编译期间进行 Tree Shaking,减少 js 体积
另外还支持动态引入
const a = await import ('xxx')
总结
不同之处在于:
- 何时加载?运行时还是编译?
- 引出的值是拷贝还是引用?
- 可在哪些环境下使用?
- 同步还是异步?
以上就是前端工程化cjs umd esm 打包差异详解的详细内容,更多关于前端cjs umd esm 打包差异的资料请关注脚本之家其它相关文章!