JavaScript模块化开发流程分步讲解
作者:dawn
接触了Vue模块化开发才发现JavaScript组件化开发的演变一直在继续,以前也没有特别在意这一块内容,写着代码能调试运行不报错就可以了,主要编程语言标准就是ECMAScript5和ECMAScript6(2015年发布),使用ECMAScript6规范编写的程序需要转译器将代码编译为ECMAScript5才能为浏览器支持。作为前度开发者了解这些以及ECMAScript5和ECMAScript6的写法区别即可。
比如,ECMAScript5的写法:
var MyModuleName = { Property1: "", Property2: "", Config: { SetName1:"", SetName2:"" }, Method1: function() {}, Method2: function() {} };
对应的ECMAScript6的写法:
export const MyModuleName = { Property1: "", Property2: "", Config: { SetName1: "", SetName2: "" }, myMethod1() {}, myMethod2() {} };
其实学习前端开发仅仅知道大概是不行的,现在把这一块的内容详细梳理一下。
1、使用传统的全局命名空间
这样情况下的缺点显而易见:
⑴全局变量无法控制是否冲突;
⑵成员函数之间看不出直接关系。
示例代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>最原始的模式</title> </head> <body> <div id="view"></div> <script> var a=1,b=2; const viewId=document.getElementById('view'); const contentId=document.createElement('div'); contentId.innerHTML="a+b="+add(a,b); viewId.appendChild(contentId); function add(a,b){ return a+b; } function subtract(a,b){ return a-b; } </script> </body> </html>
可以出正确结果,如果进行所谓的模块化开发,就是将代码不同文件化来进行。
主文件:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>最原始的模式</title> </head> <body> <div id="view"></div> <script> var a=1,b=2; </script> <script src="module2.js"></script> <script src="module1.js"></script> </body> </html>
module1.js代码:
const viewId=document.getElementById('view'); const contentId=document.createElement('div'); contentId.innerHTML="a+b="+add(a,b); viewId.appendChild(contentId);
module2.js代码:
var add=function(a,b){ return a+b; } var subtract=function(a,b){ return a-b; }
但是这导致可能因为文件引入顺序而出现运行错误,如果文件多了,依赖不容易检查,那么纠错就是一件让人头疼的事情了。
2、使用对象的写法
缺点:
⑴暴露所有模块成员;
⑵内部状态可以被外部改写;
⑶多文件化后依赖关系不好处理。
示例代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>使用对象写法</title> </head> <body> <div id="view"></div> <script> var a=1,b=2; var MyUtils=new Object({ Property1: "as", add:function(a,b){ return a+b; }, subtract:function(a,b){ return a-b; } }); const viewId=document.getElementById('view'); const contentId=document.createElement('div'); var result=MyUtils.add(a,b); contentId.innerHTML="a+b="+result; viewId.appendChild(contentId); </script> </body> </html>
3、使用命名空间的写法
前端开发者基本上都使用过JQuery.js来进行前端开发,JQuery.js主要使用率命名空间模式来组织代码,定义了一个全局变量 $ 或 jQuery,该变量是一个对象,包含了所有 jQuery 提供的功能。当使用 $或者jQuery 时,实际上就是访问这个全局变量。而 jQuery 库中所有的方法和属性都是在这个全局变量上定义的。
缺点:
⑴无法解决相互依赖问题特别是多文件化后;
⑵代码组织形式逐渐复杂化;
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>使用命名空间的写法</title> </head> <body> <div id="view"></div> <script> var a=1,b=2; var MyApp = { UI: { //定义UI操作内容 }, Data: { //定义Data内容 }, Service: { //定义Service的内容 }, Utils: { //定义工具类内容 add:function(a,b){ return a+b; }, subtract:function(a,b){ return a-b; } } }; const viewId=document.getElementById('view'); const contentId=document.createElement('div'); var result=MyApp.Utils.add(a,b); contentId.innerHTML="a+b="+result; viewId.appendChild(contentId); </script> </body> </html>
最开始常见的写法也可以是这样的:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>使用IIFE(立即执行函数)写法</title> </head> <body> <div id="view"></div> <script> var a=1,b=2; //定义全局变量 var MyUtils={}; //定义子命名空间 var MyUtils.UI={}; var MyUtils.Data={}; var MyUtils.Service={}; var MyUtils.Utils={}; //在子命名空间中定义内容 var MyUtils.Utils.add=function(a,b){ return a+b; }; const viewId=document.getElementById('view'); const contentId=document.createElement('div'); var result=MyUtils.add(a,b); contentId.innerHTML="a+b="+result; viewId.appendChild(contentId); </script> </body> </html>
4、使用IIFE的写法
缺点:
⑴外部代码无法读取内部的变量;
⑵无法彻底解决模块间的相互依赖问题。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>使用IIFE(立即执行函数)写法</title> </head> <body> <div id="view"></div> <script> var a=1,b=2; var MyUtils=(function(){ Property1: "as"; var add=function(a,b){ return a+b; }; var subtract=function(a,b){ return a-b; } return { add:add, subtract:subtract } })(); const viewId=document.getElementById('view'); const contentId=document.createElement('div'); var result=MyUtils.add(a,b); contentId.innerHTML="a+b="+result; viewId.appendChild(contentId); //外部代码无法读取内部的变量。 </script> </body> </html>
要解决外部访问对象内部数据,可以对外暴露方法:
var a=1,b=2; var MyUtils=(function(){ Property1: "as"; var add=function(a,b){ return a+b; }; var subtract=function(a,b){ return a-b; }; var setProperty=function(str){ this.Property1=str; }; return { add:add, subtract:subtract, setProperty } })(); const viewId=document.getElementById('view'); const contentId=document.createElement('div'); var result=MyUtils.add(a,b); contentId.innerHTML="a+b="+result; viewId.appendChild(contentId); MyUtils.setProperty("123"); console.log(MyUtils.Property1);
上面的代码暴露了setProperty方法,可以操作对象的内部属性值。
遵循IIFE(立即执行函数)规范很好地使用了闭包的特点,可以进行多模块的开发:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>使用IIFE(立即执行函数)写法</title> </head> <body> <div id="view"></div> <script> var a=1,b=2; var MyUtils1=(function(){ Property1: "as"; var add=function(a,b){ return a+b; }; var setProperty=function(str){ this.Property1=str; }; return { add:add, setProperty } })(); var MyUtils2=(function(){ Property1: "untils2"; var add=function(a,b){ return a+b; }; var setProperty=function(str){ this.Property1=str; }; return { add:add, setProperty } })(); //"继承"前面两个模块 var MyUtils=(function(MyUtils1,MyUtils2){ MyProperty: "MyUntils"; function add(a,b){ return MyUtils1.add(a,b); }; function subtract(a,b){ return MyUtils1.subtract(a,b); }; return { add:add, subtract:subtract } })(MyUtils1,MyUtils2); const viewId=document.getElementById('view'); const contentId=document.createElement('div'); var result=MyUtils.add(a,b); contentId.innerHTML="a+b="+result; viewId.appendChild(contentId); </script> </body> </html>
但是最终的模块严格意义上并不是真正地继承前面的两个模块,只是依赖这两个模块的注入。
本想在这一篇把所有的演变模式总结完毕,可是后续的内容太多了并且后面的内容才是重点,写到这里文字已经有点多了,还是先写到这里。
到此这篇关于JavaScript模块化开发流程分步讲解的文章就介绍到这了,更多相关JS模块化开发内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!