目标:
1. 理解模块与模块化
2. 了解各种模块化规范及其实现
3. 区别各个模块化规范之间的区别
4. 掌握基于CommonJS和ES6模块化规范的编码
1). 什么是模块?
将一个复杂的程序依据一定的规则(规范)封装成几个块(文件), 并进行组合在一起
块的内部数据/实现是私有的, 只是向外部暴露一些接口(方法)与外部其它模块通信
2). 一个模块的组成
私有的数据—>内部的变量
私有的行为(操作数据)—>内部的函数
向外暴露n个行为
3). 模块化
描述一种特别的编码项目JS的方式: 以模块为单元一个一个编写的
模块化的项目: JS编码时是按照模块一个一个编码的
4). 模块化的进化过程
/**
* 全局函数模式: 将不同的功能封装成不同的全局函数
* 问题: Global被污染了, 很容易引起命名冲突
*/
//数据
let data = 'atguigu.com'
function foo() {
console.log('foo()')
}
function bar() {
console.log('bar()')
}
/**
* namespace模式: 简单对象封装
* 作用: 减少了全局变量
* 问题: 不安全(数据不是私有的, 外部可以直接修改)
*/
let myModule = {
data: 'atguigu.com',
foo() {
console.log(`foo() ${this.data}`)
},
bar() {
console.log(`bar() ${this.data}`)
}
}
js文件中:
/**
* IIFE模式: 匿名函数自调用(闭包)
* IIFE : immediately-invoked function expression(立即调用函数表达式)
* 作用: 数据是私有的, 外部只能通过暴露的方法操作
* 问题: 如果当前这个模块依赖另一个模块怎么办?
*/
(function (window) {
//数据
let data = 'atguigu.com'
//操作数据的函数
function foo() { //用于暴露有函数
console.log(`foo() ${data}`)
}
function bar() {//用于暴露有函数
console.log(`bar() ${data}`)
otherFun() //内部调用
}
function otherFun() { //内部私有的函数
console.log('otherFun()')
}
//暴露行为
window.myModule = {foo, bar}
})(window)
//html文件中
myModule.foo()
myModule.bar()
//myModule.otherFun() //myModule.otherFun is not a function
console.log(myModule.data) //undefined 不能访问模块内部数据
myModule.data = 'xxxx' //不是修改的模块内部的data
myModule.foo() //没有改变
2). IIFE增强版
js文件中:
/**
* IIFE模式增强 : 引入依赖
* 这就是现代模块实现的基石
*/
(function (window, $) {
//数据
let data = 'atguigu.com'
//操作数据的函数
function foo() { //用于暴露有函数
console.log('foo() ${data}')
$('body').css('background', 'red')
}
function bar() {//用于暴露有函数
console.log(`bar() ${data}`)
otherFun() //内部调用
}
function otherFun() { //内部私有的函数
console.log('otherFun()')
}
//暴露行为
window.myModule = {foo, bar}
})(window, jQuery)
html文件中
//引入app.js会出错,需要用browserify编译打包,即上一步的处理,
//才能将commonjs规范应用到浏览器端
//定义一个没有依赖的模块
(function (window) {
let msg = 'atguigu.com'
function getMsg() {
return msg.toUpperCase()
}
window.dataService = {getMsg}
})(window)
//定义一个有依赖的模块
(function (window, dataService) {
let name = 'Tom'
function showMsg() {
alert(dataService.getMsg() + ', ' + name)
}
window.alerter = {showMsg}
})(window, dataService)
(function (alerter) {
alerter.showMsg()
})(alerter)
缺点:发三次请求,且依赖关系顺序不能出错
2.AMD规范
require.js使用教程
A.自定义的模块
|-js
|-libs
|-require.js
|-modules
|-alerter.js
|-dataService.js
|-main.js
|-index.html
//定义没有依赖的模块
define(function () {
let name = 'dataService.js'
function getName() {
return name;
}
//暴露模块
return {getName}
})
//定义有依赖模块
//右边是形参
define(['dataService'], function (dataService) {
let msg = 'alert.js'
function showMsg() {
alert(dataService.getMsg() + ', ' + name)
}
//暴露的模块,是个对象,因为对象能够暴露多个属性
return {showMsg}
})
(function () {
//配置
requirejs.config({
//基本路径
baseUrl: "js/", //出发点在根目录下
//模块标识名与模块路径映射
paths: {
"alerter": "modules/alerter", //后面不加.js
"dataService": "modules/dataService",
}
})
//引入使用模块
requirejs( ['alerter'], function(alerter) {
alerter.showMsg()
})
})()
B.引入第三方模块
paths: {
'jquery': 'libs/jquery-1.10.1'
//jquery不可以写成jQuery,如下图所示
}
define(['dataService', 'jquery'], function (dataService, $) {
//这里的jquery也用小写
let msg = 'alert.js'
function showMsg() {
$('body').css({background : 'red'})
alert(name + ' '+dataService.getMsg())
}
return {showMsg}
})
C.不支持AMD的第三方库
(function () {
require.config({
//基本路径
baseUrl: "js/",
//模块标识名与模块路径映射
paths: {
//第三方库
'jquery' : './libs/jquery-1.10.1',
'angular' : './libs/angular',
//自定义模块
"alerter": "./modules/alerter",
//地址中最前面的点不能去,表示当前目录,去掉点是表示一个层级
"dataService": "./modules/dataService"
},
/*
配置不兼容AMD的模块
exports : 指定与相对应的模块名对应的模块对象
*/
shim: {//就是一个单独的配置
'angular' : { //指向path中的angular模块
exports : 'angular' //暴露出的angular对象
}
}
})
//引入使用模块
require( ['alerter', 'angular'], function(alerter, angular) {
alerter.showMsg()
console.log(angular);
})
})()
sea.js简单使用教程
|-js
|-libs
|-sea.js
|-modules
|-module1.js
|-module2.js
|-module3.js
|-module4.js
|-main.js
|-index.html
define(function (require, exports, module) {
//内部变量数据
let msg = 'module1';
//内部函数
function show() {
return msg;
}
//向外暴露
module.exports = {show};
})
define(function (require, exports, module) {
module.exports = {
msg: 'I Will Back'
}
})
define(function (require, exports, module) {
const API_KEY = 'abc123'
exports.API_KEY = API_KEY
})
define(function (require, exports, module) {
//引入依赖模块(同步)
var module2 = require('./module2')
console.log(module2.msg);
//引入依赖模块(异步)
require.async('./module3', function (m3) {
console.log('异步引入依赖模块3 ' + m3.API_KEY)
})
function show() {
console.log('module4 show());
}
exports.show = show
})
define(function (require) {
var m1 = require('./module1')
var m4 = require('./module4')
m1.show()
m4.show()
})
//输出的顺序为模块1.2.4.3,因为注意有异步引入依赖模块
**
ES6-Babel-Browserify使用教程
上图为目录结构
{
"name" : "es6-babel-browserify",
"version" : "1.0.0"
}
{
"presets": ["es2015"] //读到这个知道了自己要做的事是转换es6到es5
}
```
4. 编码
js/src/module1.js 分别暴露
export function foo() {
console.log('module1 foo()');
}
export function bar() {
console.log('module1 bar()');
}
export const DATA_ARR = [1, 3, 5, 1]
js/src/module2.js 统一暴露
let data = 'module2 data'
function fun1() {
console.log('module2 fun1() ' + data);
}
function fun2() {
console.log('module2 fun2() ' + data);
}
export {fun1, fun2}
js/src/module3.js 默认暴露,可以暴露任意数据类型,暴露什么数据接收到的就是什么数据
//export default()=>{
// console.log("我是默认暴露的箭头函数");
//}
//此种写法调用:import module3 from(./module3.js);
//左边module3为形参,可以写xxx
//module3();//因为此时module3是一个函数
//默认暴露只能写一次,要暴露多个数据 用对象
export default {
name: 'Tom',
setName: function (name) {
this.name = name
}
}
js/src/app.js
//引入其他的模块
//语法: import xxx from '路径'
import {foo, bar} from './module1'
import {DATA_ARR} from './module1'
import {fun1, fun2} from './module2'
//分别暴露和统一暴露在引入的时候,必须用对象结构赋值的形式。因为模块里的东西很多,只取自己需要的
//统一暴露和分别暴露被统一称为常规暴露
import person from './module3'
import $ from 'jquery'//第七步中详细解释
$('body').css('background', 'red')
foo()
bar()
console.log(DATA_ARR);
fun1()
fun2()
person.setName('JACK')
console.log(person.name);
import $ from 'jquery'
$('body').css('background', 'red')
本文为尚硅谷模块化教学总结
几种模块化规范的区别可看https://blog.csdn.net/Real_Bird/article/details/54869066
commonjs和ES6及requirejs模块循环引用