第一章:模块化发展史
什么是模块?
将一个复杂的程序依据一定的规则(规范)封装成几个块(文件), 并进行组合在一起
块的内部数据/实现是私有的, 只是向外部暴露一些接口(方法)与外部其它模块通信
一个模块的组成
数据--->内部的属性
操作数据的行为--->内部的函数
模块化
编码时是按照模块一个一个编码的, 整个项目就是一个模块化的项目
模块化进化史
1. 全局function模式
-
module1.js
//数据 let data = 'baidu.com' //操作数据的函数 function foo() { console.log(`foo() ${data}`) } function bar() { console.log(`bar() ${data}`) }
-
module2.js
let data2 = 'other data'; function foo() { //这里与另一个模块中的函数冲突了 console.log(`foo() ${data2}`) }
-
test.html
<script type="text/javascript" src="module1.js"></script> <script type="text/javascript" src="module2.js"></script> <script type="text/javascript"> let data = "我是修改后的数据" foo() bar() </script>
-
说明:
- 全局函数模式: 将不同的功能封装成不同的全局函数
- 问题: Global被污染了, 很容易引起命名冲突
2. namespace模式
-
module1.js
let myModule = { data: 'module1 baidu.com', foo() { console.log(`foo() ${this.data}`) }, bar() { console.log(`bar() ${this.data}`) } }
-
module2.js
let myModule2 = { data: 'module2 baidu.com', foo() { console.log(`foo() ${this.data}`) }, bar() { console.log(`bar() ${this.data}`) } }
-
test.html
<script type="text/javascript" src="module1.js"></script> <script type="text/javascript" src="module2.js"></script> <script type="text/javascript"> myModule.foo() myModule.bar() myModule2.foo() myModule2.bar() //可以直接修改模块内部的数据 myModule.data = 'other data' myModule.foo() </script>
-
说明
- namespace模式: 简单对象封装
- 作用: 减少了全局变量
- 问题: 依然可以修改模块内部代码,不安全
3. IIFE模式
-
module1.js
(function (window) { //数据 let data = 'baidu.com' //操作数据的函数 function foo() { //向外暴露的内部私有函数 console.log(`foo() ${data}`) } function bar() {//向外暴露的内部私有函数 console.log(`bar() ${data}`) otherFun() //内部调用 } function otherFun() { //未暴露的内部私有函数 console.log('otherFun()') } //暴露行为 window.myModule = {foo, bar} })(window)
-
test.html
<script type="text/javascript" src="module1.js"></script> <script type="text/javascript"> myModule.foo() myModule.bar() //myModule.otherFun() //报错:myModule.otherFun is not a function console.log(myModule.data) //undefined 不能访问模块内部数据 myModule.data = 'xxxx' //并不是修改的模块内部的data myModule.foo() //未受影响 </script>
-
说明:
- IIFE模式: 匿名函数自调用(闭包)
- IIFE : immediately-invoked function expression(立即调用函数表达式)
- 作用: 数据是私有的, 外部只能通过暴露的方法操作
- 问题: 如果当前这个模块依赖另一个模块怎么办?
4. IIFE模式增强
引入jquery到项目中
-
module1.js
(function (window, $) { //数据 let data = 'baidu.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)
-
test4.html
<script type="text/javascript" src="jquery-1.10.1.js"></script> <script type="text/javascript" src="module1.js"></script> <script type="text/javascript"> myModule.foo() </script>
-
说明
- IIFE模式增强 : 引入依赖
- 这就是现代模块实现的基石
5. 页面加载多个js的问题
-
页面:
<script type="text/javascript" src="module1.js"></script> <script type="text/javascript" src="module2.js"></script> <script type="text/javascript" src="module3.js"></script> <script type="text/javascript" src="module4.js"></script> <script type="text/javascript" src="module5.js"></script> <script type="text/javascript" src="module6.js"></script> <script type="text/javascript" src="module7.js"></script> <script type="text/javascript" src="module8.js"></script> <script type="text/javascript" src="module9.js"></script> <script type="text/javascript" src="module10.js"></script> <script type="text/javascript" src="module11.js"></script> <script type="text/javascript" src="module12.js"></script>
-
说明
- 一个页面需要引入多个js文件
- 问题:
- 请求过多
- 依赖模糊
- 难以维护
- 这些问题可以通过现代模块化编码和项目构建来解决
第二章 CommonJS服务端模块化(Node.js模块化)
1. 安装Node.js
2. 创建项目结构
|-modules
|-module1.js
|-module2.js
|-module3.js
|-app.js
|-package.json
{
"name": "test-0318",
"version": "1.0.0"
}
3. 模块化编码:
-
module1.js
module.exports = { data:'module1', foo(){ console.log('foo()------',this.data); }, bar(){ console.log('bar()------',this.data); } }
-
module2.js
module.exports = function () { console.log('module2'); }
-
module3.js
exports.foo = function () { console.log('foo() module3'); } exports.bar = function () { console.log('bar() module3'); }
下载第三方模块uniq:打开左下角的Terminal,cd到02_CommonJS-Node路径,输入命令:
npm install uniq --save
-
app.js
let module1 = require('./modules/module1') let module2 = require('./modules/module2') let module3 = require('./modules/module3') let a = require('uniq') module1.foo() module1.bar() module2() module3.foo() module3.bar() let arr = [1,11,2,2,2,5,5,5,3,4,6,6,9,7,8] console.log(a(arr))
4. 在node环境下运行app.js的两种方法(任选其一):
第一种方法:用命令启动:
node app.js
第二种方法:用工具启动: 右键 --> Run 'xxxxx.js'
第三章 CommonJS浏览器端模块化
1. 创建项目结构
|-js
|-dist //生成打包文件的目录
|-src //源码所在的目录(我们编写的、没经过工具处理的代码,叫做源码)
|-module1.js
|-module2.js
|-module3.js
|-app.js
|-index.html
|-package.json
{
"name": "test-0318",
"version": "1.0.0"
}
2. 模块化编码
-
module1.js
module.exports = { foo() { console.log('moudle1 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()') }
下载第三方模块uniq:打开左下角的Terminal,cd到02_CommonJS-Node路径,输入命令:
npm install uniq --save
-
app.js
//引用模块 let module1 = require('./module1') let module2 = require('./module2') let module3 = require('./module3') let uniq = require('uniq') //使用模块 module1.foo() module2() module3.foo() module3.bar() console.log(uniq([1, 3, 1, 4, 3]))
3. 下载browserify(用于把CommonJS的模块化语法,翻译成浏览器认识的语法,一个“翻译官”)
- 第一步,执行全局安装命令:
npm install browserify -g
(若此步骤报错,请使用管理员身份打开webstorm,再次执行即可) - 第二步,执行局部安装命令:
npm install browserify --save-dev
- 备注:以上两步骤都要执行,缺一不可!
4. 执行处理命令
- 第一步,cd到指定文件夹(03_CommonJS-Browserify)
- 第二步,输入命令
browserify js/src/app.js -o js/dist/bundle.js
5. 页面使用引入:
<script type="text/javascript" src="js/dist/bundle.js"></script>
第四章 ES6模块化(借助-Babel-Browserify)
1. 创建项目结构
|-js
|-src
|-module1.js
|-module2.js
|-module3.js
|-app.js
|-index.html
|-package.json
{
"name" : "es6-modular-0318",
"version" : "1.0.0"
}
2. 安装babel-cli(cli命令行界面(英语:command-line interface,缩写:CLI)是在图形用户界面得到普及之前使用最为广泛的用户界面), babel-preset-es2015和browserify:
第一步,全局安装:npm install babel-cli browserify -g
第二步,局部安装:npm install babel-preset-es2015 --save-dev
3. 定义.babelrc文件(给babel指定具体的任务),内容如下:
{
"presets": ["es2015"]
}
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 { name: 'Tom', setName: function (name) { this.name = name } }
下载jQuery模块:
npm install jquery --save
-
js/src/app.js
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);
5. 编译源代码
- 第一步:使用Babel将ES6编译为ES5代码,命令为:
babel js/src -d js/lib
- 第二步:使用Browserify编译js上一步生成的js,命令为:
browserify js/lib/app.js -o js/lib/bundle.js
- 备注:第一步操作后Babel将es6的模块化语法,转换成了CommonJS模块化语法(浏览器不识别),所以需要第二步用Browserify再次编译。
6. 页面中引入测试
<script type="text/javascript" src="js/lib/bundle.js"></script>
第五章 AMD模块化规范
require.js使用教程
- 下载require.js
- 官网: http://www.requirejs.org/
- github : https://github.com/requirejs/requirejs
- 将require.js导入项目: js/libs/require.js
- 创建项目结构
|-js
|-libs
|-require.js
|-modules
|-loger.js
|-dataService.js
|-main.js
|-index.html
- 定义require.js的模块代码
- dataService.js
define(function () { let msg = 'baidu.com' function getMsg() { return msg.toUpperCase() } return {getMsg} })
- loger.js
define(['dataService', 'jquery'], function (dataService, $) { let name = 'Tom2' function showMsg() { $('body').css('background', 'gray') console.log(dataService.getMsg() + ', ' + name) } return {showMsg} })
- 应用主(入口)js: main.js
requirejs.config({
baseUrl: './js/',
//模块标识名与模块路径映射
paths: {
"loger": "modules/loger",
"dataService": "modules/dataService",
}
})
//引入使用模块
requirejs( ['loger'], function(loger) {
loger.showMsg()
})
- 页面使用模块:
<script data-main="js/main.js" src="js/libs/require.js"></script>
- 使用第三方基于require.js的框架(jquery)
-
将jquery的库文件导入到项目:
- js/libs/jquery-1.10.1.js
-
在main.js中配置jquery路径
paths: { 'jquery': 'libs/jquery-1.10.1' }
-
在loger.js中使用jquery
define(['dataService','jquery'],function (dataService,$) { function showMsg() { console.log(dataService.getData()); $('body').css('background','skyblue') } return {showMsg} })
备注:define中的要写jquery不可以写jQuery,因为jQuery源码已经对AMD模块化进行了适配,已经定义好了"jquery"
第六章 CMD模块化规范
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 name = 'module1'; function fun() { console.log(name); } //暴露模块 exports.showName = {fun} });
- module2.js
define(function (require,exports,module) { var name = 'module2'; function fun2() { console.log(name); } //暴露模块 module.exports = fun2 });
- module3.js
define(function (require,exports,module) { var name = 'module3'; function foo() { console.log(name); } //暴露模块 module.exports = {foo} });
- module4.js
//module4依赖于module2,module3 define(function (require,exports,module) { var name = 'module4'; function foo() { console.log(name); } //同步引入module2 let module2 = require('./module2') module2() //异步引入module3 require.async('./module3',function (m3) { m3.foo() }) //暴露模块 module.exports = {foo} });
- main.js : 主(入口)模块
define(function (require) { var m1 = require('./module1') var m4 = require('./module4') m1.showName.fun() m4.foo() })
- index.html:
<!--
使用seajs:
1. 引入sea.js库
2. 如何定义导出模块 :
define()
exports
module.exports
3. 如何依赖模块:
require()
4. 如何使用模块:
seajs.use()
-->
<script type="text/javascript" src="js/libs/sea.js"></script>
<script type="text/javascript">
seajs.use('./js/modules/main')
</script>
5.思考:为什么运行后输出结果如下?
module2
module1
module4
module3