如何自创网站,建设主管部门门户网站,买了一个域名如何做网站,企业网站建设公司制作平台[前端]模块化开发 为什么需要模块化开发模块化开发的目标模块化开发的演化文件划分方案对象封装方案立即执行函数 模块化开发规范CommonJS 规范特点定义模块全局对象 global导出模块(变量或方法)导入模块 AMD 规范特点定义模块导出模块(变量或方法)导出对象导出函数导出符合 Co… [前端]模块化开发 为什么需要模块化开发模块化开发的目标模块化开发的演化文件划分方案对象封装方案立即执行函数 模块化开发规范CommonJS 规范特点定义模块全局对象 global导出模块(变量或方法)导入模块 AMD 规范特点定义模块导出模块(变量或方法)导出对象导出函数导出符合 CommonJs 规范的接口 导入模块require 导入方式define 导入方式 CMD 规范定义模块导出模块(变量或方法)导入模块 UMD 规范ES6 规范特点局限性导出与导入模块 规范的实践案例规范的混合使用 为什么需要模块化开发
随着时代的发展,系统功能的不断增加,导致代码体量的不断增加,随着代码的增加,引发了一系列问题,例如代码的耦合性代码的加载顺序代码的命名冲突问题等这些问题导致了模块化的开发已经成为不可避免的趋势。
模块化开发的目标
模块化开发的目标是为了解决代码开发中遇到的各种问题但是前端开发的关注点不是完全基于文件类型分离的前端工程化的最终目的都是为了能够更好地维护代码因此这些目标必将成为模块化开发的优点也是模块开发演化过程中不断追求的结果
避免变量污染、命名冲突等提高代码复用率提高代码维护性方便依赖关系的管理
模块化开发的演化
主要是在模块化实践过程中,为实现模块化而出现的不同方案
文件划分方案
随着代码的不断增多,即使将每个功能和相关的一些状态数据单独存放到不同的文件当中,让每个文件像是是一个独立的模块,但是在使用的时候,通过 script 标签引入文件,导致所有模块都暴露在全局范围内工作,引起一系列问题
污染全局作用域命名冲突问题无法管理模块之间的依赖关系
对象封装方案
将变量或者函数等模块成员封装在对象中,当要使用的时候就调用这个对象的属性,但是仍旧存在问题
没有私有属性,模块成员可以在外部被访问或修改无法管理模块之间的依赖关系
立即执行函数
采用该方式为模块提供私有成员,因为在 javascript 中,只有函数内部的子函数才能读取局部变量,因此函数提供的私有作用域可确保函数中私有成员的安全,私有成员只能通过闭包的形式访问 在 javascript 中,只有函数内部的子函数才能读取局部变量 模块化开发规范
随着对模块化开发需求的增加,模块化开发形成了一些列的标准,并且这些标准也是在不断的发展的,目前的模块化开发标准包括AMD 规范,CMD 规范,CommonJS 规范,ES6 规范。
模块化开发肯定包括定义模块与使用模块相同文件中可以直接使用定义的模块;在不同的文件中使用该模块是需要先导入该模块的而被导入的模块必须是允许被导入的也即该模块是被导出的。
所以下面将分别介绍不同规范是如何定义模块导出与导入模块的 CommonJS 规范
根据 CommonJS 规范实现的库或 API 是同步加载模块,CommonJS 是服务器端规范。
在服务器端,模块文件都存在本地磁盘,读取速度快,因此同步加载在服务器端不会有影响
在浏览器端,由于网速等问题的影响,可能文件的加载需要较长时间,所以 AMD、CMD 异步加载方案更合理
特点
所有的代码都在独立的模块作用域中,不会污染全局作用域模块加载的顺序,按照其在代码中的引入顺序加载。模块可以多次加载,但是只会在第一次加载时运行一次,运行结果被缓存。之后加载从缓存中直接读取,清空缓存重新运行。module.exports 属性输出是值拷贝。一旦操作完成,模块内发生的任何变化不会影响到已经输出的值。
定义模块
通过 module 对象来定义一个模块
module 属性说明
module {id: , //模块的识别符通常是带有绝对路径的模块文件名。filename: , //模块的文件名带有绝对路径。loaded: , //返回一个布尔值表示模块是否已经完成加载。parent: , //返回一个对象表示调用该模块的模块。children: , //返回一个数组表示该模块要用到的其他模块。exports: //表示模块对外输出的值。
};全局对象 global
CommonJS 规范的全局对象是 global(类似于浏览器环境下的 window 对象),因此 global 对象上的变量或方法对其它文件是可获取的。
例如,
global.a 1;变量 a 被添加到了 global 上,在任意文件内都可以访问,该方式在模块化开发中是极不推荐的
导出模块(变量或方法)
模块导出的可以是变量或方法CommonJS 通过 module.exports 暴露变量与方法
let a 1;function getA() {//moduleAreturn a;
}module.exports.getA getA;
//等价
exports.getA getA;
//因为 exports module.exports;exports ‘Hello World’||{}, exports 直接赋值,该操作切断了被赋值的 exports 与 module.exports 的联系,因此不可直接赋值!!! module.exports 可直接赋值 const a 1;module.exports function () {return a;
};导入模块
通过 require 来引入其它模块暴露的值
let a require(./moduleA); //2
let b require(./moduleA); //2 使用被缓存的结果通过 require,同一个模块只有第一个加载时运行一次,然后运行结果被缓存其它的加载都使用被缓存的结果 AMD 规范
AMD,Async Module Define 规范就是为了解决 CommonJS 实时加载,同步执行所带来的的弊端而出现的,它兼容 CommonJS 规范,因此符合 CommonJS 规范的模块可以被 AMD 规范正确导入。
至于什么是同步与异步,不了解的可以自行查询
根据 AMD 规范实现的库或 API 是异步加载模块,异步加载即非阻塞加载,更加适合浏览器端
特点
模块的加载是异步的
定义模块
通过 define 对象来定义一个模块
define(id, [dependencies], factory);id:可选,即模块名,该参数省略情况下自动使用文件名dependencies:可选,依赖列表,数组每项是依赖的模块路径或模块名factory:必需,工厂方法,初始化模块需要执行的函数或对象。如果为函数,它只被执行一次。如果是对象,此对象会作为模块的输出值
导出模块(变量或方法)
通过 define 来导出模块模块的导出内容可以是变量也可以是方法;
独立模块不需要依赖任何其他模块;
导出对象
对象作为参数定义模块直接作为模块导出
//moduleA.js
define({printName: printName,getList: function () {return printName;}
});函数作为参数定义模块该函数必须有返回值;
函数返回对象:
//moduleA.js
define(function () {return {printName: printName,getList: function () {return printName;}};
});导出函数
函数作为参数定义模块该函数必须有返回值;
函数返回函数:
define(function () {//moduleA.jsreturn function () {return printName;};
});导出符合 CommonJs 规范的接口
非独立模块需要依赖任何其他模块
define([moduleName], function (require, exports, module, moduleName) {const a require(a);const b require(b);exports.action function () {};//exports{}直接赋值方式会失去与module.export的联系因此该方式不可取module.exports {name: 10,action: function () {console.log(输出内容);}};
});依赖 require, exports, module 的引入可以被省略
exports 直接赋值,该操作会切断被赋值的 exports 与 module.exports 的联系,因此不可直接赋值!!! module.exports 可直接赋值
导入模块
require 导入方式
//使用模块 异步加载
require([moduleA], function (moduleA) {moduleA.printName();
});define 导入方式
//引入依赖模块moduleA
//moduleA的输出值作为函数的参数
define([moduleA, jquery, Base64], function (moduleA, $, Base64) {return {printName};
});CMD 规范
CMD,Common Module Definition 规范与 AMD 规范非常相似,但是相对来说更严格。CMD 规范定义的模块在 AMD 中也是合法的,它的基本语法也有点类似于 CommonJS,因此CMD兼容AMD与CommonJs
AMD 规范所推崇的是依赖前置,即提前声明好需要依赖的模块,CMD 推崇就近依赖,一般不在 define 中写依赖,而是使用 require 引入依赖
定义模块
define(id, [dependencies], factory);id:可选,即模块名,该参数省略情况下自动使用文件名,CMD 推崇一个文件一个模块dependencies:依赖列表,CMD 推崇就近依赖,一般不在 define 中写依赖factory:回调函数 function(require,exports,module){} require 用来获取其他模块提供的接口exports 用来向外提供模块接口module 存储模块相关联的一些属性和方法
导出模块(变量或方法)
exports 是指向 module.exports 的变量。
module 即模块对象,同 CommonJS。
define(function (require, exports, module) {var $ require(jquery.js);$(div).addClass(active);// module.id module2;//exports.name$ 对外导出接口
});注意,与 AMD 一样,尽管 CMD 也使用 require 导入模块,但它也是异步加载的。CMD 规范也会提前扫描回调函数,预下载依赖的模块,然后才会执行回调函数,以防止代码在执行过程中被阻塞 导入模块
require 导入其它模块
UMD 规范
UMD,Universal Module Define,通用模块定义。它是一种适配 CommonJS、AMD 和 CMD 的模块定义标准
UMD 规范在主函数中会通过检查 define、exports 这两个变量,来判断当前使用的模块化规范
!(function (root, name, factory) {// 是否在AMD规范下if (typeof define function define.amd) {define(name, factory);// 是否在CommonJS或CMD规范下} else if (typeof exports object) {module.exports factory();// 没有使用模块化} else {root[name] factory();}
})(this, run, function () {function run() {return this is run function!;}return run;
});且 UMD 规范无论是在 CommonJS、AMD 还是 CMD 规范下运行该代码,都可以输出符合所用模块规范的模块,用以判断当前的规范,从而实现了代码的兼容性 UMD 没有对 ES6 的模块规范进行处理,因为 ES6 模块规范是语言层面的规范,未来会得到浏览器的原生支持。一旦 ES6 规范得到原生支持,UMD 规范以及另外三个规范都可能被取代 ES6 规范
传统的模块模式基于闭包,ES6 的模块规范是 JavaScript 语言层面的模块规范,目标是取代上述所有规范,成为前端领域的标准模块规范。未来的浏览器将原生支持该模块规范,也是未来推荐的开发语言
特点
输出值引用
ES 模块化规范中导出的值是引用,所以不论何时修改模块中的变量,在外部都会有体现。
静态化
ES6 模块规范的一大特点是静态化,编译的时候就确定模块之间的关系,每个模块的输入和输出变量也是确定的。静态化是为了实现 Tree Shaking 提升运行性能。
Tree Shaking:减少 web 项目中 js 的无用代码,以达到减少用户打开页面的等待的时间,缩短渲染的时间,提升响应的用户体验。
DCE:减少无用代码的操作叫做 DCE(Dead Code Elemination),DCE 意味着更小的体积,缩减 bundle size,从而获得更好的用户体验
ES6 模块化导出有 export 和 export default,建议用 export,因为 export default 导出整体对象,不利于 Tree Shaking,export default 导出的结果可以随意命名,不利于代码的管理
局限性
import 依赖必须在文件的顶部export 导出变量类型严格限制import 依赖不能动态确定
导出与导入模块
通过 export 暴露变量与方法
通过 import 来引入其它模块暴露的值
const a 1;
const fn () {};
export { a, fn };import { a, fn } from ./index; //内是js文件路径规范的实践案例
主流的开发语言使用的方案
规范库说明CommonJS 规范Nodejs、BrowerifyCommonJS 规范的实践AMD 规范RequireJSAMD 规范的最好实现CMD 规范SeaJSCMD 规范的实践
目前库陆续开发出了ES6版本但是仍旧存在很多使用以前规范的库版本
规范的混合使用
符合 ES6 规范的模块简称 ESM这种是未来的发展趋势所以很多库的开发都在朝着 ES6 的方向靠拢如果开发新的项目也推荐使用ES6规范
Node.js 则是使用的 CommonJS 模块简称 CJS。
这两种模块不兼容。
那么在 Nodejs 如何加载或运行使用 ESM 呢
看到网上有提供说是在 package 文件夹中添加 type:module的值因为本人本地测试 node api 服务与 vue web 项目是在一个目录下而如果设置 vue 中存在上述 type:module属性会导致 vue 项目启动失败
发现可以将文件后缀名改为.mjs文件将使用ESM规范解析js 文件 node 会以 type 的设置值解读。
具体的混合使用的方法可以查看ES6与CJS的混合开发