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

【JS基础】一文看懂前端模块化规范

时间:2023-04-04 01:36:12 Node.js

前言前端的模块化之路经历了漫长的过程。想深入了解的可以看看浪中航行大师写的前端模块化详解(完整版),根据几位大佬写的文章,这里总结整理一下模块化规范部分。希望看过的小伙伴能够有所收获,也希望觉得有用的小伙伴点个赞,补充一下。什么是模块?一个复杂的程序,按照一定的规则(标准)封装成若干个块(文件),组合在一起。块的内部数据和实现都是私有的,只对外暴露一些接口(方法)。与其他模块的通信CommonJSNode应用程序由模块组成,采用CommonJS模块规范。每个文件都是一个模块,有自己的作用域。文件中定义的变量、函数和类都是私有的,对其他文件不可见。在服务器端,模块在运行时同步加载;在浏览器端,模块需要提前编译打包。CommonJS规范的加载模块是同步的,也就是说加载完成后才能进行后面的操作。基本语法:exposemodule:module.exports=valueorexports.xxx=valueimportmodule:require(xxx),如果是第三方模块,xxx是模块名;如果是自定义模块,xxx是模块文件路径但是,CommonJs有一个明显的局限性使其不适合浏览器环境,那就是require操作是同步的。这在服务器端是没有问题的,因为所有的模块都存储在本地硬盘中,可以同步加载,等待的时间就是硬盘的读取时间。但是,对于浏览器来说,这是一个很大的问题,因为模块是放在服务器端的,等待时间取决于网速,可能需要很长时间,浏览器处于“假死”状态.因此,浏览器端的模块不能使用“同步加载”(synchronous),只能使用“异步加载”(asynchronous),这也是AMD规范诞生的背景。AMD特性:异步加载模块,允许指定回调函数,浏览器一般使用AMD规范代表作:require.jsusage://definemoduleswithoutdependenciesdefine(function(){returnmodule})//definedependenciesModuledefine(['module1','module2'],function(m1,m2){returnmodule})//引入和使用模块require(['module1','module2'],function(m1,m2){//使用m1/m2})CMD特点:专用于浏览器端,模块的加载是异步的,在使用模块时会加载并执行模块。exports,module){exports.xxx=valuemodule.exports=value})//定义依赖模块define(function(require,exports,module){//引入依赖模块(同步)varmodule2=require('./module2')//引入依赖模块(异步)require.async('./module3',function(m3){})//暴露模块exports.xxx=value})//引入并使用模块define(function(require){varm1=require('./module1')varm4=require('./module4')m1.show()m4.show()})CMD和AMD的区别AMD和CMD最大的区别就是dependencymodule执行时机的处理不同,不是加载的时机或方法。两者都是异步加载模块。AMD依赖前端,js可以轻松知道依赖的模块是谁,并立即加载;而CMD依赖最近的,需要用模块解析成字符串才能知道依赖了哪些模块。这也是很多人诟病CMD的一点,牺牲性能来带来开发的方便,其实解析模块使用的时间短到可以忽略不计。一句话总结:两者都是异步加载,只是执行的时机不同。AMD靠前端,提前执行,而CMD靠就近,延迟执行。UMDUMD是AMD和CommonJS的结合:AMD模块采用浏览器优先原则开发,模块异步加载。CommonJS模块基于服务器优先原则开发,选择同步加载,其模块不需要打包(unwrappedmodules)。这就迫使人们想出另一种更通用的模型UMD(UniversalModuleDefinition)。希望有一个跨平台的解决方案。UMD首先判断支持Node.js的模块(exports)是否存在,存在则使用Node.js模块模式。在判断是否支持AMD时(define是否存在),存在则使用AMD方式加载模块。(function(window,factory){if(typeofexports==='object'){module.exports=factory();}elseif(typeofdefine==='function'&&define.amd){define(factory);}else{window.eventUtil=factory();}})(this,function(){//module...});ES6模块化ES6模块被设计成尽可能静态的,这样在编译时可以确定模块的依赖关系,以及输入和输出的变量。CommonJS和AMD模块都只能在运行时确定这些东西。例如,CommonJS模块是对象,必须在输入时查找对象属性。ES6Module浏览器默认还不支持,需要用babel,每天写demo时经常出现这个错误:ES6modules使用import关键字导入模块,export关键字导出模块:/**导出模块的方式**/vara=0;export{a};//第一类exportconstb=1;//第二种letc=2;exportdefault{c}//第三种letd=2;exportdefault{dase}//第四种,别名/**导入模块的方式**/import{a}from'./a.js'//export导出方式,.js后缀可以省略importmainfrom'./c'//export默认导出方式,使用main.cimport'lodash'//只执行lodash模块,不输入任何值namedexport和defaultexportexport{}这种方法一般称为namedexport或namedexport,导出的是对变量的引用。这种导出default的方式叫做默认导出或者匿名导出,导出的是一个值。示例://a.jsletx=10lety=20setTimeout(()=>{x=100y=200},100)export{x}exportdefaulty//b.jsimport{x}from'./a.js'importyfrom'./a.js'setTimeout(()=>{console.log(x,y)//100,20},100)ES6模块和CommonJS模块的区别①CommonJS模块输出一份值,ES6模块输出是对值的引用。CommonJS模块输出值的副本,即一旦输出值,模块内部的更改不会影响该值。而且无论CommonJS模块被加载多少次,第一次加载时只会运行一次,如果以后加载,会返回第一次运行结果的缓存,除非系统缓存被手动清除。?ES6模块的运行机制与CommonJS不同。JS引擎静态分析脚本时,遇到模块加载命令import时会生成一个只读引用。获取加载模块中的值。也就是说,ES6的导入有点像Unix系统的“符号链接”。当原值改变时,import加载的值也会改变。因此,ES6模块是动态引用,不会缓存值。模块中的变量绑定到它们所在的模块。②CommonJS模块是运行时加载的,ES6模块是编译时的输出接口。CommonJS加载的是一个对象(即module.exports属性),只有在脚本运行完毕后才会生成。即在输入的时候,先加载整个模块,生成一个对象,然后从对象中读取方法。这种加载称为“运行时加载”。例如://CommonJSmodulelet{stat,exists,readFile}=require('fs');//等同于let_fs=require('fs');letstat=_fs.stat;letexists=_fs。存在;让readfile=_fs.readfile;上面代码的本质是整体加载fs模块(即加载fs的所有方法),生成一个对象(_fs),然后从这个对象中读取3个方法。因为这个对象只能在运行时获取,所以没办法在编译期做“静态优化”。ES6模块不是对象,它的对外接口只是一个静态定义,在静态代码分析阶段会生成。输出代码是通过export命令显式指定的,import是静态命令的形式。即在导入时,可以指定加载某个输出值,而不是加载整个模块。这种加载称为“编译时加载”或“静态加载”。//ES6模块import{stat,exists,readFile}from'fs';上面代码的本质是从fs模块加载3个方法,并没有加载其他方法。即ES6可以在编译时完成模块加载,比CommonJS模块加载效率更高。当然,这也使得无法引用ES6模块本身,因为它不是对象。静态分析是可能的,因为ES6模块是在编译时加载的。有了它,可以进一步拓宽JavaScript的语法,比如引入宏、类型系统等只能通过静态分析才能实现的功能。除了静态加载带来的各种好处之外,ES6模块还有以下好处:不再需要UMD模块格式,未来服务器和浏览器都将支持ES6模块格式。目前通过各种工具库,实际上已经做到了这一点。以后浏览器新的API可以以模块的形式提供,不再需要做成全局变量或者navigator对象的属性。不再需要对象作为命名空间(例如Math对象),将来可以通过模块提供这些功能。总结一下,CommonJS规范主要用于服务端编程,加载模块是同步的,不适合浏览器环境,因为同步意味着阻塞加载,而浏览器资源是异步加载的,所以有AMD和CMD解决方案。AMD规范在浏览器环境中异步加载模块,可以并行加载多个模块。但AMD规范开发成本高,代码读写困难,模块定义方法语义不流畅。CMD规范与AMD规范非常相似,都是用于浏览器编程,依赖接近,延迟执行,并且可以很容易地在Node.js中运行。但是依赖SPM封装,模块的加载逻辑是有偏差的。ES6在语言标准层面实现了模块功能,实现起来相当简单。它可以完全替代CommonJS和AMD规范,成为浏览器和服务器的通用模块方案。以上就是本文的内容。欢迎大家提出自己的想法。我们会一起学习进步,鼓励你。参考资料前端模块化详解(完整版)ECMAScript6入门近万字ES6语法知识点补充吃透javascript中require、import、exportCommonJS、AMD、CMD、ES6、require、import详解