最近在搞跨平台解决方案,讨论关于模块划分的问题以及如何尽可能多的复用逻辑代码。因而就有了此文章,以前的博客也写过,不过因为主机商跑路,宝贵的资源也就没了,说多了都是泪~ 这里按模块化发展的历史回溯的时间序html
这个是目前前端小伙伴接触的最多的,是浏览器和服务端通用的模块化解决方案,主要命令为:export
和import。export
用于导出本模块对外的接口,import
用于导入某个模块的功能。前端
//modulA.js export var goingta = "goingta"; export function hello() { console.log("hello world"); } const fn = function() { goingta = "new goingta"; console.log("fn"); }; export { fn }; const come = 1; export default come; // export default const errorDefault = 2 // default 不能导出变量表达式 //index.js import come, { fn, hello } from "./moduleA.js"; import * as aModule from "./moduleA.js"; fn(); hello(); console.log("come", come); console.log("aModule", aModule); //aModule Module{default: 1, fn: ƒ (),…} console.log("aModule.goingta", aModule.goingta); //new goingta
从上面例子,咱们能够看到export
能够在任意地方导出变量,方法,而且也能够将已有变量或方法包在一个{}
对象里面导出,可是最终都会包在一个{}
里面,简单理解就是:jquery
1. 若是单独导出一个变量或方法则是往将要导出{}对象里面添加属性。
2. 若是导出的是{},则和已生成的导出{}对象合并。es6
而后说一下特例export default
,这个是在导出对象里面加一个default
属性,还有一点值得注意的是export default
后面不能跟变量表达式。后端
最后还有一个重点是,ES6 Moudle是编译时输出,而且是值引用。浏览器
CommonJS
最主要的表明就是Node.js
,主要命令:module
、exports
、require
。其中有个使人疑惑的点是exports
和module.exports
,其实理解起来也很简单,就是在模块里面加了一句: exports = module.exports = {};
exports
和module.exports
指向同一个内存区域,只要在exports
加了属性,则module.exports
会跟着变化,可是最终导出对外的接口是以module.exports
为准,因此不推荐直接使用exports
。例如:性能优化
//moduleA.js let goingta = "我是goingta"; exports.goingta = goingta; exports.fn = function() { goingta = "new goingta"; }; exports = "把exports指向其余区域"; module.exports = "我如今没有goingta了,也没有fn了"; // 这行代码注释掉会有不同的结果 // index.js let obj = require("./moduleA.js"); console.log(obj);
最终输出的是:"我如今没有goingta了,也没有fn了",若是把最后一行代码注释掉则输出:{goingta: "我是goingta", fn:ƒ ()}异步
对于CommonJS
规范来讲,很重要的一点是CommonJS
输出的是一个值拷贝,而且是运行时加载。async
把上面的示例代码简化一下就能够看出:模块化
//moduleA.js let goingta = "我是goingta"; module.exports = { goingta }; module.exports.fn = function() { goingta = "new goingta"; }; //index.js let obj = require("./moduleA.js"); obj.fn(); console.log("obj.goingta", obj.goingta);
最终结果输出:"我是goingta"
从上面的结论咱们不可贵出,ES6 Module
和CommonJS
的本质区别:
1. 引用方式:CommonJS模块输出是值的拷贝,ES6 Module模块输出的值是引用
2. 时机:CommonJS是运行时加载,ES6 Module是编译是输出
差很少在6年前,当项目愈来愈大,用JS模拟划分出来的类致使文件愈来愈多以及须要前置依赖的各类库(那个时代的三叉戟jQuery
,Backbone
,Underscore
),模块化以及各类脚手架开始兴起,因而有了AMD
和CMD
以及当年这两种规范之争-AMD推崇依赖前置、提早执行,CMD
推崇依赖就近、延迟执行。AMD
的表明是require.js
,CMD
的表明是玉伯的Sea.js
AMD
规范是采用异步方式,依赖前置必须一开始就写好,全部的依赖加载完成后才会执行回调函数里的内容:
// moduleB.js //一开始就要写好这个模块全部的依赖 define(["jQuery", "underscore", "moduleA"], function($, _) { if (false) { // 即使没用到moduleA,也会被提早执行 moduleA.doSomething() } }); `CMD`规范按玉伯大大的原话`"是 Sea.JS 在推广过程当中对模块定义的规范化产出"`,而且当时在国内承认度很高。依赖就近书写: // moduleB.js define(function(require, exports, module) { var moduleA = require('moduleA.js');//用到时才写 moduleA..doSomething() //一堆其余业务代码 var $ = require('jquery.js'); $.xxxx() });
在07年左右正式有前端开发这个职位后(之前都是后端研发兼写前端代码),SEO、性能优化、语义模板、浏览器兼容、代码混淆加密等等都须要前端开发承接是,每每是一个公用的base.js
外加几个业务js放在html
里面,而后用defer
和async
标记:
<script src="base.js" async></script> <script src="moduleA.js" defer></script>
defer
和async
的区别在于:defer
是“渲染完再执行”,async
是“下载完就执行”。