溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

webpack4.0各個擊破(5)—— Module篇

發(fā)布時間:2020-07-26 00:07:20 來源:網(wǎng)絡 閱讀:2795 作者:大史不說話 欄目:開發(fā)技術

webpack4.0各個擊破(5)—— Module篇

webpack作為前端最火的構(gòu)建工具,是前端自動化工具鏈最重要的部分,使用門檻較高。本系列是筆者自己的學習記錄,比較基礎,希望通過問題 + 解決方式的模式,以前端構(gòu)建中遇到的具體需求為出發(fā)點,學習webpack工具中相應的處理辦法。(本篇中的參數(shù)配置及使用方式均基于webpack4.0版本

webpack4.0各個擊破(5)—— Module篇

使用webpack對腳本進行合并是非常方便的,因為webpack實現(xiàn)了對各種不同模塊規(guī)范的兼容處理,對前端開發(fā)者來說,理解這種實現(xiàn)方式比學習如何配置webpack更為重要,本節(jié)的內(nèi)容實用性較低。
webpack4.0各個擊破(5)—— Module篇

一. 模塊化亂燉

webpack4.0各個擊破(5)—— Module篇

腳本合并是基于模塊化規(guī)范的,javascript模塊化是一個非常混亂的話題,各種*MD】*規(guī)范亂飛還要外加一堆【.js】的規(guī)范實現(xiàn)?,F(xiàn)代化前端項目多基于框架進行開發(fā),較為流行的框架內(nèi)部基本已經(jīng)統(tǒng)一遵循ES6的模塊化標準,盡管支持度不一,但通過構(gòu)建工具可以解決瀏覽器支持滯后的問題;基于nodejs的服務端項目原生支持CommonJs標準;而開發(fā)中引入的一些工具類的庫,熱門的工具類庫為了能同時兼容瀏覽器和node環(huán)境,通常會使用UMD標準(Universal Module Definition) 來實現(xiàn)模塊化,對UMD范式不了解的讀者可以先閱讀《javascript基礎修煉(4)——UMD規(guī)范的代碼推演》一文,甚至有些第三方庫并沒有遵循任何模塊化方案。如果不借助構(gòu)建工具,想要對各類方案實現(xiàn)兼容是非常復雜的。

二. webpack與模塊化

webpack默認支持的是CommonJs規(guī)范,畢竟它是nodejs支持的模塊管理方式,而沒有node哪來的webpack。但同時為了擴展其使用場景,webpack在版本迭代中也加入了對ES harmony規(guī)范和AMD規(guī)范的兼容。

webpack如何識別CommonJs模塊

webpack打包后輸出文件的基本結(jié)構(gòu)是下面這個樣子的:

(function(modules) { // webpackBootstrap
    // 模塊緩存對象
    var installedModules = {};

    // webpack內(nèi)部的模塊引用函數(shù)
    function __webpack_require__(moduleId) {

        // 加載入口JS

        // 輸出
        return module.exports;
    }

    // 掛載模塊數(shù)組
    __webpack_require__.m = modules;
    // ...
    // 在__webpack_require__掛載多個屬性

    // 傳入入口JS模塊ID執(zhí)行函數(shù)并輸出模塊
    return __webpack_require__(__webpack_require__.s = 0);
});
// 包含所有模塊的數(shù)組
([
    /* id為0 */
    (function(module, exports) {
        console.log('1')
    })
]);

簡化以后實際上就是一個自執(zhí)行函數(shù):

(function(modules){
    return __webpack_require__(0);
}([Module0,Module1...]))

可以看到__webpack_reqruie__( )這個方法的參數(shù)就是模塊的唯一ID標識,返回值就是module.exports,所以webpack對于CommonJs規(guī)范是原生支持的。

webpack如何識別ES Harmony模塊

對于ES Harmony規(guī)范不熟悉的可以查看《ES6 Module語法》一文。

先使用import命令加載一個CommonJs規(guī)范導出的模塊,查看打包后的代碼可以看到模塊引用的部分被轉(zhuǎn)換成了下面這樣:

__webpack_require__.r(__webpack_exports__);
/* harmony import */ 
var _components_component10k_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./components/component10k.js");
/* harmony import */
var _components_component10k_js__WEBPACK_IMPORTED_MODULE_0___default = __webpack_require__.n(_components_component10k_js__WEBPACK_IMPORTED_MODULE_0__);

簡化一下再來看:

__webpack_require__.r(__webpack_exports__);
var a = __webpack_require__("./components/component10k.js");
var b = __webpack_require__.n(a);

這里涉及到兩個工具函數(shù):

webpack4.0各個擊破(5)—— Module篇

這個方法是給模塊的exports對象加上ES Harmony規(guī)范的標記,如果支持Symbol對象,則為exports對象的Symbol.toStringTag屬性賦值Module,這樣做的結(jié)果是exports對象在調(diào)用toString方法時會返回'Module'(筆者并沒有查到這種寫法的緣由);如果不支持Symbol對象,則將exports.__esModule賦值為true。

另一個工具函數(shù)是:

webpack4.0各個擊破(5)—— Module篇

傳入了一個模塊,返回一個getter方法,此處是一個高階函數(shù)的應用,實現(xiàn)的功能是當模塊的__esModule屬性為真時,返回一個getDefault( )方法,否則返回getModuleExports( )方法.

回過頭再來看上面的簡化代碼:

// 添加ES Harmony規(guī)范模塊標記
__webpack_require__.r(__webpack_exports__);
// a實際上得到了模塊通過module.exports輸出的對象
var a = __webpack_require__("./components/component10k.js");
// 根據(jù)a的模塊化規(guī)范類型返回不同的getter函數(shù),當getter函數(shù)執(zhí)行時才會真正得到模塊對象
var b = __webpack_require__.n(a);

總結(jié)一下,webpack所做的處理相當于對模塊增加了代理,如果被加載模塊符合ES Harmony規(guī)范,則返回module['default'],否則返回module。這里的module泛指模塊輸出的對象。

再使用import加載一個使用export語法輸出的ES Harmony模塊,查看打包結(jié)果中的模塊文件可以看到:

//component10k.js模塊文件在main.bundle.js中的內(nèi)容
__webpack_require__.r(__webpack_exports__);
__webpack_exports__["default"] = (function(){
    Array.from('component10k');
})

可以看到輸出的內(nèi)容直接綁定到了輸出模塊的default屬性上,由于這個模塊被打上了__esModule的標記,所以引用它的模塊會通過module['default']來取用其內(nèi)容,也就正好命中了模塊的輸出內(nèi)容。

webpack如何識別AMD模塊

我們將component10k.js模塊改為用AMD規(guī)范定義:

define(function(){
    console.log('test');
})

查看經(jīng)過webpack打包后,這個模塊變成了如下的樣子:

var __WEBPACK_AMD_DEFINE_RESULT__;
!(__WEBPACK_AMD_DEFINE_RESULT__ = (function(){
    console.log('test');
}).call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));

簡化一下:

var result;
!(result=(function(){}).call(...),result!==undefined && module.exports = result);

抽象一下:

var result;
!(expression1,expression2 && expression3)

這里涉及的javascript的基本知識較多,逗號表達式的優(yōu)先級最低,所以最后參與運算,逗號表達式會從左到右依次執(zhí)行語句,并返回最后一個表達式的結(jié)果,&&為短路運算語法,即前一個條件成立時才計算后面的表達式,賦值語句執(zhí)行完后會將所賦的值返回。此處外層的!(expression )語法起了什么作用,筆者也沒看懂,希望了解的讀者多多指教。

所以,webpack對于AMD模塊的處理,實際上是加了一層封裝,將模塊運行的結(jié)果掛載到了webpack模塊的module.exports對象上。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI