JS模块化CommonJS、ES6模块化 、AMD、CMD知识总结
1. 什么是模块化?
将一个复杂的程序依据一定的规则封装成几个块(文件),并最终组合在一起。块(文件)的内部数据/实现是私有的,只是向外部暴露一些接口(方法)与外部其他模块通信。
2.模块的进化史
1.最早的代码风格,全局被污染,容易造成命名冲突。
function foo(){//...
}function bar(){//...
}
2.简单封装:namespace模式。虽然减少了全局变量的数目,但本质是对象,不安全。
var myApp={foo:function(){//...},bar:function(){//...}
}myApp.foo()
3.匿名闭包:IIFE模式,立即执行函数是局部作用域,不污染全局作用域。
var module=(function(){var _private="安全作用域";var foo=function(){console.log(_private)}return {foo}
})()module.foo();
module._private;//undefined
4.在立即执行函数中引入依赖:这也是现代模块实现的基石。
var module = (function ($) {var _$body = $('body')var foo = function () {console.log(_$body)}return {foo,}
})(jQuery)module.foo()
模块化的好处:
- 避免命名冲突,减少命名空间污染
- 更好的分离,按需加载
- 更高的复用性
- 更容易维护
缺点:
- 由于一个文件被分割成多个文件,造成页面加载时需要更多的
请求数
,而往往我们需要减少发起请求的数量。 - 依赖模糊,拆分的文件往往需要按照一定的顺序引入,这容易造成犯错。
- 难以维护
3.CommonJS
说明:
- 服务器端:模块的加载是运行时
同步加载
的 - 浏览器端:模块需要
提前编译打包
处理
基本语法
暴露模块:
- module.exports=value
- exports.xxx=value
引入模块:
- require(xxx)
注意:
- 当引入的模块是第三方模块,则xxx是模块名,比如(
const jQuery = require('jQuery')
) - 当引入的模块是自定义模块,则xxx是自定义模块的路径,比如(
let myModule = require('./myModule.js')
)
CommonJS常用于NodeJs中。
示例:
创建四个文件:module1.js、 module2.js、module3.js、app.js文件
module1.js
function foo() {console.log('module1 foo()')
}module.exports = {foo,
}
module2.js
module.exports = function () {console.log('module2()')
}
module3.js
exports.foo = function () {console.log('module3 foo()')
}exports.bar = function () {console.log('module3 bar()')
}
app.js用于引入模块
const module1 = require('./module1') //const { foo } = require('./module1');//使用了es6语法,进行对象解构
const module2 = require('./module2')
const module3 = require('./module3') //const {foo,bar} = require('./module3')module1.foo()
module2()
module3.foo()
module3.bar()
面试:module.exports
与exports
的区别?(重点)
找出区别需要了解的知识点:
1.在js模块中,有一个全局对象module,该module对象有个exports属性,exports属性初始默认值是一个空对象。即初始化时:module.exports = { }。
2.那exports是什么呢?在底层源码中,只是简单的exports = module.exports = { } ,即exports和module.exports是指向同一个堆地址。
3. 那么exports.foo = function() { xxx },实际上就是往module.exports指向的空对象中添加foo属性。
4.如果在一个模块中只是使用exports去暴露模块,那么module.exports与exports是相同的,没有什么区别,即exports是module.exports的一个引用。
特别注意:
在const xxx = require('./module.js')
中的xxx
指的是什么呢?
无论模块中是使用exports.foo 暴露模块,还是使用module.exports去暴露模块,上面的xxx都只能是module.exports所指的内容,而不会是exports所指的内容。
思考问题
:
- 问题一:为什么前面说一个模块中只是使用exports去暴露模块,exports与module.exports才会相同,难道使用module.exports去暴露模块,exports就不等于module.exports了吗?
- 问题二:上面不是说exports与module.exports指向相同的地址吗?那么为什么还要关心require引入的xxx只能是module.exports。
答案:
在前面三个模块中,module1与module2两个模块,exports !== module.exports。而module3模块,exports === module.exports。
原因:(关键看module.exports有没有改变指针指向)
- module1中:明显module.exports 使用了
=
号指向了一个新的对象,对象有个foo属性,而exports还是指向原本的堆地址,那么exports !== module.exports
module.exports = {foo,}
- module2中:同理,module.exports指向了一个函数,函数也是一个对象,也改变了地址
- module3中:只是使用了exports.xxx,即module.exports没有改变指向,即exports === module.exports
4.ES6模块化
支撑ES6模块的两个主要新关键字是import和export。它们在语法上有许多微妙之处,所以我们来深入了解一下。
注意:
这里有一个很容易被忽略的重要细节:import和export都必须出现在使用它们的最顶层作用域。举例来说,不能把imort或export放在if条件中;它们必须出现在所有代码块和函数的外面。
4.1 导出export
export关键字或者是放在声明的前面
,或者是作为一个操作符与一个要导出的绑定列表
一起使用。
命名导出:
//export放在声明前面
export function foo() {//...
}
export var awesome = 42;//export 与要导出的绑定列表一起使用
var bar = [1,2,3];
export { bar }
下面是同样导出的另外一种表达形式:
function foo() {//...
}
var awesome = 42;
var bar = [1,2,3];
export { foo,awesome,bar } ;//导出绑定列表
在命名导出
时还可以“重命名”(即别名)
一个模块的成员:
function foo(){}
export { foo as bar};
注意
:
- 导入这个模块时,只有成员名称bar可以导入;foo还是隐藏在模块内部,即
import { foo }
是不可行的。 命名导出可以取别名,在导入时也可以取别名,在导入时必须与你导出时的名称一一对应。
考虑(注意)
:
var awesome = 42;
export { awesome };awesome = 100
导入这个模块时,不管是在awesome = 100 之前还是之后,一旦赋值发生,导入的绑定就会决议到100而不是42.
默认导出:
每个模块只能有一个default
,使用默认导出可以在导入模块时重命名。
关于默认导出有一个微妙的细节需要格外小心。比较下面两端代码:
function foo(){}
export default foo;
--------------------------------------
function foo(){}
export { foo as default } //可以把它当成命名导出时取别名
微妙的细节:
- 上面说了使用命名导出:导入这个模块时,不管是在awesome = 100 之前还是之后,一旦赋值发生,导入的绑定就会决议到100而不是42.
即新值覆盖旧值
. - 默认导出:如果之后你再给foo附一个不同的值,模块导入得到的仍然是原来导出的函数,而不是新的值。
即新值不会覆盖旧值
- 命名导出可以使用export var foo = … (或者 let 或者 const) ,但是默认导出只能export default function foo(){},而不能是export default var foo = … (或者 let 或者 const)
因为一个模块只能有一个default,所以你可能会忍不住把模块设计成默认导出一个对象
:
export default {foo,bar
}
但这是官方不建议采用的。如果你想导出多个你可以这样:
export default function foo(){}
export function bar(){}
export function baz(){}
即一个单独的默认导出,其它采用命名导出。
你也可以再次导出某个模块的导出,就像这样:
export {foo , bar} from "baz";
export {foo as FOO,bar as BAR } from "baz";
export * from "baz";
即先从“baz”模块导入,然后显示列出它的成员再从你的模块导出。但这些形式中,“baz”模块的成员不会导入到你的模块的局部作用域中,它们就像不留痕迹地穿过。
4.2 导入import
对于命名导出:
import {foo , bar , baz} from "foo";
或
import {foo as FOO,bar as BAR,baz as BAZ} from "foo"
注意:对于命名导出的模块,导入时需要用{ }
包裹。
对于默认导出:
export default function foo(){}
import foo from "foo" || import xxx from "foo" xxx可以是任意名,即别名
当你的模块同时使用命名导出和默认导出时:
export default function foo(){}export function bar(){}
export function baz(){}导入这个模块的默认导出和它的两个命名导出:
import FOOFN, { bar , baz as BAZ } from “foo”;FOOFN(); //FOOFN是默认导出foo的别名
bar();
BAZ();
命名空间导入:
export function bar() {}
export var x =42;
export function baz() {}
export default function foo() {}//全部导入可以用命名空间导入方式,那么就不需要一个个全导入
import fooFun , * as hello from "foo"
fooFun();
hello.default()//fooFun === hello.default
hello.x;
hello.bar();
hello.baz();即import * as hello from "foo"也引入了默认导出,hello.default()调用即可。
5.AMD(Asynchronous Module Definition 异步模块定义)
说明:
- 专门用于
浏览器端
,模块的加载是异步
的。 - require.js实现
基本语法
定义模块
- 定义没有依赖的模块:
define(function(){return 模块
})
- 定义有依赖的模块:
define(['module1','module2'],function(m1,m2){return 模块
})
引入模块
require(['module1','module2'],function(m1,m2){使用m1/m2模块
})
require.js使用教程
1.下载require.js, 并引入
- 官网: http://www.requirejs.cn/
- github : https://github.com/requirejs/requirejs
- 将require.js导入项目: js/libs/require.js
2.创建项目结构
|-js|-libs|-require.js|-modules|-alerter.js|-dataService.js|-main.js|-index.html
3.定义require.js的模块代码
- dataService.js
define(function () {let msg = 'atguigu.com'function getMsg() {return msg.toUpperCase()}return {getMsg}})
- alerter.js
define(['dataService', 'jquery'], function (dataService, $) {let name = 'Tom2'function showMsg() {$('body').css('background', 'gray')alert(dataService.getMsg() + ', ' + name)}return {showMsg}})
4.应用主(入口)js: main.js
(function () {//配置requirejs.config({//基本路径baseUrl: "js/",//模块标识名与模块路径映射paths: {"alerter": "modules/alerter","dataService": "modules/dataService",}})//引入使用模块requirejs( ['alerter'], function(alerter) {alerter.showMsg()})})()
5.index.html
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>AMD示例</title></head><body><script data-main="js/main.js" src="js/libs/require.js"></script></body>
</html>
6.CMD (Common Module Definition 通用模块定义)–少用
说明:
- 专门用于浏览器端,模块的加载是
异步
的。 - sea.js实现
- CMD是common.js与AMD的集合
基本语法
定义暴露模块:
- 定义没有依赖的模块
define(function(require,exports,module){exports.xxx=valuemodule.exports=value
})
- 定义有依赖的模块
define(function(require,exports,module){//同步引入依赖模块var module2 = require('./module2')//异步引入依赖模块require.async('./module3',function(m3){//...})//暴露模块exports.xxx=value
})
引入模块:
define(function(require){var m1 = require('./module1')var m3 = require('./module3')//使用m1/m3
})
sea.js简单使用教程
- 下载sea.js, 并引入
- 官网: http://seajs.org/
- github : https://github.com/seajs/seajs
- 将sea.js导入项目: js/libs/sea.js
- 创建项目结构
|-js|-libs|-sea.js|-modules|-module1.js|-module2.js|-module3.js|-module4.js|-main.js|-index.html
- 定义sea.js的模块代码
- module1.js
define(function (require, exports, module) {//内部变量数据var data = 'atguigu.com'//内部函数function show() {console.log('module1 show() ' + data)}//向外暴露exports.show = show})
- module2.js
define(function (require, exports, module) {module.exports = {msg: 'I Will Back'}})
- module3.js
define(function (require, exports, module) {const API_KEY = 'abc123'exports.API_KEY = API_KEY})
- module4.js
define(function (require, exports, module) {//引入依赖模块(同步)var module2 = require('./module2')function show() {console.log('module4 show() ' + module2.msg)}exports.show = show//引入依赖模块(异步)require.async('./module3', function (m3) {console.log('异步引入依赖模块3 ' + m3.API_KEY)})})
- main.js : 主(入口)模块
define(function (require) {var m1 = require('./module1')var m4 = require('./module4')m1.show()m4.show()})
- index.html:
<script type="text/javascript" src="js/libs/sea.js"></script>
<script type="text/javascript">seajs.use('./js/modules/main')
</script>
JS模块化CommonJS、ES6模块化 、AMD、CMD知识总结相关推荐
- JS模块化(node.js模块化和es6模块化)
JS 模块化 模块化概念 模块化是指解决一个复杂问题时,自顶向下逐层把系统划分为若干模块的过程,模块是可组合.分解和更换的单元. 模块化可提高代码的复用性和可维护性,实现按需加载. 模块化规范是对代码 ...
- 前端工程化(ES6模块化和webpack打包)
文章目录 目标: 目录: 1.模块化相关规范 1.1.模块化概述 1.2.浏览器端模块化规范 AMD CMD 1.3.服务器端模块化规范 CommonJS 1.4.大一统的模块化规范-ES6模块化 1 ...
- ES6模块化与异步编程高级用法
ES6模块化与异步编程高级用法 一.学习目标 能够知道如何使用ES6的模块化语法 能够知道如何使用Promise解决回调地域的问题 能够知道如何使用async/await 简化Promise的调用 能 ...
- 什么是ES6模块化?
ES6中的模块化 1. 前端模块化规范的分类 在 ES6 模块化规范诞生之前,JavaScript 社区已经尝试并提出了AMD.CMD.CommonJS等模块化规范. 但是,这些由社区提出的模块化标准 ...
- 史上最详细易懂的ES6模块化语法(重点)
对于初次学习ES6的小伙伴来说,ES6的模块化语法是一个重点,在没有模块化之前,前端js代码有一下三句话 1.私密不漏 2.重名不怕 3.依赖不乱 一.接下来先演示在没有模块化之前,这个"私 ...
- Requirejs与r.js打包,AMD、CMD、UMD、CommonJS、ES6模块化
一:require.js 1.require.js是什么?为什么要用它? require.js是一个JavaScript模块载入框架,实现的是AMD规范.使用require.js有以下优点: ① 异步 ...
- JavaScript模块化 --- Commonjs、AMD、CMD、es6 modules
随着前端js代码复杂度的提高,JavaScript模块化这个概念便被提出来,前端社区也不断地实现前端模块化,直到es6对其进行了规范,下面就介绍JavaScript模块化. 这篇文章还是希望能给大家一 ...
- JS JavaScript模块化(ES Module/CommonJS/AMD/CMD)
前言 前端开发中,起初只要在script标签中嵌入几十上百行代码就能实现一些基本的交互效果,后来js得到重视,应用也广泛起来了, jQuery,Ajax,Node.Js,MVC,MVVM等的助力也使得 ...
- 浅谈js模块化:commons、AMD、CMD、ES6几种模块化的用法及各自的特点
文章目录 一个页面需要引入多个js文件引发的问题: 模块化的好处: 几种常用的模块化规范 1. commonJs 2. AMD 3. ES6 4.CMD 总结 js模块化是现在比较流行的一种用法,它能 ...
- 前端模块化CommonJS、AMD、CMD、ES6
一.前端模块化 什么是模块化?为什么前端需要模块化? js代码量激增,放在同一个文件里面,不容易维护,而且牵一发而动全身. 这时候就需要将代码按照逻辑放在不同的文件里面,按照一定的语法规则,遵循特定的 ...
最新文章
- Android中设置控件可见与不可见详解
- autoware源码安装与运行ROSBAG示例(二)
- 软件开发流程包含哪些内容
- poj3621 Sightseeing Cows --- 01分数规划
- @RequestBody注解失效?从前端传来的数据到底需不需要@RequestBody注解?前端传输数据解析的问题?
- 11.Windows线程切换_线程优先级
- Markdown:数学公式练习(3)
- Win2003 + SQL 2005 做数据库集群总结(虚拟机)
- ConcurrentHashMap--自用,非教学
- Top 10 Project Management Software
- OAF_OAF组件系列1 - Item Style汇总(概念)
- 扩展RocketMQ 使其支持任意时间精度的消息延迟
- LAMP架构mariadb/apache的安装及基本使用
- oracle8ir2,Oracle8iR2(oracle8.1.6)在redhat linux 7.1上的简易安装方法
- C语言程序设计期末卷子,C语言程序设计试题1
- asp.net中涉及子文件夹的母版页和相对路径文件引用问题(一)
- 直播app源码,隐藏导航栏和状态栏
- 计算机函数sumif怎么用,怎么用sumif函数求和
- QEMU虚拟机源码分析概论
- 2020 用html jQuery实现广告轮播图自动切换 滚动页面 鼠标悬浮下标且左右切换图片