尚硅谷JS模塊化教程
當項目功能越來越多,代碼量便也會越來越多,後期的維護難度會增大,此時在JS方面就會考慮使用模塊化規範去管理。
本視頻內容涵蓋:理解模塊化,爲什麼要模塊化,模塊化的優缺點以及模塊化規範。並且將帶領大家學習開發中最流行的commonjs, AMD, ES6、CMD規範。
建議同學們學習完模塊化規範以後再學習項目構建,以更好的武裝自己的技能。
(01. 尚硅谷_JS模塊化_入門介紹&)
01. 尚硅谷_JS模塊化_入門介紹&
(02. 尚硅谷_JS模塊化_模塊進化史&)
02. 尚硅谷_JS模塊化_模塊進化史&
全局winow添加moule3屬性,module3是一個對象裏面有個方法是foo。
暴露的是module3對象
直接調用,module4是一個函數
## 模塊化進化史教程
1. 全局function模式
* module1.js
```
//數據
let data = 'atguigu.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}`)
}
```
* test1.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: 'atguigu.com',
foo() {
console.log(`foo() ${this.data}`)
},
bar() {
console.log(`bar() ${this.data}`)
}
}
```
* module2.js
```
let myModule2 = {
data: 'atguigu.com2222',
foo() {
console.log(`foo() ${this.data}`)
},
bar() {
console.log(`bar() ${this.data}`)
}
}
```
* test2.html
```
<script type="text/javascript" src="module2.js"></script>
<script type="text/javascript" src="module22.js"></script>
<script type="text/javascript">
myModule.foo()
myModule.bar()
myModule2.foo()
myModule2.bar()
myModule.data = 'other data' //能直接修改模塊內部的數據
myModule.foo()
</script>
```
* 說明
* namespace模式: 簡單對象封裝
* 作用: 減少了全局變量
* 問題: 不安全
3. IIFE模式
* module3.js
```
(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)
```
* test3.html
```
<script type="text/javascript" src="module3.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到項目中
* module4.js
```
(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)
```
* test4.html
```
<script type="text/javascript" src="jquery-1.10.1.js"></script>
<script type="text/javascript" src="module4.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>
```
* 說明
* 一個頁面需要引入多個js文件
* 問題:
* 請求過多
* 依賴模糊
* 難以維護
* 這些問題可以通過現代模塊化編碼和項目構建來解決
(03. 尚硅谷_JS模塊化規範_commonjs基於服務器端(node)應用&)
03. 尚硅谷_JS模塊化規範_commonjs基於服務器端(node)應用&
## Node.js模塊化教程
1. 下載安裝node.js
2. 創建項目結構
```
|-modules
|-module1.js
|-module2.js
|-module3.js
|-app.js
|-package.jsonA
{
"name": "commonJS-node",
"version": "1.0.0"
}
```
3. 下載第三方模塊
* npm install uniq --save
4. 模塊化編碼
* 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()')
}
```
* app.js
```
/**
1. 定義暴露模塊:
module.exports = value;
exports.xxx = value;
2. 引入模塊:
var module = require(模塊名或模塊路徑);
*/
"use strict";
//引用模塊
let module1 = require('./modules/module1')
let module2 = require('./modules/module2')
let module3 = require('./modules/module3')
let uniq = require('uniq')
let fs = require('fs')
//使用模塊
module1.foo()
module2()
module3.foo()
module3.bar()
console.log(uniq([1, 3, 1, 4, 3]))
```
5. 通過node運行app.js
* 命令: node app.js
* 工具: 右鍵-->運行
不能無限暴露,下面的會把上面的覆蓋
第二種暴露方式,不斷向exports對象添加屬性
(04. 尚硅谷_JS模塊化規範_commonjs基於瀏覽器端應用&)
04. 尚硅谷_JS模塊化規範_commonjs基於瀏覽器端應用&
瀏覽器執行打包生成的build.js文件
## Browserify模塊化使用教程
1. 創建項目結構
```
|-js
|-dist //打包生成文件的目錄
|-src //源碼所在的目錄
|-module1.js
|-module2.js
|-module3.js
|-app.js //應用主源文件
|-index.html
|-package.json
{
"name": "browserify-test",
"version": "1.0.0"
}
```
2. 下載browserify
* 全局: npm install browserify -g
* 局部: npm install browserify --save-dev
3. 定義模塊代碼
* 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()')
}
```
* app.js (應用的主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]))
```
* 打包處理js:
* browserify js/src/app.js -o js/dist/bundle.js
* 頁面使用引入:
```
<script type="text/javascript" src="js/dist/bundle.js"></script>
```
(05. 尚硅谷_JS模塊化規範_AMD規範_NoAMD&)
05. 尚硅谷_JS模塊化規範_AMD規範_NoAMD&
一個頁面一個模塊
## 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. 頁面使用模塊:
<script data-main="js/main" src="js/libs/require.js"></script>
------------------------------------------------------------------------
6. 使用第三方基於require.js的框架(jquery)
* 將jquery的庫文件導入到項目:
* js/libs/jquery-1.10.1.js
* 在main.js中配置jquery路徑
```
paths: {
'jquery': 'libs/jquery-1.10.1'
}
```
* 在alerter.js中使用jquery
```
define(['dataService', 'jquery'], function (dataService, $) {
var name = 'xfzhang'
function showMsg() {
$('body').css({background : 'red'})
alert(name + ' '+dataService.getMsg())
}
return {showMsg}
})
```
------------------------------------------------------------------------
7. 使用第三方不基於require.js的框架(angular)
* 將angular.js導入項目
* js/libs/angular.js
* 在main.js中配置
```
(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' : {
exports : 'angular'
}
}
})
//引入使用模塊
require( ['alerter', 'angular'], function(alerter, angular) {
alerter.showMsg()
console.log(angular);
})
})()
```
(06. 尚硅谷_JS模塊化規範_AMD規範_自定義模塊&)
06. 尚硅谷_JS模塊化規範_AMD規範_自定義模塊&
暴露接口是一個對象,對象裏面是一個方法
有依賴的時候,通過數組定義依賴的模塊
引入需要配置
baseUrl是根路徑,找到js下面的modules
引入規範require庫
(07. 尚硅谷_JS模塊化規範_AMD規範_第三方模塊&)
07. 尚硅谷_JS模塊化規範_AMD規範_第三方模塊&
jquery遇到AMD必須jquery小寫q才能返回jQuery對象
(08. 尚硅谷_JS模塊化規範_CMD規範應用&)
08. 尚硅谷_JS模塊化規範_CMD規範應用&
## sea.js簡單使用教程
1. 下載sea.js, 並引入
* 官網: http://seajs.org/
* github : https://github.com/seajs/seajs
* 將sea.js導入項目: js/libs/sea.js
2. 創建項目結構
```
|-js
|-libs
|-sea.js
|-modules
|-module1.js
|-module2.js
|-module3.js
|-module4.js
|-main.js
|-index.html
```
3. 定義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()
})
```
4. 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>
```
(09. 尚硅谷_JS模塊化規範_ES6規範_基本使用&)
09. 尚硅谷_JS模塊化規範_ES6規範_基本使用&
引用的時候必須用對象的解構賦值的方式,否則是undefined
相當於在當前作用域下定義了foo變量
重新編譯打包
es6轉爲es5
重新打包編譯
## ES6-Babel-Browserify使用教程
1. 定義package.json文件
```
{
"name" : "es6-babel-browserify",
"version" : "1.0.0"
}
```
2. 安裝babel-cli, babel-preset-es2015和browserify
* npm install babel-cli browserify -g
* npm install babel-preset-es2015 --save-dev
* preset 預設(將es6轉換成es5的所有插件打包)
3. 定義.babelrc文件
```
{
"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
}
}
```
* 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代碼(但包含CommonJS語法) : babel js/src -d js/lib
* 使用Browserify編譯js : browserify js/lib/app.js -o js/lib/bundle.js
6. 頁面中引入測試
```
<script type="text/javascript" src="js/lib/bundle.js"></script>
```
7. 引入第三方模塊(jQuery)
1). 下載jQuery模塊:
* npm install jquery@1 --save
2). 在app.js中引入並使用
```
import $ from 'jquery'
$('body').css('background', 'red')
```
(10. 尚硅谷_JS模塊化規範_ES6規範_默認暴露&)
10. 尚硅谷_JS模塊化規範_ES6規範_默認暴露&
默認暴露只能暴露一次,可以暴露一個對象
module3就是一個函數
下載1版本jquery
## JS模塊化
* 模塊化的理解
* 什麼是模塊?
* 將一個複雜的程序依據一定的規則(規範)封裝成幾個塊(文件), 並進行組合在一起
* 塊的內部數據/實現是私有的, 只是向外部暴露一些接口(方法)與外部其它模塊通信
* 一個模塊的組成
* 數據--->內部的屬性
* 操作數據的行爲--->內部的函數
* 模塊化
* 編碼時是按照模塊一個一個編碼的, 整個項目就是一個模塊化的項目
* 模塊化的進化過程
* 全局function模式 :
* 編碼: 全局變量/函數
* 問題: 污染全局命名空間, 容易引起命名衝突/數據不安全
* namespace模式 :
* 編碼: 將數據/行爲封裝到對象中
* 解決: 命名衝突(減少了全局變量)
* 問題: 數據不安全(外部可以直接修改模塊內部的數據)
* IIFE模式/增強
* IIFE : 立即調用函數表達式--->匿名函數自調用
* 編碼: 將數據和行爲封裝到一個函數內部, 通過給window添加屬性來向外暴露接口
* 引入依賴: 通過函數形參來引入依賴模塊
```
(function(window, module2){
var data = 'atguigu.com'
function foo() {
module2.xxx()
console.log('foo()'+data)
}
function bar() {
console.log('bar()'+data)
}
window.module = {foo}
})(window, module2)
```
* 模塊化規範
* CommonJS
* Node.js : 服務器端
* Browserify : 瀏覽器端 也稱爲js的打包工具
* 基本語法:
* 定義暴露模塊 : exports
```
exports.xxx = value
module.exports = value
```
引入模塊 : require
```
var module = require('模塊名/模塊相對路徑')
```
* 引入模塊發生在什麼時候?
* Node : 運行時, 動態同步引入
* Browserify : 在運行前對模塊進行編譯/轉譯/打包的處理(已經將依賴的模塊包含進來了),
運行的是打包生成的js, 運行時不存在需要再從遠程引入依賴模塊
* AMD : 瀏覽器端
* require.js
* 基本語法
* 定義暴露模塊: define([依賴模塊名], function(){return 模塊對象})
* 引入模塊: require(['模塊1', '模塊2', '模塊3'], function(m1, m2){//使用模塊對象})
* 配置:
```
require.config({
//基本路徑
baseUrl : 'js/',
//標識名稱與路徑的映射
paths : {
'模塊1' : 'modules/模塊1',
'模塊2' : 'modules/模塊2',
'angular' : 'libs/angular',
'angular-messages' : 'libs/angular-messages'
},
//非AMD的模塊
shim : {
'angular' : {
exports : 'angular'
},
'angular-messages' : {
exports : 'angular-messages',
deps : ['angular']
}
}
})
```
* CMD : 瀏覽器端
* sea.js
* 基本語法
* 定義暴露模塊:
```
define(function(require, module, exports){
通過require引入依賴模塊
通過module/exports來暴露模塊
exports.xxx = value
})
```
* 使用模塊seajs.use(['模塊1', '模塊2'])
* ES6
* ES6內置了模塊化的實現
* 基本語法
* 定義暴露模塊 : export
* 暴露一個對象:
```
export default 對象
```
* 暴露多個:
```
export var xxx = value1
export let yyy = value2
var xxx = value1
let yyy = value2
export {xxx, yyy}
```
* 引入使用模塊 : import
* default模塊:
```
import xxx from '模塊路徑/模塊名'
```
* 其它模塊
```
import {xxx, yyy} from '模塊路徑/模塊名'
import * as module1 from '模塊路徑/模塊名'
```
* 問題: 所有瀏覽器還不能直接識別ES6模塊化的語法
* 解決:
* 使用Babel將ES6--->ES5(使用了CommonJS) ----瀏覽器還不能直接支行
* 使用Browserify--->打包處理----瀏覽器可以運行