模塊化

主要解決

避免全局污染,解決命名沖突

JS設(shè)計(jì)初衷

JS誕生的時(shí)候,僅僅是為了實(shí)現(xiàn)網(wǎng)頁表單的本地校驗(yàn)和簡(jiǎn)單的DOM操作,并沒有模塊化的規(guī)范設(shè)計(jì)

隨著前端越來越豐富,一些問題隨之而來,于是模塊化才漸漸的趨于規(guī)范化

原始JS命名沖突

張三寫了一個(gè)腳本tab.js,李四寫了一個(gè)腳本index.js,如果命名有沖突,就會(huì)相互覆蓋

// 張三
var name = "產(chǎn)品";
// 李四
var name = "首頁";

前綴是之前變量沖突很好的解決方案,但是缺點(diǎn)一是命名不規(guī)范,缺點(diǎn)二是有些人的名稱簡(jiǎn)寫相同

// 張三
var zs_name = "產(chǎn)品";
// 李四
var ls_name = "首頁";

這時(shí)候,模塊化的思想就漸漸開始形成,比如模塊化演變一:命名空間

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),然后前端模塊化進(jìn)入大爆發(fā)時(shí)代,未來的趨勢(shì)肯定是ES6的標(biāo)準(zhǔn)方案會(huì)逐漸統(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)了很多實(shí)現(xiàn)AMD規(guī)范的插件,現(xiàn)在以requireJS(https://requirejs.org/)為例

requireJS出了個(gè)rJS專門針對(duì)nodeJS的模塊化標(biāo)準(zhǔn),但是一般不會(huì)使用

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ù)時(shí),得到模塊的相對(duì)路徑,生成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;
};

運(yùn)行動(dòng)態(tài)添加的腳本語言,執(zhí)行模塊的define函數(shù),參數(shù)push到全局隊(duì)列

// 源碼截取
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ù),取出隊(duì)列作為參數(shù)

define函數(shù)的參數(shù)

語法:\color{red}{define(name?:string, dependencies?:string [], callback)}

name:模塊名稱,如果沒有命名則為引入腳本的名字。如果寫了name,模塊名必須是頂級(jí)的和絕對(duì)的(不允許用相對(duì)名字)

dependencies:依賴項(xiàng)

callback:回調(diào)函數(shù),返回模塊內(nèi)容

define有amd屬性, 它的值是一個(gè)對(duì)象,這是為了規(guī)范編程規(guī)則,可以防止與現(xiàn)有定義了define函數(shù)但不遵從AMD編程接口的代碼相沖突

define.amd = {
    jQuery: true
};

怎么判斷一個(gè)庫支不支持Amd標(biāo)準(zhǔn),判斷define是全局函數(shù),且define有amd屬性,比如jquery

if (typeof define === "function" && define.amd) {
    define("jquery", [], function () {
        return jQuery;
    });
}

define可以引入依賴模塊,依賴模塊的地址可以用require.config進(jìn)行路徑映射(見下)

define(['jquery'], function ($) {
    'use strict';
    $.get("/user/info", (res) => {
        console.log(res);
    })
});

define的標(biāo)準(zhǔn)寫法是使用exports一個(gè)對(duì)象作為返回對(duì)象,如果沒有exports,會(huì)以函數(shù)的返回值作為返回對(duì)象

define("moduleA", ["exports"], (exports) => {
    exports.moduleA = { name: "產(chǎn)品" }
})

使用exports導(dǎo)出的對(duì)象的格式變?yōu)閧 moduleA: {name: "產(chǎn)品"} },而不是{name: "產(chǎn)品"},如果想實(shí)現(xiàn)return效果可以使用解構(gòu)

require(["moduleA", "moduleB"], function ({ moduleA }, moduleB) {
    console.log(moduleA);
    console.log(moduleB);
})

defined還有一種寫法,引入require模塊后,使用require引入其他模塊。依賴模塊非常多時(shí),這種寫法比較簡(jiǎn)潔

define("moduleA", ["require", "jquery"], (require) => {
    let $ = require("jquery");
    console.log($.fn);
    return { name: "產(chǎn)品" };
})
require函數(shù)

require可以有一個(gè)config屬性,可以配置模塊路徑

require.config({
    paths: {
        jquery: "../lib/jquery.js"
    }
});

CMD標(biāo)準(zhǔn)

sea.js(國(guó)產(chǎn))在推廣過程中,逐漸形成了CMD規(guī)范,跟AMD比較類似,并且兼容CommonJS的模塊寫法,但是目前已不再維護(hù)

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),差點(diǎn)統(tǒng)一模塊化天下

模塊寫法

module.exports = {
    name: "首頁"
}

在CommonJS規(guī)范中,module.exports和exports相等,所以module.exports可以簡(jiǎn)寫為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,則引入時(shí),可以用以下方式

 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一個(gè)頁面只有有一個(gè),export可以有多個(gè),可共存)

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è)對(duì)象接收,且不支持解構(gòu)

import { name, version } from "./moduleA"
import info from "./moduleA"

默認(rèn)export導(dǎo)出的內(nèi)容無法用對(duì)象接收,export default導(dǎo)出的內(nèi)容無法解構(gòu),如果希望export導(dǎo)出的內(nèi)容也能用對(duì)象接收,可以用關(guān)鍵字as申明別名

// 聲明
let name = "首頁";
let version = "2.0.7";

export {
    name, version
}

// 接收
import * as info from "./moduleA"
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容