2022-12-02
模塊 js require module
模塊化簡介
JavaScript 最初的目的是為了解決用戶交互頁面的問題;但是隨著互聯(lián)網(wǎng)技術(shù)的發(fā)展,瀏覽器性能大大提升,很多用戶交互頁面也隨之復(fù)雜起來,更是隨著 Web2.0 的發(fā)展,頁面異步交互,前端代碼庫層出不窮,前端代碼日益膨脹;此時(shí) Js 方便就會去考慮使用代碼模塊規(guī)范去管理。
而在項(xiàng)目開發(fā)中我們常把一個(gè)功能歸類為一個(gè)模塊,多個(gè)程序員去負(fù)責(zé)不同的模塊。 js也不例外,然而 js 早期通過閉包的方式去實(shí)現(xiàn)模塊化的方式存在很多的問題,隨著 js 的發(fā)展中, 很多人為 js 提供了各種出色的模塊化方案。 其中代表有requirejs ,seajs,commonjs,es6 的模塊化方案。(其中 es6 的模塊化方案必定是將來的主流,這也是至今官方提出唯一的規(guī)范)。
模塊化初級
什么是模塊
將一些復(fù)雜的程序代碼根據(jù)制定的規(guī)則封裝成幾個(gè)塊(文件), 并進(jìn)行組合在一起
塊(文件)的內(nèi)部數(shù)據(jù)是私有的, 只向外部暴露部分接口(方法)與其它模塊進(jìn)行通信
模塊化的進(jìn)化
原始寫法
模塊就是實(shí)現(xiàn)特定功能的一組方法。只要把不同的函數(shù)(以及記錄狀態(tài)的變量)簡單地放在一起,就算是一個(gè)模塊。
function m1(){
//...
}
function m2(){
//...
}
上面的函數(shù) m1()和 m2(),組成一個(gè)模塊。使用的時(shí)候,直接調(diào)用就行了。這種做法的缺點(diǎn)很明顯:"污染"了全局變量,無法保證不與其他模塊發(fā)生變量名沖突,而且模塊成員之間看不出直接關(guān)系。
對象寫法
為了解決上面的缺點(diǎn),可以把模塊寫成一個(gè)對象,所有的模塊成員都放到這個(gè)對象里面。
var module1 = {
count : 0.
m1: function (){
//...
},
m2: function (){
//...
}
};
上面的函數(shù) m1()和 m2(),都封裝在 module1 對象里。使用的時(shí)候,就是調(diào)用這個(gè)對象的屬性。module1.m1();但是,這樣的寫法會暴露所有模塊成員,內(nèi)部狀態(tài)可以被外部改寫。比如,外部代碼可以直接改變內(nèi)部計(jì)數(shù)器的值。module1.count = 5;
匿名函數(shù)自調(diào)用(閉包)
將數(shù)據(jù)和行為封裝到一個(gè)函數(shù)內(nèi)部, 通過給 window 添加屬性來向外暴露接口
// module.js 文件
(function(window,$) {
var data = '1000phone.com'
//操作數(shù)據(jù)的函數(shù)
function foo() {//用于暴露有函數(shù)
console.log("foo()"+data);
}
function bar() {//用于暴露有函數(shù)
console.log("bar()"+data);
$('body').css('background', 'red');
otherFun(); //內(nèi)部調(diào)用
}
function otherFun() {
//內(nèi)部私有的函數(shù)
console.log('otherFun()');
}
//暴露行為
window.myModule = {
foo: foo,
bar: bar
}
})(window, $)
使用閉包的方式, 可以達(dá)到不暴露私有成員的目的, 外部通過暴露的方法操作私有成員
案例中通過 jquery 方法將頁面的背景顏色改成紅色, 所以必須先引入 jQuery 庫,就把這個(gè)庫當(dāng)作參數(shù)傳入。 這樣做除了保證模塊的獨(dú)立性,還使得模塊之間的依賴關(guān)系變得明顯。
模塊化的好處
減少命名空間污染
更好的分離, 按需加載
更高復(fù)用性
高可維護(hù)性
引入多個(gè)模塊后出現(xiàn)的問題
請求過多
首先我們要依賴多個(gè)模塊,那樣就會發(fā)送多個(gè)請求,導(dǎo)致請求過多
依賴模糊
我們不知道他們的具體依賴關(guān)系是什么,也就是說很容易因?yàn)椴涣私馑麄冎g的依賴關(guān)系導(dǎo)致加載先后順序出錯(cuò)。
難以維護(hù)
前兩個(gè)原因就導(dǎo)致了很難維護(hù),很可能出現(xiàn)牽一發(fā)而動全身的情況導(dǎo)致項(xiàng)目出現(xiàn)嚴(yán)重的問題。
模塊化固然有多個(gè)好處,然而一個(gè)頁面需要引入多個(gè) js 文件,就會出現(xiàn)以上這些問題。而這些問題可以通過模塊化規(guī)范來解決
CommonJS
09 年, Ryan Dahl 創(chuàng)造了 node.js 項(xiàng)目,將 JavaScript 語言用于服務(wù)器端編程。這也標(biāo)志著"JavaScript 模塊化編程"正式誕生。因?yàn)槔蠈?shí)說,在瀏覽器環(huán)境下,沒有模塊也不是特別大的問題,畢竟網(wǎng)頁程序的復(fù)雜性有限;但是在服務(wù)器端,一定要有模塊, 與操作系統(tǒng)和其他應(yīng)用程序互動,否則根本沒法編程。
node.js 的模塊系統(tǒng),就是參照 CommonJS 規(guī)范實(shí)現(xiàn)的。在 CommonJS 中,有一個(gè)全局性方法 require(),用于加載模塊。假定有一個(gè)文件模塊 fs.js,就可以像下面這樣加載,并調(diào)用模塊提供的方法
const fs = require('fs');
fs.writeFileSync('./test.js'.'hello node');
//require()用于加載模塊
上面的代碼中有一個(gè)問題,fs.writeFileSync('./test.js'.'hello node')代碼的運(yùn)行是在載入 fs.js 之后; 即文件的同步加載還是異步加載, 這對于服務(wù)端不是問題,但是對于瀏覽器端就是大問題;瀏覽器端如果是同步加載模塊, 則載入的模塊的等待時(shí)間取決于網(wǎng)速的快慢,可能需要等待很長時(shí)間, 這對用戶體驗(yàn)是一個(gè)很大的考驗(yàn)。 由此而來 AMD 規(guī)范誕生的背景。
AMD 異步模塊定義
目前,主要有兩個(gè) Javascript 庫實(shí)現(xiàn)了 AMD 規(guī)范: require.js 和 curl.js。
我們主要介紹require.js
require.js 的誕生,就是為了解決這兩個(gè)問題:
實(shí)現(xiàn) js 文件的異步加載,避免網(wǎng)頁失去響應(yīng);
管理模塊之間的依賴性,便于代碼的編寫和維護(hù)。
require.js 的加載
使用 require.js 的第一步,是先去官方網(wǎng)站下載最新版本。https://requirejs.org/docs/download.html#latest,下載后,假定把它放在 js/libs 子目錄下面,就可以加載了。
上面的 require.js 加載完畢后,會首先檢查script標(biāo)簽中的 data-main 屬性, 加載 data-main 屬性值中所定義的 js 文件;如:data-main='js/libs/main',將在 require.js 加載完畢后第一個(gè)加載 js/libs 路徑下的main.js注意: require.js 后綴名是 js,所以 main 的后綴名省略)
使用案例(引入第三方庫)
創(chuàng)建 js/libs 目錄存放 require.js 及第三方庫文件
將下載的 require.js 文件及需要用到的第三方庫文件放入 js/libs 目錄下
編寫自定義模塊
編寫自定義功能模塊(網(wǎng)址大寫轉(zhuǎn)換)
// js/modules/dataService.js 文件
// 定義沒有依賴的模塊
define(function() {
var msg = 'www.1000phone.com';
function getMsg() {
return msg.toUpperCase();
}
return { getMsg } // 暴露模塊
})
編寫自定義功能模塊(控制臺輸出,改變網(wǎng)頁背景顏色)
// js/modules/console.js 文件
// 定義有依賴的模塊
define(['dataService','jquery'], function(dataService,$) {
var name = 'Leon';
function showMsg() {
console.log(dataService.getMsg() + ',' + name);
$('body').css('background','skyblue');
}
// 暴露模塊
return { showMsg }
})
語法解析: 定義暴露模塊:
//定義沒有依賴的模塊
define(function(){
return 模塊
})
//定義有依賴的模塊
define(['module1', 'module2'], function(m1. m2){
return 模塊
})
// 其中 module1. module2 為依賴模塊
定義入口模塊
// js/main.js 文件
(function() {
require.config({
baseUrl: 'js/', //基本路徑 出發(fā)點(diǎn)在根目錄下
paths: {
//映射: 模塊標(biāo)識名: 路徑
// 自定義模塊
console: './modules/console', //此處不能寫成 console.js,會報(bào)錯(cuò)
dataService: './modules/dataService',
// 第三方模塊
jquery: './libs/jquery-1.11.3.min'
}
});
require(['console'], function(console) {
console.showMsg();
});
})()
語法解析:
引入使用模塊:
require(['module1', 'module2'], function(m1. m2){
// 使用 m1/m2
})
require——該函數(shù)用于讀取依賴。同樣它是一個(gè)全局函數(shù),不需要使用 requirejs 命名空間.
config——該函數(shù)用于配置 RequireJS.
require.config 配置參數(shù)選項(xiàng)
baseUrl——用于加載模塊的根路徑。
paths——用于映射存在根路徑下面的模塊路徑
定義主頁面
AMD module
小結(jié):AMD 模塊定義的方法非常清晰,不會污染全局環(huán)境,能夠清楚地顯示依賴關(guān)系。 AMD 模式可以用于瀏覽器環(huán)境,并且允許非同步加載模塊,也可以根據(jù)需要動態(tài)加載模塊。
CMD
CMD 規(guī)范專門用于瀏覽器端,模塊的加載是異步的,模塊使用時(shí)才會加載執(zhí)行。 CMD 規(guī)范整合了 CommonJS 和 AMD 規(guī)范的特點(diǎn)。在 Sea.js 中,所有 JavaScript 模塊都遵循 CMD模塊定義規(guī)范。 Sea.js 可以實(shí)現(xiàn) JavaScript 的模塊化開發(fā)及加載機(jī)制。它應(yīng)用于早期的一些 js 項(xiàng)目中,是淘寶 js 工程師玉伯提出的一個(gè)方案。文檔
CMD 規(guī)范基本語法
define(function(require, exports, module){...});
用來定義模塊。 Sea.js 推崇一個(gè)模塊一個(gè)文件,遵循統(tǒng)一的寫法
define(function(require){var a = require("xModule"); ... });
require 用來獲取指定模塊的接口,引入的是模塊, js 文件的后綴.js 可以不寫
require.async
用來在模塊內(nèi)部異步加載一個(gè)或多個(gè)模塊。 例如:
define(function(require){
require.async(['aModule','bModule'],function(a,b){
// 異步加載多個(gè)模塊,在加載完成時(shí),執(zhí)行回調(diào)
a.func();
b.func();
});
});
exports
用來在模塊內(nèi)部對外提供接口。 例如:
define(function(require, exports){
exports.varName01 = 'varValue'; // 對外提供 varName01 屬性
exports.funName01 = function(p1.p2){ // 對外提供 funName01 方法
....
}
});
module.exports
用來在模塊內(nèi)部對外提供接口。例如:
define(function(require, exports, module) {
module.exports = { // 對外提供接口
name: 'a',
doSomething: function() {...};
};
});
seajs.config({...});
用來對 Sea.js 進(jìn)行配置。
seajs.use(['a','b'],function(a,b){...});
用來在頁面中加載一個(gè)或多個(gè)模塊。
定義暴露模塊:
//定義沒有依賴的模塊
define(function(require, exports, module){
exports.xxx = value
module.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 m4 = require('./module4')
m1.show()
m4.show()
})
創(chuàng)建 js/libs 目錄,
將下載的 seajs 放在js/libs目錄下
自定義模塊代碼并使用
定義 module1 模塊,并暴露 show 方法
// js/modules/module1.js 文件
define(function (require, exports, module) {
//內(nèi)部變量數(shù)據(jù)
var data = '1000phone.com';
//內(nèi)部函數(shù)
function show() {
console.log('module1 show() ' + data);
}
//向外暴露
exports.show = show;
})
定義 module2 模塊,并暴露接口信息 msg
// js/modules/module2.js 文件
define(function (require, exports, module) {
module.exports = {
msg: '1000phone study'
}
})
定義 module3 模塊,并暴露一個(gè)常量信息
// js/modules/module3.js 文件
define(function(require, exports, module) {
const API_KEY = 'leon';
exports.API_KEY = API_KEY
})
定義 module4 模塊, 同步引入 module2. 異步引入 module3 模塊
// js/modules/module4.js 文件
define(function (require, exports, module) {
// 引入依賴模塊(異步)
require.async('./module3', function (m3) {
console.log('異步引入依賴模塊 3 ' + m3.API_KEY)
});
// 引入依賴模塊(同步)
var module2 = require('./module2');
function show() {
console.log('module4 show() ' + module2.msg)
}
exports.show = show;
});
定義主文件 main 模塊,引入模塊 module1 和模塊 module4 并分別調(diào)用 show 方法
// js/modules/main.js 文件
define(function (require) {
var m1 = require('./module1')
var m4 = require('./module4')
m1.show()
m4.show()
})
創(chuàng)建主頁面,并在頁面中加載 main 模塊
小結(jié):通過 Sea.js 可以將大量 javascript 代碼封裝成一個(gè)個(gè)小模塊,然后輕松實(shí)現(xiàn)模塊的加載和依賴管理
ES6 的 Module
在 ES6 之前,模塊加載方案主要還是使用 CommonJS 和 AMD 兩種。前者用于服務(wù)器,后者用于瀏覽器。 ES6 在語言標(biāo)準(zhǔn)的層面上,實(shí)現(xiàn)了模塊功能,而且實(shí)現(xiàn)得相當(dāng)簡單,完全可以取代 CommonJS 和 AMD 規(guī)范,成為瀏覽器和服務(wù)器通用的模塊解決方案。ES6 模塊的設(shè)計(jì)思想是盡量的靜態(tài)化,使得編譯時(shí)就能確定模塊的依賴關(guān)系,以及輸入和輸出的變量。 CommonJS 和 AMD 模塊,都只能在運(yùn)行時(shí)確定這些東西。比如:CommonJS 模塊就是對象,輸入時(shí)必須查找對象屬性。ES6 的模塊自動采用嚴(yán)格模式,不管你有沒有在模塊頭部加上 "use strict";。
模塊功能主要命令
模塊功能主要由兩個(gè)命令構(gòu)成: export 和 import。
export 命令用于規(guī)定模塊的對外接口,
import 命令用于輸入其他模塊提供的功能。
一個(gè)模塊就是一個(gè)獨(dú)立的文件。該文件內(nèi)部的所有變量,外部無法獲取。如果你希望外部能夠讀取模塊內(nèi)部的某個(gè)變量,就必須使用 export 關(guān)鍵字輸出該變量。使用 export 命令定義了模塊的對外接口以后,其他 JS 文件就可以通過 import 命令加載這個(gè)模塊。
/** 定義模塊 math.js **/
var basicNum = 0;
var add = function (a, b) {
return a + b;
};
export { basicNum, add };
/** 引用模塊 **/
import { basicNum, add } from './math';
function test(ele) {
ele.textContent = add(99 + basicNum);
}
瀏覽器加載 ES6 模塊
瀏覽器加載 ES6 模塊,也使用上面代碼在網(wǎng)頁中插入一個(gè)模塊 foo.js,由于 type 屬性設(shè)為 module,所以瀏覽器知道這是一個(gè) ES6 模塊。
瀏覽器對于帶有 type="module"的
使用 Web 服務(wù)器打開首頁 index.html 文件
開班時(shí)間:2021-04-12(深圳)
開班盛況開班時(shí)間:2021-05-17(北京)
開班盛況開班時(shí)間:2021-03-22(杭州)
開班盛況開班時(shí)間:2021-04-26(北京)
開班盛況開班時(shí)間:2021-05-10(北京)
開班盛況開班時(shí)間:2021-02-22(北京)
開班盛況開班時(shí)間:2021-07-12(北京)
預(yù)約報(bào)名開班時(shí)間:2020-09-21(上海)
開班盛況開班時(shí)間:2021-07-12(北京)
預(yù)約報(bào)名開班時(shí)間:2019-07-22(北京)
開班盛況Copyright 2011-2023 北京千鋒互聯(lián)科技有限公司 .All Right 京ICP備12003911號-5 京公網(wǎng)安備 11010802035720號