之前总是对模块机制类的内容走马观花,知其一不知其二,只了解AMD\CMD\Common.js很表面的知识,今天看到一篇大佬的文章,总结的很好,做个笔记以后方便查阅。
1. UMD
2. CommonJs
3. es6 module
(function (global,factory){
typeof exports ==='object'&&typeof module !=='undefined'?module.exports=factory()
typeof define==='function'&& define.amd ? define(factory):(global.libName=factory())
}(this,(function(){'use strict'})));
如果你在js文件头部看到这样的代码,那么这个文件使用的就是UMD规范,实际上就是amd+commonjs+全局变量这三种风格的结合。
这段代码就是对当前运行环境的判断,如果是Node环境就是使用CommonJs规范,如果不是就判断是否为AMD环境,最后导出全局变量
有了UMD后我们的代码和同时运行在Node和浏览器上,所以现在前端大多数的库最后打包都使用的是UMD规范
关键字:module.exports exports
//foo.js
// 一个一个导出
module.exports.age=1
module.exports.foo=function(){}
exports.a='hello'
//整体导出
module.exports={age:1,a:'hello',foo:function(){}}
//整体导出不能用`exports` 用exports不能在导入的时候使用
exports={age:1,a:'hello',foo:function(){}}
这里需要注意exports不能被赋值,可以理解为在模块开始前exports=module.exports,因为赋值之后exports失去了对module。exports的引用,成为了一个模块内的局部变量
关键字:require
const foo=require('./foo.js')
console.log(foo.age)//1
//a.js
module.exports.a=1
var b=require('./b')
console.log(b)
module.exports.a=2
//b.js
module.exports.b=11
var a=require('./a')
console.log(a)
module.exports.b=22
//main.js
var a=require('./a')
console.log(a)
es6 module基本语法:
export * from 'module';
export {name1,name2,....,nameN } from 'module';//重定向命名导出
export {import1 as name1,import2 as name2,...,nameN } from 'module'; //重定向重命名导出
export {name1,name2,..,nameN};//与之前声明的变量名绑定 命名导出
export let name1='name1';//声明命名导出或者var,const,function,function*,class
export default expression;//默认导出
export default function(){...}//或者function*,class
export default function name1(){...}//或者function*,class
export { name1 as default,...};//重命名为默认导出
//命名导出module.js
let a=1,b=2
export {a,b}
export let c=3
//命名导入main.js
import {a,b,c} from 'module';//a:1 b:2 c:3
import {a as newA,b,c as newC } from 'module';//newA:1 b:2 newC:3
// 默认导出module.js
export default 1
// 默认导入 main.js
import defaultExport from 'module';//defaultExport:1
// 混合导出module.js
let a=1
export {a}
const b=2
export {b}
export let c=3
export default [1,2,3]
// module.js
Array.prototype.remove=function(){}
//副作用 只运行一个模块
import 'module';//执行module 不导出值 多次调用module.js只运行一次
//动态导入(异步导入)
var promise=import('module');
// if for while 等都无法使用
{
export let a=1
import defaultExport from 'module'
}
true || export let a=1
import的导入名不能为字符串或在判断语句,下面代码是错误的
import 'defaultExport' from 'module'
let name='Export'
import 'default' + name from 'module'
静态的语法意味着可以在编译时确定导入和导出,更加快速的查找依赖,可以使用lint工具对模块依赖进行检查,可以对导入导出加上类型信息进行静态的类型检查
//js中 基础类型是值传递
let a =1
let b=a
b=2
console.log(a,b) //1 2
//js中 引用类型是引用传递
let obj={name:'obj'}
let obj2=obj
obj2.name="obj2"
console.log(obj.name,obj2.name) //obj2 obj2
//es6 module 中基本类型也按引用传递
// foo.js
export let a=1
export function count(){
a++
}
// main.js
import {a,count} from './foo'
console.log(a)//1
count()
console.log(a)//2
// export default 是无法a的动态绑定 这一点跟CommonJs 有点相似 都是值的拷贝
let a=1;
export default a
// 可以用另一种方式实现default的动态绑定
let a=1;
export {a as default}
export function count(){
a++
}
// 就跟上面main.js一样
上面这段代码就是CommonJs导出变量和ES6导出变量的区别
//bar.js
import { foo } from './foo'
console.log(foo);
export let bar="bar"
//foo.js
import {bar} from './bar'
console.log(bar);
export let foo='foo'
// main.js
import {bar} from './bar'
console.log(bar)
1、执行main.js->导入 bar.js
2、bar.js->导入foo.js
3、foo.js->导入 bar.js-> bar.js已经执行过直接返回 ->输出 bar ->bar is not defined,bar未定义报错
我们可以使用function的方式解决:
//bar.js
import {foo} from './foo'
console.log(foo());
export function bar(){
return 'bar'
}
// foo.js
import { bar } from './bar'
console.log(bar());
export function foo(){
return 'foo'
}
//main.js
import {bar} from './bar'
console.log(bar)
因为函数声明会提示到文件顶部,所以就可以直接在foo.js调用还没执行完毕的bar.js的bar方法,不要在函数内使用外部变量,因为变量还未声明(let,const)和赋值,var
1、CommonJs导出的是变量的一份拷贝,ES6 Module导出的是变量的绑定(export default是特殊的)
2、CommonJs是单个值导出,ES6 Module可以导出多个
3、CommonJs是动态语法可以写在判断力,ES6 Module静态语法只能写在顶层
4、CommonJs的this是当前模块,ES6 Module的this是undefined