前端系列-2 模块化CommonJs+AMD+CMD+ES6

模块化

js中, 一个文件就是一个模块,模块化可以提高代码利用率而减少重复代码,将相同的逻辑维护在一个模块中,分而治之,提高代码的可维护性;另外,模块化的出现可以在工程中引入第三方模块,极大提高开发效率。

在模块化出现前,只能通过<script>标签在html中导入,并根据依赖控制导入顺序。CommonJS出现后,模块化技术不断发展,目前主流的js模块化规范有CommonJS, AMD/CMD以及ES6等模块系统,按照模块化的发展可排序为: CommonJS -> AMD/CMD -> ES6.

CommonJS用于服务端,AMD/CMD用于浏览器端,ES6是服务端和浏览器端通用的模块解决方案。

1.CommonJS规范

CommonJS规范是NodeJs默认的模块化的规范,适用于服务器端,不适用于浏览器端(理由在后面介绍).

在CommonJS中, 每个js文件就是一个模块,有自己的作用域:文件内定义的变量、函数、类都是私有的,其他模块不可见;可以通过module.exports和require组合的方式实现跨模块访问。

1.1 module对象

js 复制代码
module.exports = {
    // 可以导出内部的变量、方法等
}

module变量是一个对象,表示当前模块;有一个exports属性,表示模块对外输出的值。

module对象除了这个exports属性外,还有以下属性:

1\] id 模块的标识符,带有绝对路径的模块文件名; \[2\] filename: 模块的文件名,带有绝对路径; \[3\] parent: 返回一个对象,调用该模块的对象; \[4\] loaded:boolean,表示该模块是否加载完成; \[5\] children:数组,该模块内用到的其他模块; ### 1.2 全局函数require 一般而言,导出是为了导入,不然导出就没有了意义。CommonJS规范通过require方法进行模块的导入. ```js // 引用自定义的模块时,参数包含路径,可省略.js var math = require('./math'); // 引用核心模块时,不需要带路径 var http = require('http'); ``` require是加载模块,并给导入的模块定义名称。 require基于缓存加载模块,所有加载的模块都会缓存在require.cache中,之后再次加载会直接从缓存中读取(每个模块只会被加载一次)。require加载返回的对象为module.exports属性对象。如果被加载的模块没有给exports属性对象赋值,则加载后返回的对象为空对象{}。 ### 1.3 案例 ```js // entry.js var myModule = require("./module.js") console.log(myModule) --- // module.js var num = 1 function addNum(val){ return num + val } module.exports = { num, addNum } ``` 运行结果如下: {num: 1, addNum: ƒ} 表示myModule对象包含一个属性值num=1,和一个addNum函数。 ```js // entry.js var myModule = require("./module.js") console.log(myModule) --- // module.js var num = 1 function addNum(val){ return num + val } ``` 运行结果如下: {} 表示myModule对象为空对象。 ### 1.4 特点 CommonJs同步加载模块,即后面的模块需要等待前面的模块加载完成。对于服务器端,模块文件存在与本地文件系统,不存在问题。浏览器需要从服务器下载模块并加载,由于渲染是顺序进行的,当script标签阻塞时浏览器不会继续向下渲染DOM,因此页面会失去响应。因此,浏览器端只能采用异步方式加载模块。 ## 2.AMD AMD模块规范中,模块的加载是异步的,专用于浏览器端;使用时需要依赖第三方库RequireJs. ### 2.1 引入主模块 在html中通过\标签引入require.js库文件时,通过data-main属性指定主模块的路径: ```html ``` 当require.js加载并执行后,会根据data-main属性指定的URL加载并运行主模块(此时为index.js). ### 2.2 定义模块 AMD使用define定义模块的信息、模块的依赖、函数。 第一个参数为模块的名称(不常使用,一般用后面两个参数),第二个参数为数组类型,依赖的模块,第三个参数为函数(用于返回定义的模块信息)。 ```js // 定义没有依赖的模块 define(functioin(){   return 模块对象 }) // 定义有依赖的模块 define(['module1','module2'], function(m1, m2){ // m1对应导入的module1对象, m2对应导入的module2对象   return 模块对象 }) ``` ### 2.3 引入模块 ```js // 主模块中用过requirejs.config指定模块的路径 requirejs.config({ baseUrl: 'js/', paths: { module1: './modules/module1', module2: './modules/module2' } }) requirejs(['module1'], function (m1) { // 操作m1模块对象 }) ``` 通过requirejs函数引入模块,第一个参数为依赖的模块组数,第二个参数为回调函数。 另外,在主模块中通过requirejs.config指定模块的路径,baseUrl指定一个路径,后续配置路径时以此为相对路径(可选属性;paths中配置各个模块与模块文件路径的映射关系。 ### 2.4 案例 案例使用的文件路径为: ```shell -lib -require.js #依赖库 -js -module1.js -module2.js -index.html -index.js ``` **index.js文件** ```javascript (function (){ requirejs.config({ paths: { baseUrl: './js/', module1: './module1', module2: './module2' } }) requirejs(['module1'], function (m1) { m1.print1("hello world") }) })() ``` **module1.js文件** ```javascript define(['module2'], function (m2) { function print1 (param) { m2.print2(" -m1- "+param); } return { print1 } }) ``` **module2.js文件** ```javascript define(function () { function print2 (param) { console.log(" -m2- "+param) } return { print2 } }) ``` **index.html文件** ```html ``` 浏览器console运行结果如下: -m2- -m1- hello world **注意**:本地测试------直接通过浏览器访问html文件时,Chrome浏览器会因为跨域报错,可以进行如下参数设置: ```shell --allow-file-access-from-files --user-data-dir="空文件夹" --disable-web-security ``` ## 3.CMD CMD与AMD类似使用异步加载模块的机制,作为浏览器端的模块规范,区别在于CMD依赖提前下载,而CMD延迟下载,CMD的实现依赖于SeaJS。 ### 3.1 引入主模块 ```html ``` ### 3.2 定义和加载模块 使用define定义模块,如下所示: ```js define(function(require, exports, module) { //... }) ``` 其中:\[1\] require函数用于加载其他模块; \[2\] exports对象用于向外提供接口,导出对象或者方法;\[3\]存储当前模块的属性和方法。 require用法同AMD: ```js define(function(require, exports, module) { const m1 = require("./module1"); m1.print1("hello world"); }) ``` SeaJs也有use功能(在3.1章节中使用到),在需要先引入锁依赖的模块: ```js seajs.use(['module1', 'module2'], function(m1, m2){ //... }); ``` ### 3.3 案例 案例使用的文件路径为: ```shell -lib -sea.js #依赖库 -js -module1.js -module2.js -index.html -index.js ``` **index.js文件** ```javascript define(function(require, exports, module) { const m1 = require("./js/module1"); m1.print1("hello world"); }) ``` **module1.js文件** ```javascript define(function(require, exports, module) { const m2 = require("./module2"); exports.print1 = function(param) { m2.print2(" -m1- "+param) } }) ``` **module2.js文件** ```javascript define(function(require, exports, module) { exports.print2 = function(param) { console.log(" -m2- "+param) } }) ``` **index.html文件** ```html ``` 浏览器console运行结果如下: -m2- -m1- hello world ## 4.ES6 ES6使用export和import关键词进行模块的导入和导出。 ### 4.1 export和import 可以使用export指定本模块需要导出的对象(变量、方法、类等信息);在其他模块中通过import导入。 **export:** export有两种使用方式,推荐 export { }形式。 ```javascript // module1.js export var test1 = "1"; export var fun1 = function() { console.log("hello world") } ``` 等价于: ```javascript // module1.js var test1 = "1"; var fun1 = function(){ console.log("hello world"); } export { test1, fun1 } ``` 另外,导出时也可以执行别名, 如下所示: ```javascript // ... export { test1, fun1 as fun1, fun1 as fun2 } ``` **import:** 使用`import {...} from 模块` 形式或使用`import * as 别名 from 模块`形式导入模块。 ```javascript // index.js import {test1, fun1} from "./module1"; console.log(test1); //1 fun1(); // hello world ``` 或者: ```javascript // index.js import * as m1 from "./module1"; console.log(m1.test1); //1 m1.fun1(); // hello world ``` 导入时也可以指定别名: ```javascript import {test1, fun1 ad fun} from "./module1"; ``` 注意:使用 import{}和import \* 不包括export default导出的对象。 ### 4.2 export default和import 一个模块可以使用export default导出一个对象(变量、函数、类),import导入deault时不需要使用大括号。 在module1.js中使用export default导出一个默认输出: ```javascript // module1.js export default function test1(){ console.log("hello world") } ``` 在index.js中使用import导入: ```javascript // index.js import test from "module1"; test(); ``` 导入默认导出项时,需要指定别名,此时为test.

相关推荐
想要飞翔的pig几秒前
uniapp+vue3页面滚动加载数据
前端·vue.js·uni-app
HarryHY几秒前
git提交库常用词
前端
SoraLuna几秒前
「Mac畅玩AIGC与多模态41」开发篇36 - 用 ArkTS 构建聚合搜索前端页面
前端·macos·aigc
Wannaer3 分钟前
从 Vue3 回望 Vue2:性能优化内建化——从黑盒优化到可控编译
javascript·vue.js·性能优化
霸王蟹8 分钟前
React Fiber 架构深度解析:时间切片与性能优化的核心引擎
前端·笔记·react.js·性能优化·架构·前端框架
benben0448 分钟前
Unity3D仿星露谷物语开发44之收集农作物
前端·游戏·unity·游戏引擎
会功夫的李白12 分钟前
uniapp自动构建pages.json的vite插件
前端·uni-app·vite
一口一个橘子30 分钟前
[ctfshow web入门] web77
前端·web安全·网络安全
yyywoaini~1 小时前
wordcount程序
前端·javascript·ajax
Yvonne爱编码1 小时前
CSS- 4.2 相对定位(position: relative)
前端·css·状态模式·html5·hbuilder