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