发布网友 发布时间:2024-09-30 19:52
共1个回答
热心网友 时间:2024-12-05 05:13
在介绍ESMole规范之前,我们先了解下AMD和CMD两种规范。
AMD规范
AMD规范采用非同步加载模块,允许指定回调函数
node模块通常位于本地,加载速度快,所以适用于同步加载
浏览器环境下,模块需要远程请求获取,所以适用于异步
require.js是AMD的一个具体实现库
CMD规范
CMD整合了Commonjs和AMD的优点,模块加载是异步的
CMD专门用于浏览器端,sea.js是CMD规范的实现
AMD和CMD最大的问题是没有通过语法升级来解决模块化的问题。它们去定义模块化还是调用js方法的方式去生成一个模块,如果当项目模块达到成百上千个,这种方式无法进行模块规模化的应用。要想模块规模化应用则需要一种标准的语法规范,这是AMD和CMD都没有实现的。
ESMole规范
ESMole设计理念是希望在编译时就确定模块依赖关系即输入输出
Commonjs和AMD必须在运行时才能确定依赖和输入、输出
ESMole通过import加载模块,通过export输出模块
下面我们来详细介绍ESMole。
ESMole使用export正常导出,import导入所有通过export导出的属性,在import中可以通过结构的方式,解构出来。
export导出
constname='dog'constauthor='xiaoming'export{name,author}exportconstsay=function(){console.log('hello,world')}import导入
//name,author,say对应a.js中的name,author,sayimport{name,author,say}from'./a.js'默认导出exportdefaultconstname='dog'constauthor='xiaoming'constsay=function(){console.log('hello,world')}exportdefault{name,author,say}导入
importmesfrom'./a.js'console.log(mes)//{name:'dog',...}exportdefaultanything?默认导出。?anything?可以是函数,属性方法,或者对象。
对于引入默认导出的模块,importanyNamefrom'mole',anyName可以是自定义名称。
混合导入|导出exportconstname='dog'exportconstauthor='xiaoming'exportdefaultfunctionsay(){console.log('hello,world')}导入有两种方式,第一种是:
importtheSay,{name,authorasbookAuthor}from'./a.js'console.log(theSay,//?say(){console.log('hello,world')}name,//'dog'bookAuthor//'xiaoming')第二种:
importtheSay,*asmesfrom'./a'console.log(theSay,//?say(){console.log('hello,world')}mesmes对象如下,可以看到把导出的所有属性都收敛到了一个对象里面,其中exportdefault导出值的key为default。
{name:'dog',author:'xiaoming',default:?say(){console.log('hello,world')}}ESMole特点静态语法ESMole的设计理念是希望在编译时就确定模块依赖关系即输入输出,那如何在编译时就能确定依赖关系呢?
在传统编译语言的流程中,程序中的一段源代码在执行之前都需要经过"编译"。对于JavaScript这样的解释型语言来说,也是需要编译的,只不过编译过程发生在代码执行前的几微秒(甚至更短)的时间内。
要想在编译阶段就能确定依赖关系,那必须要把import进行类似于变量提升。我们来看一下JavaScript中的变量提升。
functiontest(){console.log(a)console.log(foo())vara=1functionfoo(){return2}}test()在编译阶段发生了变量提升,经过预编译,执行顺序就变成了这样:
functiontest(){functionfoo(){return2}varaconsole.log(a)console.log(foo())a=1}test()所以打印结果是:
//name,author,say对应a.js中的name,author,sayimport{name,author,say}from'./a.js'0import提升其实JavaScript代码在编译阶段发现有import也会像var一样进行提升。为了验证这一点,看一下如下demo。
main.js
//name,author,say对应a.js中的name,author,sayimport{name,author,say}from'./a.js'1a.js
//name,author,say对应a.js中的name,author,sayimport{name,author,say}from'./a.js'2b.js
//name,author,say对应a.js中的name,author,sayimport{name,author,say}from'./a.js'3执行顺序如下:
//name,author,say对应a.js中的name,author,sayimport{name,author,say}from'./a.js'4当执行main.js,可以看到先答应"b模块加载",但是main.js第一行代码是console.log('main.js开始执行'),但是并没有执行。这是因为在编译阶段把import进行了提升,类似于var的变量提升,所以会首先执行import语句。
也就是在编译阶段去加载模块,然后在执行阶段就去执行文件,这跟var变量的执行顺序是一样的,即首先会把vara=undefined提升,然后在执行阶段去赋值。
因为这种静态语法,所以import?,?export?不能放在块级作用域或条件语句中。
//name,author,say对应a.js中的name,author,sayimport{name,author,say}from'./a.js'5在编译过程中确定了导入和导出的关系,所以更方便去查找依赖,更方便去treeshaking(摇树),这也是ESMole支持tree-shaking操作的原因。同时,还可以使用各种lint工具对模块依赖进行检查,比如:eslint。
导出绑定:不能修改import导入的属性//name,author,say对应a.js中的name,author,sayimport{name,author,say}from'./a.js'6当执行main.js的时候会报错:UncaughtTypeError:Assignmenttoconstantvariable。通过import导入的值可以看出是一个const常量,不能修改。
引用传递Common.js是值的拷贝,ESMole是引用传递。
Common.js
//name,author,say对应a.js中的name,author,sayimport{name,author,say}from'./a.js'7当第一次打印导入的变量a的值是1,然后执行plus方法,再次打印a发现值仍然是1。当我们通过get方法获取模块内的变量a的时候,发现值为2。所以,在Commonjs规范下,导入的变量只是值的拷贝而已,具体细节可以参考上一篇文章#CommonJS规范详解。
ESMole
//name,author,say对应a.js中的name,author,sayimport{name,author,say}from'./a.js'8当第一次打印导入的变量a的值是1,然后执行plus方法,再次打印a发现值是2。也就是,使用import导入的变量是与原变量是引用关系,而不是拷贝。
import()动态引入import()?返回一个?Promise?对象,返回的?Promise?的then成功回调中,可以获取模块的加载成功信息。我们来简单看一下?import()?是如何使用的。
//name,author,say对应a.js中的name,author,sayimport{name,author,say}from'./a.js'9打印结果如下:
CommonjsVSESMole通过上面的介绍,我们来总结下Commonjs和ESMole的区别:
Commonjs的输出是值的拷贝,ESMole的输出是值的引用。
Commonjs是运行时加载,只有在运行结束后才能确定输入和输出,ESMole在编译的时候就能明确的知道输入和输出,它是一个确定的结果。
像这段代码在仅仅被parse成AST(抽象语法树)时,很难分析出究竟依赖了哪些模块。
constname='dog'constauthor='xiaoming'constsay=function(){console.log('hello,world')}exportdefault{name,author,say}0同样,Commonjs在做模块导出时也无法静态识别:
constname='dog'constauthor='xiaoming'constsay=function(){console.log('hello,world')}exportdefault{name,author,say}1但是在ESMole中,import/exports一目了然,对于没有被import的部分,也很自然的容易区分出来,并进行tree-shaking。
总之,就是通过限定语法,让本来需要运行代码才能确定的依赖,可以在AST阶段就能确定下来。
Commonjs为同步加载,ESMole支持异步加载,可以通过import().then()来实现。
原文;https://juejin.cn/post/7098537945103073316