1、打包后不仅有 min.js
Vue 使用 lib 模式 开发自己的组件库时,打包后的 dist 目录不仅有 lib.min.js,还会出现 lib.common.js 和 lib.umd.js。第一次看到这两个文件的时候不知道这是啥,为了知其所以然,于是乎查了一下,在这里稍作整理。
无论是做项目还是开发自己的组件库,发布之前的最后一步就是打包部署了,之前部署项目时打包文件都是会把 html/css/js 等文件做一些压缩处理,再加一些打包工具的代码,比如 webpack 的运行时环境等。那这个 common/umd 是什么?
ps:这里有 Vue CLI 官网指定的应用模式和库模式的打包方式,感兴趣的同学可以 移步这里。
2、CommonJS/Amd/Umd 简介
以史为镜,可以知兴替。在聊这两个文件内之前需要先了解一些 JS 模块化的东西。
其实 JavaScript 在发展之初是一直没有模块(module)体系的,无法将一个大的程序拆分成相互依赖的小文件,在用简单的方法将它们拼装起来。其他语言都有这项功能,比如 Ruby 的 require、Python 的 import,甚至连 CSS 都有 @import,但是 JavaScript 却没有任何这方面的支持,这对于开发大型、复杂的项目而言无疑是一个巨大的障碍。
下面我们来看看 JavaScript 在应对起初的没有模块化这一问题,在发展历程中都经历的哪些阶段?
<script> 标签:在 JS 发展的襁褓里没有模块化,即使复杂的功能代码也是放在一起的一大坨,为了解决这个问题,当时普遍都是使用一个立即执行函数,通过立即执行函数将 js 代码包裹到一个单独的文件中,然后通过 <script> 标签对其进行引入,这样就能使得各自文件的 js 代码在一个局部的作用域中执行,避免了相互影响。
Common.js :但在后来伴随 node.js 的出现,就出现了一个 js 文件之间的相互依赖问题,要知道 node.js 中是没有 <script> 标签的,于是乎当时就有了 Common.js 模块依赖的语法,引入时使用 var fs = require('fs'),导出用 module.export a = 1;。
AMD(异步模块定义) :Common.js 的问题在于它是同步执行的,很显然浏览器不会希望一开始就下载,于是乎就出现了一种 AMD 的语法:require.js,它是专门为浏览器发明的。它的具体用法是通过 回调 :require('vue',(Vue) => {new Vue()}) 拿到 异步 得到的模块。
import/export:上述两种引入方式相互并存了一段时间后,制定 JavaScript 规范的委员会出面,发布了标准的 js 模块化的import/export 语法,通过 import 导入 js 模块,通过 export 导出模块,这也是现如今使用比较流行的语法。
UMD(统一模块定义):这种模块语法会自动监测开发人员使用的是 Common.js/AMD/import/export 种的哪种方式,然后再针对各自的语法进行导出,这种方式可以兼容所有其他的模块定义方法。
了解了以上简介,就知道了为什么 Vue 的 lib 模式会打包出 umd 文件了,为的就是能兼容多种模块定义方式。
3、CommonJS 和 AMD 与 ES6 的区别
在 ES6 之前,社区指定的上述这些模块加载方案,其中最主要的还是 CommonJS 和 AMD 两种,前者用于服务器,后者用于浏览器。ES6 在语言规格的层面上实现了模块功能,而且实现得相当简单,完全可以取代现有的 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。
ES6 模块设计思想是尽量 静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块都是只能在运行时确定这些东西。
比如 CommonJS 模块就是对象,输入时必须查找对象属性。
//CommonJS 模块 let {stat, exists, readFile} = require('fs'); //相当于 let _fs = require('fs'); let stat = _fs.stat; let exists = _fs.exists; let readFile = _fs.readFile;
上述代码的实质是整体加载 fs 模块(即加载 fs 的所有方法),生成一个对象(_fs),然后再从这个对象上读取 3 个方法。这种加载称为 "运行时加载",因为只有运行时才能得到这个对象,导致完全没有办法在编译时进行 "静态优化"。
而 ES6 模块不是对象,它是通过 export 命令显式指定输出的代码,再同构 import 命令输入。
//ES6 模块 import {stat, exists, readFile} from 'fs';
上述代码的实质是从 fs 模块加载 3 个方法,而不加载其他方法。这种加载称为 "编译时加载" 或者静态加载,即 ES6 可以在编译时就完成模块加载,效率比 CommonJS 的加载方式高。当然,这也导致 ES6 模块本身无法被引用,因为它不是对象。