当前位置: 首页 > 后端技术 > Node.js

前端模块——ES6和commonJS的区别

时间:2023-04-03 16:46:15 Node.js

一、概述模块化就是把变量和函数放到不同的文件中。该模块的范围是私有的。内部定义的代码只能在当前文件中使用。模块暴露了模块化的含义,减少了全局变量,避免了变量名和函数命名冲突,提高了代码的复用性和可维护性。commonJS和module依赖解析的区别是“动态的”,而ES6Module是静态的。commonjs模块输出的是valueShallowcopy,ES6模块输出的是value的引用(cmmonjs模块输出后改变,其引用模块不会改变,ES6模块会改变)。commonJS的“动态”是指模块依赖的建立发生在代码运行阶段。ES6中的术语“静态”意味着模块依赖构建发生在代码编译期间。webpack的tree-shaking只能应用于ES6modules,因为ES6modules在编译的时候可以确定依赖。2.commonJSCommonJS规范,每个模块内部有两个变量,可用于使用require和modulerequire加载模块。module代表当前Module是一个对象,保存了当前模块的信息。exports是模块上的一个属性,保存了当前模块要导出的接口或变量。使用require加载的模块获得的值是该模块使用exports导出的值。module.exports对象将用作require函数的返回值。加载。require的模块路径可以动态指定,可以传入一个表达式,也可以使用if语句来判断是否加载某个模块。因此,在执行CommonJS模块之前,无法明确依赖关系,模块的导入导出发生在代码运行时。CommonJSexportsNode.js中的CommonJS规范,每个模块都有一个exports私有变量,exports指向模块。exportsexports是模块中私有的局部变量,它只是指向module.exports,所以直接赋值给exports是无效的,这只会让导出不再指向module.exports。//可以理解为在每个模块的开头默认添加如下代码varexports=modules.exports//test.jsconstname='yang';letage=29;exports.name=name;exports.getAge=function(){返回年龄;};CommonJS的require命令的基本功能是读取并执行一个js文件,然后返回模块的exports对象。如果没有找到指定的模块,就会报错。当第一次加载模块时,Node会缓存模块,稍后再次加载模块时,会直接从缓存中读取module.exports属性。CommonJS模块的加载机制是require是导出值的副本。也就是说,一旦一个值被导出,模块内部的变化不会影响这个值//test.jsconstname='yang';letage=29;exports.name=name;exports.age=age;exports.setAge=function(){age++;}//index.jsletp=require('./test.js');//yangconsole.log(p.name);//29console.log(p.age);p.name='yang++'//yang++console.log(p.name);//内部age++不影响导出值p.setAge();控制台日志(p.age);//29//导出的age++会自增p.age++;让b=require('./test.js');//yang++console.log(b.name);//30console.log(b.age);实现一个commonJS给一个立即执行函数提供require、exports、module三个参数,模块代码放在立即执行函数中。模块导出值放在module.exports中,实现模块化加载(function(module,exports,require){//b.jsvara=require("a.js")console.log('a.name=',a.name)console.log('a.age=',a.getAge())varname='yang'varage=29exports.name=nameexports.getAge=function(){returnage}})(module,module.exports,require)webpack编译后的代码//bundle.js(function(modules){//模块管理的实现})({'a.js':function(module,exports,require){//a.js文件的内容},'b.js':function(module,exports,require){//b.js文件的内容},'index.js':function(module,exports,require){//index.js文件内容}})webpack实现了__webpack_require__,初始化了一个module对象,放入installedModules中。再次引用该模块时,直接从installedModules中取值。此时,它是一个空对象,这就解释了上面例子的现象。function__webpack_require__(moduleId){/******//******///检查模块是否在缓存中/******/if(installedModules[moduleId]){/******/returninstalledModules[moduleId].exports;/******/}/******///创建一个新模块(并放入缓存)/******/varmodule=installedModules[moduleId]={/******/i:moduleId,/******/l:false,/******/exports:{}/******/};/******//******///执行模块函数/******/modules[moduleId].call(module.exports,module,module.exports,__webpack_require__);/******//******///将模块标记为已加载/******/module.l=true;/******//******///返回模块的exports/******/returnmodule.exports;/******/}三、ES6ModuleES6Module的导入导出是declarations风格,不支持import路径作为表达式,所有的imports和exports都必须位于模块的顶层范围内(不能放在if语句中)。因此ES6Module是一个静态的模块结构,在ES6代码编译阶段就可以分析模块的依赖关系。ES6改进了死代码检测和排除,通过静态分析工具检测哪些模块没有被调用。比如引入了一个工具库,项目可能只使用了某个接口,但是可能整个工具包都加载进去了,没有被调用的代码永远不会执行。通过静态分析,可以在打包时去掉这些不用的模块,减少打包资源的体积。模块变量类型检查,js是动态类型语言,不会在代码执行前检查类型错误。例如调用字符串类型的函数。ES6Module的静态模块结构可以保证模块之间传递的值或接口类型是正确的。编译器优化,CommonJS本质上是导入一个对象,而ES6Module支持导入变量,减少引用层级,让程序更高效。值拷贝和动态映射导入模块时,CommonJS是导出值的拷贝,而ES6Module是值的动态映射,这个映射是只读的。CommonJS修改文件中导入的值不会更改导入文件上的值。因为它是一个复制值。ES6Module中导入的变量是原始值的动态映射,ES6Module中导入的变量不能更改,因为这个映射是只读的。循环依赖循环依赖是指模块A依赖模块B,模块B依赖模块A(项目中要尽量避免循环依赖,会增加复杂度,依赖关系不明确)。说明以上内容出处和复习时的网络搜索,也主要用于个人学习,相当于一个记事本的存在,链接文章暂不列出。如果作者看到了,可以联系我,贴出原文链接。