当前位置: 首页 > Web前端 > HTML5

说说Js前端模块化规范

时间:2023-04-05 01:23:08 HTML5

抛出问题:开发中导入模块时经常用到require和import;导出模块时使用module.exports/exports或export/exportdefault;奇怪的是require也可以用import????它们之间有什么区别?于是就有了菜鸟解疑解惑的搜索过程。.....追根溯源,我们来到了Js模块化规范1.CommonJS规范(同步加载模块)允许模块通过require方法同步加载自己依赖的其他模块,然后通过exports导出需要暴露的接口或module.exports。用法://导入要求(“模块”);要求(“../app.js”);//导出exports.getStoreInfo=function(){};module.exports=someValue;优点:简单易用的服务器端模块易于重用。缺点:同步加载方式不适合在浏览器环境下使用。同步意味着阻塞加载。浏览器资源是异步加载的。不能非阻塞并行加载多个模块?为什么浏览器不能使用同步加载,而服务端可以呢?因为模块都是放在服务器端的,对于服务器端,加载模块,而对于浏览器端,因为模块放在服务器端,所以加载时间还取决于网速等因素,如果需要等待很长时间,整个应用会被阻塞。因此,浏览器端的模块不能使用“同步加载”(CommonJs),只能使用“异步加载”(AMD)。引用CommonJs模块来表示node.js的模块系统AMD(AsynchronousLoadModule)异步加载模块,模块的加载不影响后续语句的运行。所有依赖于该模块的语句都定义在一个回调函数中,直到加载完成才会执行回调函数。使用示例://definedefine("module",["dep1","dep2"],function(d1,d2){...});//加载模块require(["module","../app"],function(module,app){...});加载模块要求([模块],回调);第一个参数[module]是一个数组,里面的成员是要加载的模块;第二个参数callback为加载成功后的回调函数。优点:适合在浏览器环境下异步加载模块。可以并行加载多个模块。缺点:开发成本增加,代码读写困难,模块定义语义不流畅,不符合一般的模块化思维模式。这种折衷的实现实现了AMD规范代表require.jsRequireJS对模块的态度是pre-execution。由于RequireJS是AMD规范的实现,所有依赖的模块先执行;即RequireJS提前执行依赖模块,相当于提前require。js文件的实际路径根据js文件的实际路径,在dom中插入script节点,并绑定onload事件,获取模块加载完成的通知。所有依赖脚本加载完成后,调用回调函数CMD规范(异步加载模块)CMD规范类似于AMD,简单,与CommonJS和Node.js的Modules规范保持了很好的兼容性;在CMD规范中,A模块只是一个文件。定义模块使用全局函数define,接收工厂参数。工厂可以是函数、对象或字符串;factory是一个三参数函数,function(require,exports,module):require是一个方法,接受模块ID作为唯一参数获取其他模块提供的接口:require(id)exports是一个对象,用于对外提供模块接口。module是一个对象,存储了一些与当前模块关联的属性和方法示例:define(function(require,exports,module){vara=require('./a');a.doSomething();//依赖是写在附近,什么时候用什么时候引入varb=require('./b');b.doSomething();});优点:依赖就近,延迟执行在Node.js中可以轻松运行缺点:依赖在SPM封装上,模块的加载逻辑偏向于实现代表库sea.js:SeaJS对模块的态度是惰性执行,SeaJS只会在真正需要使用(依赖)模块时才执行模块。AMD和CMD的区别对于依赖模块,AMD是提前执行,CMD是延迟执行。不过从RequireJS2.0开始,也改成了可以延迟执行(根据不同的写法,不同的处理方式)。CMD提倡越懒越好,AMD提倡靠前面;CMD提倡就近依赖,只在使用到某个模块时才需要。//AMDdefine(['./a','./b'],function(a,b){//依赖一定要写在开头a.doSomething()//这里省略100行b.doSomething()...});//CMDdefine(function(require,exports,module){vara=require('./a')a.doSomething()//这里省略100行varb=require('./b')//依赖可以写在附近b.doSomething()//...});UMDUMD是AMD和CommonJS的结合AMD基于浏览器第一原理开发的异步加载模块。CommonJS模块以服务器优先的原则开发,选择同步加载,其模块不需要打包。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规范,成为浏览器和服务器的通用模块解决方案。ES6模块设计思想:尽可能静态,这样模块的依赖关系在编译时就可以确定,输入输出变量也可以确定(CommonJS和AMD模块只能在运行时确定这些东西)。Usage://importimport"/app";importReactfrom“react”;import{Component}from“react”;//exportexportfunctionmultiply(){...};exportvaryear=2018;exportdefault...优点:易于进行静态分析面向未来的EcmaScript标准缺点:该标准的新命令词尚未在原生浏览器端实现,新版Node.js仅支持。回到问题“require和import的区别”require使用的是CommonJs规范,import使用的是Es6模块规范;所以两者的区别本质上就是两种规格的区别;CommonJS:对于基本数据类型来说,就是拷贝。即会被模块缓存;同时,模块输出的变量可以在另一个模块中重新赋值。对于复杂的数据类型,它是浅拷贝。由于两个模块引用的对象指向同一个内存空间,修改模块的值会影响另一个模块。当使用require命令加载模块时,将运行整个模块的代码。当使用require命令加载同一个模块时,不会再次执行该模块,而是取缓存中的值。也就是说,无论CommonJS模块被加载多少次,第一次加载时只会运行一次,如果以后加载,会返回第一次运行的结果,除非系统缓存被手动清除。在循环加载期间,它在加载过程中执行。也就是说,当需要脚本代码时,它会被完整地执行。一个模块一旦被“循环加载”,只有执行过的部分才会输出,没有执行过的部分不会输出。ES6模块ES6模块中的值属于【动态只读引用】。对于只读,即不允许修改导入变量的值,导入变量是只读的,无论是基本数据类型还是复杂数据类型。当模块遇到导入命令时,会生成一个只读引用。当脚本真正执行时,根据只读引用去加载模块获取值。对于动态来说,如果原来的值发生变化,那么import加载的值也会发生变化。无论是基本数据类型还是复杂数据类型。循环加载时会动态引用ES6模块。只要两个模块之间存在引用,代码就能够执行。最后:require/exports是必要且常见的;因为实际上你写的import/export最终是编译成require/exports来执行的。参考AMD和CMD规范解释CommonJS模块和ES6模块的区别。Js模块规范了解Js模块化Require和import的区别。往期经典:Git操作,团队协作必备,一个页面从输入url到加载显示就完成了,发生了什么?相关好文推荐:网络篇——浏览器缓存(一)