主要解決
避免全局污染较性,解決命名沖突
JS設(shè)計初衷
JS誕生的時候呵晚,僅僅是為了實現(xiàn)網(wǎng)頁表單的本地校驗和簡單的DOM操作瘩欺,并沒有模塊化的規(guī)范設(shè)計
隨著前端越來越豐富必盖,一些問題隨之而來拌牲,于是模塊化才漸漸的趨于規(guī)范化
原始JS命名沖突
張三寫了一個腳本tab.js,李四寫了一個腳本index.js歌粥,如果命名有沖突塌忽,就會相互覆蓋
// 張三
var name = "產(chǎn)品";
// 李四
var name = "首頁";
前綴是之前變量沖突很好的解決方案,但是缺點一是命名不規(guī)范失驶,缺點二是有些人的名稱簡寫相同
// 張三
var zs_name = "產(chǎn)品";
// 李四
var ls_name = "首頁";
這時候土居,模塊化的思想就漸漸開始形成,比如模塊化演變一:命名空間
var tab = {
name:"產(chǎn)品"
}
var index = {
name:"首頁"
}
模塊化演變二:局部作用域
function tab () {
var name = "產(chǎn)品";
}
function index () {
var name = "首頁";
}
模塊化演變?nèi)鹤詧?zhí)行函數(shù)(已很好的隔離作用域嬉探,很多非標(biāo)準(zhǔn)模塊化的插件擦耀,腳本都是以這種形式書寫)
;(function () {
var name = "產(chǎn)品";
})();
;(function () {
var name = "首頁";
})();
最終演變
模塊化演變到現(xiàn)在出現(xiàn)了AMD,CMD涩堤,CommonJS(node方案)等模塊化標(biāo)準(zhǔn)眷蜓,然后前端模塊化進入大爆發(fā)時代,未來的趨勢肯定是ES6的標(biāo)準(zhǔn)方案會逐漸統(tǒng)一胎围,但是AMD吁系,CMD、CommonJS和ES6的的標(biāo)準(zhǔn)方案相差不大
AMD標(biāo)準(zhǔn)
中文API地址https://github.com/amdjs/amdjs-api/wiki/AMD-(%E4%B8%AD%E6%96%87%E7%89%88)
模塊化逐步演變的過程中痊远,AMD規(guī)范一直站前端主導(dǎo)地位垮抗,出現(xiàn)了很多實現(xiàn)AMD規(guī)范的插件,現(xiàn)在以requireJS(https://requirejs.org/)為例
requireJS出了個rJS專門針對nodeJS的模塊化標(biāo)準(zhǔn)碧聪,但是一般不會使用
hello
在html頁面中引入
<!-- src是為了引入腳本冒版,data-main設(shè)置入口文件 -->
<script src="./lib/require2.3.6.min.js" data-main="lib/main"></script>
lib/main.js
require(["moduleA", "moduleB"], function (moduleA, moduleB) {
console.log(moduleA);
console.log(moduleB);
})
lib/moduleA.js
define(() => ({
name: "產(chǎn)品"
}));
lib/moduleB.js
define(() => ({
name: "首頁"
}));
源碼解析(非完全)
執(zhí)行require函數(shù)時,得到模塊的相對路徑逞姿,生成script腳本辞嗡,比如:<script async src="加載的模塊地址"></script>,并添加到head標(biāo)簽中滞造,添加偵聽腳本加載完成事件
// 源碼截取
req.createNode = function (config, moduleName, url) {
var node = config.xhtml ?
document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') :
document.createElement('script');
node.type = config.scriptType || 'text/javascript';
node.charset = 'utf-8';
node.async = true;
return node;
};
運行動態(tài)添加的腳本語言续室,執(zhí)行模塊的define函數(shù),參數(shù)push到全局隊列
// 源碼截取
define = function (name, deps, callback) {
// 很多省略代碼
if (context) {
context.defQueue.push([name, deps, callback]);
context.defQueueMap[name] = true;
} else {
globalDefQueue.push([name, deps, callback]);
}
};
執(zhí)行require的回調(diào)函數(shù)谒养,取出隊列作為參數(shù)
define函數(shù)的參數(shù)
語法:
name:模塊名稱挺狰,如果沒有命名則為引入腳本的名字。如果寫了name买窟,模塊名必須是頂級的和絕對的(不允許用相對名字)
dependencies:依賴項
callback:回調(diào)函數(shù)丰泊,返回模塊內(nèi)容
define有amd屬性, 它的值是一個對象始绍,這是為了規(guī)范編程規(guī)則瞳购,可以防止與現(xiàn)有定義了define函數(shù)但不遵從AMD編程接口的代碼相沖突
define.amd = {
jQuery: true
};
怎么判斷一個庫支不支持Amd標(biāo)準(zhǔn),判斷define是全局函數(shù)亏推,且define有amd屬性学赛,比如jquery
if (typeof define === "function" && define.amd) {
define("jquery", [], function () {
return jQuery;
});
}
define可以引入依賴模塊年堆,依賴模塊的地址可以用require.config進行路徑映射(見下)
define(['jquery'], function ($) {
'use strict';
$.get("/user/info", (res) => {
console.log(res);
})
});
define的標(biāo)準(zhǔn)寫法是使用exports一個對象作為返回對象,如果沒有exports盏浇,會以函數(shù)的返回值作為返回對象
define("moduleA", ["exports"], (exports) => {
exports.moduleA = { name: "產(chǎn)品" }
})
使用exports導(dǎo)出的對象的格式變?yōu)閧 moduleA: {name: "產(chǎn)品"} }变丧,而不是{name: "產(chǎn)品"},如果想實現(xiàn)return效果可以使用解構(gòu)
require(["moduleA", "moduleB"], function ({ moduleA }, moduleB) {
console.log(moduleA);
console.log(moduleB);
})
defined還有一種寫法,引入require模塊后绢掰,使用require引入其他模塊锄贷。依賴模塊非常多時,這種寫法比較簡潔
define("moduleA", ["require", "jquery"], (require) => {
let $ = require("jquery");
console.log($.fn);
return { name: "產(chǎn)品" };
})
require函數(shù)
require可以有一個config屬性曼月,可以配置模塊路徑
require.config({
paths: {
jquery: "../lib/jquery.js"
}
});
CMD標(biāo)準(zhǔn)
sea.js(國產(chǎn))在推廣過程中,逐漸形成了CMD規(guī)范柔昼,跟AMD比較類似哑芹,并且兼容CommonJS的模塊寫法,但是目前已不再維護
CMD推崇的是就近依賴捕透,AMD則默認(rèn)約束模塊一開始就聲明相關(guān)依賴聪姿,其他定義方式及模塊相關(guān)變量都很相似
CMD寫法
define((require, exports, module) => {
// 模塊代碼
})
CommonJS標(biāo)準(zhǔn)
這是nodeJS的模塊化標(biāo)準(zhǔn),致力于前后端統(tǒng)一的模塊化標(biāo)準(zhǔn)乙嘀,差點統(tǒng)一模塊化天下
模塊寫法
module.exports = {
name: "首頁"
}
在CommonJS規(guī)范中末购,module.exports和exports相等,所以module.exports可以簡寫為exports
exports = {
name: "首頁"
}
模塊引入虎谢,不加后綴表示js文件(注意盟榴,CMD和AMD如果和入口文件在同一目錄下,可以省略./婴噩,但是CommonJS不行)
let moduleA = require("./moduleA");
console.log(moduleA);
模塊引入可解構(gòu)
let { name } = require("./moduleA");
console.log(name);
可引入內(nèi)置文件(Node自帶功能)
const path = require("path");
引入node_modules文件夾的內(nèi)容擎场,也可以直接寫名稱,比如:cnpm i jquery几莽,則引入時迅办,可以用以下方式
const jquery = require("jquery");
ES6模塊化標(biāo)準(zhǔn)
未來模塊化的一統(tǒng)標(biāo)準(zhǔn)
定義模塊
模塊寫法一
export var a = 10;
export var f = function () {
console.log(1);
}
模塊寫法二
var a = 10;
var f = function () {
console.log(1);
}
export { a, f }
模塊寫法三(export default一個頁面只有有一個,export可以有多個章蚣,可共存)
export default {
name: "首頁"
}
模塊寫法可以使用別名
function v1() { }
function v2() { }
export {
v1 as streamV1,
v2 as streamV2
}
引入模塊
假如有如下定義模塊
let name = "首頁";
let version = "2.0.7";
export {
name, version
}
export default {
name, version
}
引入處理
使用export導(dǎo)出的模塊站欺,必須用import并用{ }來接收
export default導(dǎo)出,比如用一個對象接收纤垂,且不支持解構(gòu)
import { name, version } from "./moduleA"
import info from "./moduleA"
默認(rèn)export導(dǎo)出的內(nèi)容無法用對象接收矾策,export default導(dǎo)出的內(nèi)容無法解構(gòu),如果希望export導(dǎo)出的內(nèi)容也能用對象接收洒忧,可以用關(guān)鍵字as申明別名
// 聲明
let name = "首頁";
let version = "2.0.7";
export {
name, version
}
// 接收
import * as info from "./moduleA"