CommonJs和ES Module
Posted 登楼痕
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CommonJs和ES Module相关的知识,希望对你有一定的参考价值。
commonJs实现原理:
编译过程中将导出的代码js进行首尾包装进一个函数内:
function wrapper (script) {
return '(function (exports, require, module, __filename, __dirname) {' +
script +
'\\n})'
}
模块加载的时候通过runInThisContext执行modulefunction,传入require那些参数。
在使用require引入的时候,require("xxxx")可以写为核心模块例如fs、path那些,可以是相对or绝对路径,也可以是第三方模块。
1、核心模块:加载优先级仅次于缓存加载,因为核心模块已经被编译成二进制文件所以加载速度很快;
2、文件路径:会将路径转换为真实路径,作为索引,缓存起来;
3、自定义模块:会先逐级寻找node_modules直到根目录,并且会找package.json下main指向的文件,若没有则去node环境寻找index.js等。
require引入的文件,采用深度优先遍历按父-子-父顺序执行,在引入的时候会避免重复加载循环引入,因为首次加载的文件的module会被缓存到Module对象上,当执行到后面代码如果其他文件又引入了这个文件,则会直接读取Module上的缓存而不会再次执行。
exports和module.exports这两个持有相同的引用
exports使用的时候必须exports.xxx=xxx; exports.zzz=zzz,不能直接用对象进行覆盖,比如exports={xx:xx, yy:yy},因为exports是作为形参传入的,在js内部修改没有意义,外部的exports不会改变,所以只能用exports.xxx这样增加属性。
对于module.exports其实本质就是exports,二者指向同一地址,类似于let a = new Object(); let b=a; 所以如果exports直接被覆盖了,那么js内部exports地址的复制品的引用也会改变了,对外部的exports没有影响。但如果是exports.xxx改变参数,就是改变的地址指向存储在栈内的值。
所以不要在一个js文件中exports和module.exports同时使用,避免引起混乱。
提一嘴:
CommonJS同步加载并执行模块文件,对于服务器而言读取硬盘速度快,但是浏览器读取时的等待时间取决于网速等等因素,所以需要异步加载模块,因此有了AMD,也就是Asynchronous Module Definition。接受俩参数:require([module],callback())。像require.js、curl.js实现了AMD规范。
ES Module
es6之后js真正有了自己的模块化规范,export到处,import引入。
export导出有两种方式:
1、命名式导出:export {a,b},import {a,b} from "...", 其中a,b这些名字要对应,也可以用as重命名
2、默认导出:export default anything,import anyName from "..",这样anyName可以是自定义名称。
特性:
1、静态语法:ES6 Module的引入和导出是静态的,import会自动提升到代码顶层,export不能放到块级作用于或者条件语句中,比如说放在函数里写一句export或者if(export)这种。另外import的导入名称不能是字符串或者表达式。
2、ES6 Module是提前加载并执行模块文件,预处理阶段分析依赖,执行阶段进行执行,执行顺序是由子到父。
3、导出绑定:import执行在严格模式下,由import引入的变量是read-only,无法被赋值,并且是与原变量绑定引用的,也就是引用传递(无论是否为基础类型),可以用过同时导出一个方法来修改。
import()返回的是一个Promise,所以可以用.then进行动态引入。
以上是关于CommonJs和ES Module的主要内容,如果未能解决你的问题,请参考以下文章
CommonJs和ES6 module的异同和使用整理(定期整理)