手机版

深入理解需求——实现一个简单的模块加载器

时间:2021-09-01 来源:互联网 编辑:宝哥软件园 浏览:

在上一篇文章中,我们不止一次强调了模块化编程的重要性以及它可以解决的问题:

(1)解决单文件变量命名冲突的问题

解决前端多人协作的问题

解决文件依赖问题

4按需加载(这种说法实际上是非常错误的)

.

为了深入了解加载器,我看了一点requireJS的源代码,但是对于很多同学来说,加载器的实现还是不太清楚

事实上,如果你没有通过代码实现它,你只能通过单独阅读来理解一个库或框架,所以今天你将实现一个简单的加载器

加载器原理分析

点收盘

事实上,一个程序需要一个完整的模块来运行。以下面的代码为例:

//得到性能系数var性能系数=function(){ return 0.2;};//住房公积金var公司准备金的计算方法=函数(工资){返回工资* 0.2;};//个人所得税var所得税=函数(工资){返回工资* 0.2;};//基本工资var工资=1000;//最终工资var myssalary=工资薪金*绩效系数();mySalary=mySalary-company reserve(mySalary)-incomeTax(mySalary-company reserve(mySalary));console . log(MySalary);对于我的完全工资,公司会有绩效奖励,但它的算法可能很复杂,可能涉及到出勤率、完成度等,这里暂时忽略

有增必有减,所以我们要缴纳住房公积金,扣除个人所得税,最终就是我的工资

对于一个完整的程序来说,以上流程是不可或缺的,但每个功能可能都极其复杂,而且与金钱有关的一切都很复杂,所以光是公司业绩就可能超过1000行代码

所以我们这边会开始分裂:

script src=' http : company reserve . js ' type=' text/JavaScript '/script script src=' http : incometax . js ' type=' text/JavaScript '/script script src=' http : performance coefficient . js ' type=' text/JavaScript '/script script type=' text/JavaScript '/基本工资var工资=1000;//最终工资var myssalary=工资薪金*绩效系数();mySalary=mySalary-company reserve(mySalary)-incomeTax(mySalary-company reserve(mySalary));console . log(MySalary);上面的代码/脚本显示世界是“分裂”的,这实际上造成了“合并”的问题。我怎样才能把它们很好地组合在一起?毕竟,文件也可能涉及依赖关系,所以我们在这里输入我们的需求和定义。

要求并定义

其实以上方案还是按文件划分,不是按模块划分。如果文件名改变,页面也会改变。事实上,应该有一个路径映射来处理这个问题

var path CFG={ ' company reserve ' : ' company reserve ',' incomeTax': 'incomeTax ',' performance coefficient ' : ' performance coefficient ' };所以我们的一个模块对应一个path js文件,剩下的就是加载对应的模块,因为前端模块涉及请求。所以这种写作方式:

company reserve=require(' company reserve ');它不适用于前端。即使你在某个地方看到了,你也一定做了一些“手脚”。这里我们需要遵循AMD的规范:

require . config({ ' company reserve ' : ' company reserve ',' incomeTax': 'incomeTax ',' performance coefficient ' : ' performance coefficient ' });该要求(['公司准备金','所得税','绩效系数'],函数(公司准备金,所得税,绩效系数){//基本工资var薪资=1000;//最终工资var myssalary=工资薪金*绩效系数();mySalary=mySalary-company reserve(mySalary)-incomeTax(mySalary-company reserve(mySalary));console . log(MySalary);});这是编写requireJS的标准方法。首先,定义模块及其路径映射,其中定义了依赖关系

require(depar,callback)一个简单完整的模块加载器基本上是这样的。首先,它是一个依赖数组,后面是一个回调。回调需要在运行之前加载所有依赖项,回调参数是依赖项执行的结果,所以一般要求define模块有返回值。

有了计划,怎么实现?

实施方案

说到模块加载,人们的第一反应就是ajax,因为每当他们能够获得模块文件的内容时,基本上都是模块化的,但是ajax是不可行的,因为ajax存在跨域的问题

模块化方案不可避免地要处理跨域问题,因此使用动态创建的脚本标签来加载js文件是首选。然而,没有ajax的方案对实现难度仍有要求

PS:在我们的实际工作中,会有一个加载html模板文件的场景,后面会讲到

通常,我们这样做。require被用作调度javascript资源的程序入口,在将它们加载到每个定义模块中之后,每个模块将悄悄地创建脚本标签来加载

加载后,向require模块队列报告您已经完成加载,当require中的许多依赖模块已经完成加载时,执行它们的回调

原理大致相同,剩下的只是具体实现,然后还要论证这个理论是否可靠

加载器阉割实现

核心模块

根据上述理论,我们把入口的全部,首先是三个基本功能

var require=function(){ };require . config=function(){ };require . define=function(){ };这三个模块不可或缺:

(1) config用于配置模块和路径之间的映射,或者用于其他目的

require是程序条目

定义设计每个模块,并响应要求的时间表

然后我们将有一个方法来创建一个脚本标签,并监听它的onLoad事件

加载脚本

其次,在我们加载脚本标签之后,应该有一个全局模块对象来存储加载的模块,所以这里提出了两个要求:

require . moduleobject模块存储对象

模块,模块的构造器

通过上述核心模块,我们形成了以下代码:

(function(){ var Module=function(){ this . status=' loading ';//只有加载和已加载两种状态。this . Depcount=0;//模块依赖项this.value=null//定义回调执行的返回};var loadScript=函数(url,回调){ };var config=function(){ };var require=function (deps,callback){ };require . config=function(CFG){ };var define=function (deps,回调){ };})();所以下一步就是具体的实现,然后补上实现过程中没有的接口和细节,最后的实现往往和最初的设计没有关系.

代码实现

这一块刚实现的时候,我本来想直接引用requireJS的实现,但是我们老板笑着拿出了他写的一个加载器。我不得不承认,乍一看有点妖

因此,在这里,我们从它的实现中学习并进行简单的转换:

(function () {//存储加载的模块var moduleCache={ };var require=function (deps,callback){ var params=[];var Depcount=0;var i,len,isEmpty=false,modName//获取当前正在执行的js代码段,在onLoad事件之前执行。modname=document . currentscript document . currentscript . id | | ' require _ main ';//实现简单,这里不做参数检查,只考虑数组if (deps)的情况。长度){for (I=0,len=deps。长度;我透镜;I) {(函数(i) {//依赖加上一个depCount//这次回调非常关键。loadmod (deps [I],function(param){ params[I]=param;depCount-;if(Depcount==0){ save module(ModName,params,callback);} });})(I);} } else { isEmpty=true} if(isEmpty){ setTimeout(function(){ save module(ModName,null,callback);}, 0);} };//考虑最简单的逻辑:var _ getpath URL=function(modname){ var URL=modname;//松散if (URL。indexof('。js ')===-1)URL=URL '。js ';返回url};//模块加载var load mod=function (modname,回调){var URL=_ getpathurl (modname),fs,mod;//如果模块已经加载,如果(module cache[modname]){ mod=module cache[modname];if(mod . status==' loaded '){ setTimeout(回调(this.params),0);} else {//如果加载前直接将值插入onLoad,加载后会从mod.onload.push(回调)中释放依赖关系;}} else {/*这里强调的是,Module对象状态代表的是模块状态onLoad,实际对应的是requireJS的事件回调,以及引用该模块时执行了多少个回调。通知依赖项释放依赖项*/mod=module cache[modname]={ modname 3360 modname,status: ' loading ',export3360 null,on load :[callback]};_ script=document . create element(' script ');_ script.id=modName_ script . type=' text/JavaScript ';_ script . charset=' utf-8 ';_ script.async=true_ script.src=url//这段代码在这个场景中意义不大,//_script.onload=function (e) {}有注解。fs=document . getelementsbytagname(' script ')[0];fs . parent node . insert before(_ script,fs);} };var saveModule=function (modName,params,callback) { var mod,fn;if(Modulecache . HasownProperty(ModName)){ Mod=Modulecache[ModName];mod.status='已加载';//输出项mod.export=回调?回调(参数): null//移除父类的依赖关系。事实上,最好使用事件监视while(fn=mod . onload . shift()){ fn(mod . export);} } else { callback callback . apply(window,params);} };window.require=requirewindow.define=require})();首先,这段代码有一些问题:

参数不被处理,字符串等不被处理

未解决的循环依赖问题

未处理的CMD写入

未处理的html模板加载相关性

参数配置没有被处理,baseUrl没有做任何事情

基于此,不可能打包文件

.

但是这100行代码是加载器的核心。代码很短,对你理解加载器很有帮助。有两点需要注意:

(1) requirejs使用事件监控处理自己的依赖关系,这里直接放入onLoad数组。

这里有一件非常有趣的事情

Document.currentScript这可以获取当前执行的代码段

Requirejs处理onLoad中的每个模块,这里使用不同的实现。加载每个JS文件后,将执行require(define)方法

执行后,您会得到当前正在执行的文件,并得到文件名来加载它。因此,即使脚本的onLoad事件也被保存.

演示实现

html xmlns=' http://www。w3。org/1999/XHTML ' head title/title/head body/body script src=' http 3360 require。js ' type=' text/JavaScript '/script script type=' text/JavaScript ' require([' util ',' math ',' num'],function (util,math,num){ num=math。getradom()' _ ' num;num=util。格式编号(num);控制台。日志(数字);});/script/html//utildefine([],function(){ return { format num 3360 function(n){ if(n ^ 10)return ' 0 ' n;返回n;} };});//mathdefine(['num'],function(num){ return { getradom : function(){ return parsent(math。random()* num);} };});//mathdefine(['num'],function(num){ return { getradom : function(){ return parsent(math。random()* num);} };});

小结

今天我们实现了一个简单的模块加载器,通过他希望可以帮助各位了解模块化开发或者seaJS,最后顺利进入模块化编程的行列

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

版权声明:深入理解需求——实现一个简单的模块加载器是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。