日常开发中,我们都会用到一些第三方库,或者自己封装一些库,用来减少自己的开发量。在封装库的时候,我们会借助一些打包工具,来转换代码的语法(比如解析jsx语法,把 JavaScript 中 es2015/2016/2017 等新语法转化为 es5),使其能在目标浏览器上正常执行。而打包工具有很多,比如webpack,babel,rollup,每个打包工具都有自己的配置项和插件,且体系庞大,构成复杂。所以本文不介绍打包工具如何使用,而是让大家对不同的模块规范有一个简单的了解,以及普及一些打包知识。
注:本文主要以babel为例。
这是立即执行函数,它的代码结构如下所示:
(function(){
...
})()
这种格式的文件主要通过<script>标签引用来使用的,所以可以直接在浏览器中执行使用,大部分的前端sdk会采用这种方式。
是一种服务端的模块规范,常见于node.js,其引入和导出的方法如下所示:
// 引入
const myModule = require('./myModule.js');
// 导出
module.exports = function doSomething(n) { ... };
module.exports.a = 1;
//或者
exports = function otherThing(n) { ... };
exports.b = 2;
引入CJS包的方法大家应该都懂,就是通过require把对应路径的模块作为一个对象赋值给某个变量。但是在导出时,需要注意的是我们用的是exports,而不是export,因为export是ESM的导出方式,这里需要区分一下。另外,导出时module.exports和exports的作用是一样的,module.exports只是对exports的引用。CJS模块有以下几个特点:
AMD是主要用于浏览器的模块规范,其引入和导出的方法如下所示:
// 引入
require(["./amd.js"], function (m) {
console.log(m);
});
// 导出
define("a");
define(['dep1', 'dep2'], function (dep1, dep2) {
// Define the module value by returning a value.
return function () {};
});
AMD的引入方式其实和CJS差不多,都是通过require来引入,不过它提供了两个参数,第一个是需要引入的模块的名称数组,第二个则是模块引入完成后执行的回调函数。所以当大家看到require时,不要简单的以为这个模块就是CJS规范的,主要还是要看它是同步加载还是异步加载的。
另外,在导出AMD模块时,主要是用define方法,该方法也提供两个参数,第一个是依赖的模块数组,第二个则是一个匿名函数,该函数返回一个新的函数,返回的函数就是导出的模块,AMD会先加载依赖,然后再执行模块代码。
CMD和AMD类似,只不过AMD需要先加载依赖,再执行代码,而CMD将依赖后置,在模块内部,在需要时才通过require引入依赖,其代码格式如下所示:
define(function(require, export, module){
require('./a.js') // 引入 模块
module.exports = { // 导出模块
a: 1,
}
})
UMD是对多种模块格式(包括上面四种)都进行兼容的模块规范,所以它既能在服务端执行,又能在浏览器端执行,它打包后的模块格式如下:
(function (root, factory) {
if (typeof define === "function" && define.amd) {
// 支持 AMD 规范
define(["jquery", "underscore"], factory);
} else if (typeof define === 'function' && define.cmd){
// 支持 CMD 规范
define(function(require, exports, module) {
module.exports = factory()
})
} else if (typeof exports === "object") {
// 支持 CommonJS
module.exports = factory(require("jquery"), require("underscore"));
} else {
// 支持全局引用
root.Requester = factory(root.$, root._);
}
}(this, function ($, _) {
var Requester = { ... };
return Requester;
}))
根据上面代码的条件判断可知,上面的代码只兼容了四种模块规范,很显然,ESM不在其中,所以如果你用UMD打包完你的代码后,发现打包后的文件里没有export导出的模块,那么你可能没办法通过import来引入你的模块了。
ESM是我们最熟知的模块规范,日常的组件开发也是用的这种规范,简单的import和export即可,其格式如下所示:
// 引入
import {foo, bar} from './myLib';
// 导出
export default function() {
...
}
ESM模块规范是ES6出现后才有的模块,它有以下几个特点;
从上文我们可以看到不同模块规范下的模块格式,在实际开发过程中,我们也需要根据开发环境来选择合适的模块规范,不要试图用import去引入一个没有export的模块。
如果要使用babel插件来打包代码,需要在当前项目目录下添加.babelrc文件,下面是该文件的一个例子:
.babelrc文件内存放的是一个对象,其中的presets和plugins都是插件数组,只不过presets里的插件是一整套的,而plugins里的插件都是单个的。
此外,如果我们要对插件进行配置,就需要用数组进行包裹,以["@babel/preset-env",{"modules":false}]为例,数组中第一个元素是插件名称,第二个则是插件的配置对象,不同插件的配置对象内容是不同的。
env这个插件是babel中很重要的一个插件,它的核心目的是通过配置得知目标环境的特点,然后只做必要的转换。例如目标浏览器支持 es2015,那么 es2015 这个 preset 其实是不需要的,于是代码就可以小一点(一般转化后的代码总是更长),构建时间也可以缩短一些。
另外,在env中,我们可以设置modules的值为 amd, umd, systemjs, commonjs,以及false,分别对应不同的模块规范,false则是不进行模块化处理,仅仅进行打包。
文章
3.2K人气
8粉丝
1关注
©Copyrights 2016-2022 杭州易知微科技有限公司 浙ICP备2021017017号-3 浙公网安备33011002011932号
互联网信息服务业务 合字B2-20220090