JS中的几种模块化规范(CommonJS、AMD、CMD、ES6 Module) 您所在的位置:网站首页 属于动作模块中的是什么模块 JS中的几种模块化规范(CommonJS、AMD、CMD、ES6 Module)

JS中的几种模块化规范(CommonJS、AMD、CMD、ES6 Module)

2023-12-22 21:50| 来源: 网络整理| 查看: 265

文章目录 引入问题:为什么要进行模块化?JS模块化大致发展过程模块化规范的种类模块化规范的发展趋势 1.CommonJS规范1.1说明:1.2 使用1.2.1模块定义与使用 2.AMD规范2.1说明2.2 标准内容2.3 举例使用: 3.CMD规范3.1 说明3.2 使用 4. ES6中的Module模块4.1 标准内容4.2 模块的定义4.4 模块的引入 5.CommonJS、AMD、CMD、ES6 Module的区别5.1 AMD与CMD区别5.2 ES6模块与CommonJS模块加载的区别

引入问题:为什么要进行模块化?

1)模块化就是将系统分离成独立功能的模块,这样我们需要什么功能,就加载什么功能 2)模块化的好处:

避免命名空间的冲突(减少命名空间的污染)更好的分离,实现按需加载提高可代码的复用性提高了代码的维护性 JS模块化大致发展过程

CommonJS(服务端)=》AMD(浏览器)=》CMD=》ES6 Module模块化

模块化规范的种类

在这里插入图片描述

模块化规范的发展趋势

在这里插入图片描述 终极:现代工具webpack

webpack自己实现了一套模块机制,无论是CommonJs模块的require语法还是ES6模块的import语法,都能够被解析并转换成指定的环境的可运行代码。(随着webpack打包工具的流行,ES6语法广泛手中,后来开发者对于AMD CMD的感知越来越少) 1.CommonJS规范 1.1说明: 每个文件都可以作为一个模块(这里的文件指的是js文件)在服务器端:模块的加载是运行时同步加载的在浏览去端:模块需要提前编译打包处理,不然浏览器不能识别require语法 1.2 使用

主要分为定义模块和引入模块两个步骤 定义模块语法:

1)module.exports=value 2)export.xxx=value

引入模块语法

var xxx=require(url)

模块标识: 模块标识就是require()函数的参数,规范是这样的:

必须时字符串可以时以./ …/开头的相对路径可以时绝对路径可以省略后缀名

其中,当引入的模块为自定义的模块时,那么url则是该模块所在的路径 当引入的模块是第三方模块时,则url为其具体包名 标准内容:

模块通过exports来向外暴露API,exports只能是一个对象,暴露的API需作为此对象的属性定义全局函数require,通过传入模块标识来引入其他模块,执行的结果即为别的模块暴露出来的API如果被reqiure函数引入的模块中也包含依赖,那么以此加载这些依赖

特点

模块加载是一项阻塞操作,也就是同步加载 1.2.1模块定义与使用

举例1:

方式①(module1.js) module.exports={ var msg="i am msg" foo(){ console.log("i am ①"); } } 方式②(module2.js) //这里暴露处的是一个函数 //当引入的方式为 //var module2=require('./module2')时, //便可以直接使用 module2()调用即可 //当引入的方式为 //var fun=require('./module2')时, //便可以直接使用 fun()调用即可 module.exports=function(){ return ... } 如: module.exports=function(){ console.log('i am ② '); } 方式③(module3.js) module.exports.xxx=function(){ .... } 如: module.exports.foo=function(){ console.log('i am ③'); } module.exports.bar=function(){ console.log('i am bar from ③'); } 引入(一般在主模块中如main.js) /*说明: ./module1是该模块所在的路径 */ const module1=require('./module1'); const module2=require('./module2'); const module3=require('./module3'); //引入第三方模块 var http = require('http'); //使用 module1.foo(); module2(); module3.foo(); module3.bar();

举例2:

// file greeting.js 定义一个模块 var helloInLang = { en: 'Hello world!', es: '¡Hola mundo!', ru: 'Привет мир!' }; var sayHello = function (lang) { return helloInLang[lang]; } // 对外输出 module.exports.sayHello = sayHello; // file hello.js 引入一个模块 var sayHello = require('./lib/greeting').sayHello; var phrase = sayHello('en'); console.log(phrase);

举例3:

// a.js module.exports = { moduleFunc: function() { return true; }; } // 或 exports.moduleFunc = function() { return true; }; // 在 b.js 中引用 var moduleA = require('a.js'); // 或 var moduleFunc = require('a.js').moduleFunc; console.log(moduleA.moduleFunc()); console.log(moduleFunc()) 2.AMD规范 2.1说明 CommonJS规范出现后,在Node开发中产生了非常好的效果,开发者希望借鉴这个经验来解决浏览器JS的模块化但是大部分人认为浏览器和服务器的环境差别太大,毕竟浏览器JS时通过网络动态以此加载的,而服务器的JS是保存在本地磁盘中。因此浏览器需要实现异步加载,模块在定义的时候就必须先知名它所需要依赖的模块,然后把本模块的代码写在回调函数中执行,最终衍生出了AMD规范AMD的主要思想时异步模块,主逻辑在函数回调中执行 2.2 标准内容

在这里插入图片描述

1.定义没有依赖的模块 module1.js

define(function(require,exports.module){ return 模块 }

2.定义具有依赖的模块 module2.js

define(['module1','module2'],function(m1,m2){ return 模块 }

3.引入模块 main.js

require(['module1','module2'],function(m1,m2){ })

在这里插入图片描述

2.3 举例使用: //module.js define(function (require, exports, module) { console.log('module.js') exports.name = "jack" //暴露 }) //module2.js define(function (require, exoprts, module) { console.log('module2.js'); exports.desc = "hello world" }) //main.js require(['module1', 'module2'], function (m1, m2) { console.log('main.js'); console.log(m1.name + ',' + m2.desc); //引入module1和module2之后,直接使用其暴露的属性 }) // 执行顺序: // module1.js // module2.js // main.js

人无完人,AMD/RequireJS 也存在饱受诟病的缺点。按照 AMD 的规范,在定义模块的时候需要把所有依赖模块都罗列一遍(前置依赖),而且在使用时还需要在 factory 中作为形参传进去。 CMD/RequireJS模块化的顺序是这样的:模块化加载=》全部模块预执行=》主逻辑中调用模块 所以是依赖加载完成后会先预先将模块执行一遍,这种方式会使得程序效率低;

define(['a', 'b', 'c', 'd', 'e', 'f', 'g'], function(a, b, c, d, e, f, g){ ..... }); 3.CMD规范 3.1 说明 AMD/RequireJS的JS模块实现有很多不优雅的地方,主要原因不能以一种更好的管理模块的依赖加载和执行; 那么就出现了SeaJS,SeaJs遵循的是CMD规范,CMD规范在AMD的基础上改进的一种规范,解决了AMD对依赖模块的执行时机的问题;SeaJS模块化的顺序是:模块化预加载=》主逻辑调用模块时才执行模块中的代码SeaJS的用法和AMD基本相同,并且融合了 CommonJS的写法: 3.2 使用

(对于模块的引入,具有同步和异步两中方式)

//module1.js define(function (require, exports, module) { console.log('module1.js') // module.exports = value; // exports.xxx = value exports.name="i am module1" }) //main.js define(function (require, exports, module) { //引入依赖模块(同步) var module2 = require('./module2'); console.log(module2.name) //引入依赖模块(异步1) require.async('./module3', function (m3) { //这里m3对应module3 }) //引入依赖模块(异步2) var module4=require.async('./module4'); console.log(module4.name) })

总结: SeaJS的出现,是CommonJS在浏览器的践行者,并吸收了RequireJS的优点

4. ES6中的Module模块 4.1 标准内容 模块功能主要由两个命令构成:export和importexport用于暴露接口,import用于引入模块 4.2 模块的定义

有如下3中方式

方式1-----分别暴露 //module1.js export var m=1 export var arr=[1,2,4] export function fun(){ console.log('i am a fun') } //引入与使用(结构引入): import {m,arr,fun} from './module1' console.log(m); fun() 方式2—统一暴露 //module2.js var m=1; var arr=[1,2,4] function fun() { console.log('i am a fun') } export {m,arr,fun} //引入与使用(结构引入): import {m,arr,fun} from '/module2.js' console.log(arr); fun() 方式3----默认暴露 //这里默认暴露对象 //module3.js //export default其实是导出一个叫做default的变量,所以其后面不能跟变量声明语句。 //错误 export default var a = 1; //正确 export default { m:1, fun(){ console.log('i am a fun from export defalut') } } //person.js export default function getName(){ ... } //引入和使用(module为自定义任意名字) import module from './module3.js' module.fun()

在这里插入图片描述

4.4 模块的引入 // 解构引入 import { firstName, lastName, year } from 'a-module'; // 为输入的变量重新命名 import { lastName as surname } from 'a-module'; // 引出模块对象(引入所有) import * as ModuleA from 'a-module'; 在使用 ES Module 值得注意的是:import 和 export 命令只能在模块的顶层,在代码块中将会报错,这是因为 ES Module 需要在编译时期进行模块静态优化,import 和 export 命令会被 JavaScript 引擎静态分析,先于模块内的其他语句执行,这种设计有利于编译器提高效率,但也导致无法在运行时加载模块(动态加载)。

对于这个缺点,TC39 有了一个新的提案 – Dynamic Import,提案的内容是建议引入 import()方法,实现模块动态加载。

// specifier: 指定所要加载的模块的位置 import(specifier) import()方法返回一个Promise对象 import('b-module') .then(module => { module.helloWorld(); }) .catch(err => { console.log(err.message); });

PS:

import()函数可以用在任何地方,不仅仅是模块,非模块的脚本也可以使用。 它是运行时执行,也就是说,什么时候运行到这句话,就会加载到指定的模块。另外,import()函数所加载的模块没有静态链接关系,这点也是与import语法不同注意的时ES6 的Module语法有些浏览器是不支持的,因此需要Babel先进性转码,将import和export命令转成ES5语法才能被浏览器解析。

这里举例之前做过的一个小项目,就是使用了ES6语法中的module模块化思想结合axios库,将需要向服务端发送请求的操作封装到一个模块中,然后对于不同的请求数据操作,直接导入该模块调用即可 1)封装ajax请求函数(使用的时默认暴露)

//首先向外暴露一个函数 //默认为GET请求 import axios from 'axios' export default function ajax(url,data={},type='GET') { return new Promise(function (resolve, reject) { //这里的return主要时结果的回调函数,即成功了则回调sesolve函数,失败了则回调reject函数 //即异步返回的数据是response.data let promise if (type === 'GET') { //这里的目的主要是想将参数拼接成url?username=xx&password=XX let dataString = ''; Object.keys(data).forEach(key => { dataString += key + '=' + data[key] + '&' }) if (dataString != null) { dataString = dataString.substring(0, dataString.length - 1); url = url + '?' + dataString; } //使用axios发送get请求 promise = axios.get(url) ///console.log(promise) } else { //使用axios发送post请求 //post请求不用 promise = axios.post(url, data) } //response是axios发送请求后得到的promise对象中的response, //最后因为响应体的数据杂多,这里只取response中的data promise.then(function (response) { //成功了则调用resolve resolve(response.data); }).catch(function (error) { //失败了则调用reject reject(error) }) }) }

2)封装接口请求函数----使用的时分别暴露 (这个接口函数需要导入上面封装的ajax请求函数,调用,发送请求)

import ajax from './ajax' const BASE_URL='/api' //注册接口 //注册时即向后台传送username与password---->一个user对象---参数 //需要ajax为桥梁发送请求,ajax返回的结果就是现所需要的结果 //再ajax中需要指定参数 //url---只需要指定后面部分 //2.获取食品分类列表 export const reqFootCategory=()=>ajax(BASE_URL+'/index_category') //3.商店数组对象 export const reqShops=(longitude,latitude)=>ajax(BASE_URL+'/shops',{longitude,latitude}) //1.根据经纬度获取地址详情 export const reqAddress=(geohash)=>ajax(`${BASE_URL}/position/${geohash}`) //4.根据经纬度和关键字搜索商铺列表 export const reqSearchShop = (geohash, keyword) => ajax(BASE_URL+'/search_shops', {geohash, keyword}) //5.获取一次性验证码 export const reqGetcaptcha=()=>ajax(BASE_URL+'/captcha') //6.用户名密码登录 export const reqPwdLogin=({name,pwd,captcha})=>ajax(BASE_URL+'/login_pwd',{name,pwd,captcha},'POST') //7.发送短信验证码 export const reqSendCode=(phone)=>ajax(BASE_URL+'/sendcode',{phone}) //8.手机号验证码登录 export const reqSmsLogin=(phone,code)=>ajax(BASE_URL+'/login_sms',{phone,code},'POST') //18813216310 //9.根据会话获取用户信息 export const reqUserInfo=()=>ajax(BASE_URL+'/userinfo') //10.用户登出 export const reqLogout=()=>ajax(BASE_URL+'logout')

3)当其他模块需要调用发送请求时,直接使用import结构导入封装的接口函数即可

5.CommonJS、AMD、CMD、ES6 Module的区别 5.1 AMD与CMD区别

1)模块定义时对依赖的处理不同

AMD推崇迁至以来,在定义模块时就要声明其依赖的模块;而CMD推从就近依赖,只有在用到某个模块时再使用require导入; AMD:

difine(['module1','module2'],function(m1,m2){ })

CMD:

define(function(require,exports,module){ const module1=require('./module1'); })

2)对依赖模块的处理机制不同

首先AMD和CMD对模块的加载方式都是异步的不过区别在于AMD当加载了依赖模块之后立即执行依赖模块,依赖模块的执行顺序和我们书写的顺序不一定一致;而CMD加载完依赖模块之后,并不会立即执行,等所有的依赖模块都加载好之后,进入回到函数逻辑,遇到require语句的时候,才执行对应的模块,这样模块的执行顺序就和我们书写的时候一致了 5.2 ES6模块与CommonJS模块加载的区别 CommonJS时运行时加载,因为ComminJS加载是先加载整个模块,生成一个对象(这个对象包含了path这个模块的所有API),然后再从这个对象上面读取方法-----运行时加载ES6是编译时加载,ES6模块不是对象,它的对外接口只是一种静态定义,在代码静态定义阶段就会生成-----编译时加载 //ES6模块 import { basename, dirname, parse } from 'path'; //CommonJS模块 let { basename, dirname, parse } = require('path');

以上这种写法与CommonJS的模块加载有什么不同?

当require path 时,CommonJS会将path模块运行一遍,并返回一个对象,这个对象包含path模块的所有API。 参考文章: https://juejin.cn/post/6844903629447495687


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

      专题文章
        CopyRight 2018-2019 实验室设备网 版权所有