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

Java学习笔记——JavaScript模块化历史

时间:2023-03-27 13:56:56 JavaScript

1.Infancy——NoModularity将js代码按照功能放入不同的JS文件中,在模板中使用script标签引用文件分离是模块化的第一步存在问题:全局作用域的污染=>不利于大型项目和多人团队的开发共建2.成长期-命名空间模式单一全局变量中流行的命名空间模式JavaScript就是选择一个全局变量作为主要的引用对象。例如,jQuery库就使用了这种方法。varmyApplication=(function(){function(){//...},return{//...}})();命名空间前缀命名空间前缀模式有一个非常清晰的思路,就是选择一个唯一的命名空间,然后在它之后声明变量、方法和对象。varmyApplication_propertyA={};varmyApplication_propertyB={};functionmyApplication_myMethod(){}objectliteralnotationobjectliteral模式可以认为是一个包含一组键值对的对象,每对key和value之间用冒号隔开,key也可以是代码的新命名空间。varmyApplication={//可以轻松地为对象字面量定义函数getInfo:function(){//***},//可以进一步支持对象命名空间models:{},views:{pages:{}},collections:{}};嵌套命名空间嵌套命名空间模式可以说是对象字面量模式的升级版,也是一种有效的避免冲突的模式,因为即使存在命名空间,也不太可能出现嵌套子对象同上的情况。varmyApplication=myApplication||{};//定义嵌套的子对象myApplication.routers=myApplication.routers||{};myApplication.routers.test=myApplication.routers.test||{};IIFE(ImmediatelyInvokedFunctionExpression,ImmediatelyInvokeFunctionExpression)IIFE其实就是立即执行一个匿名函数。在JavaScript中,由于变量和函数是在只能在内部访问的上下文中显式定义的,因此函数调用提供了一种实现私有变量和方法的便捷方式。IIFE是封装应用程序逻辑以保护它不受全局命名空间影响的常用方法,在这里它也可以发挥其特殊作用。var命名空间=命名空间||{};(function(o){o.foo="foo";o.bar=function(){return"bar";};})(namespace);console.log(namespace);//定义一个简单的moduleconstiifeModule=(()=>{让count=0;return{increase:()=>++count,reset:()=>{count=0;}}})();iifeModule.increase();iifeModule.reset();//IIFEconst依赖其他模块iifeModule=((dependencyModule1,dependencyModule2)=>{letcount=0;return{increase:()=>++count,reset:()=>{count=0;}}})(dependencyModule1,dependencyModule2);iifeModule.increase();iifeModule.reset();//显示模块模式constiifeModule=(()=>{letcount=0;functionincreaseCount(){++count;}functionresetCount(){count=0;}return{increase:increaseCount,reset:resetCount}})();iifeModule.increase();iifeModule.reset();/***显示模块模式定义:*在模块模式的基础上,重新定义返回的私有作用域中的所有函数和变量。并返回一个匿名对象。他拥有所有指向私有函数的指针。*Module模式最初被定义为传统软件工程中为类提供私有和公共封装的方法。这里的JS最初使用IIEF封装**/namespaceinjectionNamespaceinjection是IIFE的另一种变体,它从函数包装器内部“注入”特定命名空间的方法和属性,将其用作命名空间代理。这种模式的优点是功能行为可以应用于多个对象或命名空间。varmyApplication=myApplication||{};myApplication.utils={};(function(){varvalue=5;this.getValue=function(){returnvalue;}//定义新的子命名空间this.tools={};}).apply(myApplication.utils);(function(){this.diagnose=function(){return"diagnose";}}).apply(myApplication.utils.tools);命名空间注入用于为多个模块或命名空间指定一组类似的基本功能,但最好在声明私有变量或方法时使用它,否则嵌套命名空间就足够了。自动嵌套命名空间functionextend(ns,nsStr){varparts=nsStr.split("."),parent=ns,pl;pl=零件.长度;for(vari=0;i++计数;constreset=()=>{count=0;}//做一些和引入依赖相关的事情...//暴露接口部分//方法一exports.increase=increase;exports.reset=重置;//方法二module.exports={increase,reset}//index.js//使用moduleconst{increase,reset}=require('./main.js');increase();reset();优点和缺点优点:CommonJS最先在服务端实现,从框架层面解决依赖和全局变量污染问题缺点:主要针对服务端解决方案。异步拉取依赖的处理集成不是那么友好。AMD规范AMD是“AsynchronousModuleDefinition”的缩写,意思是“异步模块定义”。它异步加载模块,模块的加载不影响后面语句的运行。所有依赖该模块的语句都定义在一个回调函数中,回调函数只有在加载完成后才会运行。异步模块定义(AMD)API指定了一种用于定义模块的机制,以便可以异步加载模块及其依赖项。这在浏览器环境中特别有用,在浏览器环境中,同步加载模块会产生性能、可用??性、调试和跨源访问问题。定义模块:definefunctiondefine(id?:String,dependencies?:String[],factory:Function|Object);id是模块名,optionaldependencies指定要依赖的模块列表,它是一个数组,也是可选参数,每个依赖模块的输出都会作为参数传递给工厂一次。如果未指定依赖项,则默认为["require","exports","module"]。define(function(require,exports,module){})factory是最后一个参数,包装了模块的具体实现,是一个函数或者对象。如果是函数,它的返回值就是模块的输出接口或值。调用模块:requirefunctionrequire(dependencies?:String[],callback:Function);示例require(['foo','bar'],function(foo,bar){foo.doSomething();});在define方法中使用requireddefine(function(require){varotherModule=require('otherModule');//dosomethingwithotherModule});优缺点优点:适用于浏览器加载异步模块,可以并行加载多个模块缺点:会有引入成本,UMD(兼容AMD和CommonJS)不能按需加载。UMD(UniversalModuleDefinition),希望提供一个前后端跨平台的解决方案(支持AMD和CommonJS模块)(function(global,factory){if(typeofmodule==="object"&&typeofmodule.exports==="object"){module.exports=factory(global);}elseif(typeofdefine==='function'&&define.amd){define([],factory);}else{global.returnExports=factory();}})(this,function(){//定义模块代码//返回模块内容return{};});CMD规范按需加载主应用框架sea.jsdefine('module',(require,exports,module)=>{let$=require('jquery');//jquery相关逻辑letdependencyModule1=require('./dependecyModule1');//dependencyModule1相关逻辑})优点:按需加载,依赖最近的包,加载逻辑存在于每个模块中,扩展模块体积ES6模块化新时代新定义:引入关键字——importexport关键字——导出的地方模块被导入、导出和定义://导入区importdependencyModule1from'./dependencyModule1.js';从'./dependencyModule2.js'导入dependencyModule2;//实现代码逻辑letcount=0;exportconstincrease=()=>++count;exportconstreset=()=>{count=0;}//导出区域exportdefault{increase,reset}导入模板的地方node:import{increase,reset}from'./esModule.mjs';增加();重置();从'./esModule.mjs'导入esModule;esModule.increase();esModule.reset();面试题六:动态模块调查:exportpromiseES11native解决方法:import('./esModule.js').then(dynamicEsModule=>{dynamicEsModule.increase();})优点(重要性):在最统一形式缺点(局限):本质上,运行时依赖分析是解决模块化的新思路——前端工程的背景是解决前面模块的优化方案,依赖运行时分析来解决问题。前端工程使用离线执行解决这个问题(gruntgulpwebpack)define('a',()=>{letb=require('b');letc=require('c');export.run=(){//run}})工程实现step1:扫描依赖表:{a:['b','c'],b:['d'],e:[]}step2:重新生成依赖数据模板step3:执行工具,使用模块化解决方案解决模块化处理依赖define('a',['b','c'],()=>{//执行代码export.run=()=>{}})优点:构建时生成配置,运行时执行最终转化为执行处理,依赖关系可以充分扩展。基于webpack的工程化+mvvm框架组件化+设计模式参考文档:JS进阶篇--命名空间模式解析CommonJS规范AMD规范RequireJS与AMD规范