最新文章專題視頻專題問答1問答10問答100問答1000問答2000關(guān)鍵字專題1關(guān)鍵字專題50關(guān)鍵字專題500關(guān)鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關(guān)鍵字專題關(guān)鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
當(dāng)前位置: 首頁 - 科技 - 知識百科 - 正文

探索webpack模塊及webpack3新特性

來源:懂視網(wǎng) 責(zé)編:小采 時間:2020-11-27 22:29:43
文檔

探索webpack模塊及webpack3新特性

探索webpack模塊及webpack3新特性:本文從簡單的例子入手,從打包文件去分析以下三個問題:webpack打包文件是怎樣的?如何做到兼容各大模塊化方案的?webpack3帶來的新特性又是什么? 一個簡單的例子 webpack配置 // webpack.config.js module.exports = { entry: '.
推薦度:
導(dǎo)讀探索webpack模塊及webpack3新特性:本文從簡單的例子入手,從打包文件去分析以下三個問題:webpack打包文件是怎樣的?如何做到兼容各大模塊化方案的?webpack3帶來的新特性又是什么? 一個簡單的例子 webpack配置 // webpack.config.js module.exports = { entry: '.

本文從簡單的例子入手,從打包文件去分析以下三個問題:webpack打包文件是怎樣的?如何做到兼容各大模塊化方案的?webpack3帶來的新特性又是什么?

一個簡單的例子

webpack配置

 // webpack.config.js
module.exports = {
 entry: './src/index.js',
 output: {
 filename: 'bundle.js',
 path: path.resolve(__dirname, 'dist')
 },
};

簡單的js文件

 // src/index.js
 console.log('hello world');

webpack打包后的代碼

一看你就會想,我就一行代碼,你給我打包那么多???(黑人問號)

// dist/bundle.js
 /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {
console.log('hello world');
/***/ })
/******/ ]);

我們來分析一下這部分代碼,先精簡一下,其實(shí)整體就是一個自執(zhí)行函數(shù),然后傳入一個模塊數(shù)組

 (function(modules) { 
 //...
 })([function(module, exports) {
 //..
 }])

好了,傳入模塊數(shù)組做了什么(其實(shí)注釋都很明顯了,我只是大概翻譯一下)

 /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache 緩存已經(jīng)load過的模塊
/******/ var installedModules = {};
/******/
/******/ // The require function 引用的函數(shù)
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache 假如在緩存里就直接返回
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache) 構(gòu)造一個模塊并放入緩存
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId, //模塊id
/******/ l: false, // 是否已經(jīng)加載完畢
/******/ exports: {} // 對外暴露的內(nèi)容
/******/ };
/******/
/******/ // Execute the module function 傳入模塊參數(shù),并執(zhí)行模塊
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded 標(biāo)記模塊已經(jīng)加載完畢
/******/ module.l = true;
/******/
/******/ // Return the exports of the module 返回模塊暴露的內(nèi)容
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__) 暴露模塊數(shù)組
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache 暴露緩存數(shù)組
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports 為ES6 exports定義getter
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) { // 假如exports本身不含有name這個屬性
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules 解決ES module和Common js module的沖突,ES則返回module['default']
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__ webpack配置下的公共路徑
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports 最后執(zhí)行entry模塊并且返回它的暴露內(nèi)容
/******/ return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {
console.log('hello world');
/***/ })
/******/ ]);

整體流程是怎樣的呢

  • 傳入module數(shù)組
  • 調(diào)用__webpack_require__(__webpack_require__.s = 0)
  • 構(gòu)造module對象,放入緩存

    調(diào)用module,傳入相應(yīng)參數(shù)modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); (這里exports會被函數(shù)內(nèi)部的東西修改)

    標(biāo)記module對象已經(jīng)加載完畢

    返回模塊暴露的內(nèi)容(注意到上面函數(shù)傳入了module.exports,可以對引用進(jìn)行修改)

  • 模塊函數(shù)中傳入module, module.exports, __webpack_require__
  • 執(zhí)行過程中通過對上面三者的引用修改,完成變量暴露和引用
  • webpack模塊機(jī)制是怎樣的

    我們可以去官網(wǎng)看下webpack模塊

    doc.webpack-china.org/concepts/mo…

    webpack 模塊能夠以各種方式表達(dá)它們的依賴關(guān)系,幾個例子如下:

  • ES2015 import 語句
  • CommonJS require() 語句
  • AMD define 和 require 語句
  • css/sass/less 文件中的 @import 語句。
  • 樣式(url(...))或 HTML 文件()中的圖片鏈接(image url)
  • 強(qiáng)大的webpack模塊可以兼容各種模塊化方案,并且無侵入性(non-opinionated)

    我們可以再編寫例子一探究竟

    CommonJS

    修改src/index.js

    var cj = require('./cj.js');
    console.log('hello world');
    cj();

    新增src/cj.js,保持前面例子其他不變

    // src/cj.js
    function a() {
     console.log("CommonJS");
    }
    module.exports = a;

    再次運(yùn)行webpack

    /******/ (function(modules) { // webpackBootstrap
     //... 省略代碼
    /******/ })
    /************************************************************************/
    /******/ ([
    /* 0 */
    /***/ (function(module, exports, __webpack_require__) {
    let cj = __webpack_require__(1);
    console.log('hello world');
    cj();
    /***/ }),
    /* 1 */
    /***/ (function(module, exports) {
    function a() {
     console.log("CommonJS");
    }
    module.exports = a;
    /***/ })
    /******/ ]);

    我們可以看到模塊數(shù)組多了個引入的文件,然后index.js模塊函數(shù)多了個參數(shù)__webpack_require__,去引用文件(__webpack_require__在上一節(jié)有介紹),整體上就是依賴的模塊修改了module.exports,然后主模塊執(zhí)行依賴模塊,獲取exports即可

    ES2015 import

    新增src/es.js

    // src/es.js
    export default function b() {
     console.log('ES Modules');
    }

    修改src/index.js

    // src/index.js
    import es from './es.js';
    console.log('hello world');
    es();
    webpack.config.js不變,執(zhí)行webpack
    /******/ (function(modules) { // webpackBootstrap
    // ... 省略代碼
    /******/ })
    /************************************************************************/
    /******/ ([
    /* 0 */
    /***/ (function(module, __webpack_exports__, __webpack_require__) {
    "use strict";
    Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
    /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__es_js__ = __webpack_require__(1);
    console.log('hello world');
    Object(__WEBPACK_IMPORTED_MODULE_0__es_js__["a" /* default */])();
    /***/ }),
    /* 1 */
    /***/ (function(module, __webpack_exports__, __webpack_require__) {
    "use strict";
    /* harmony export (immutable) */ __webpack_exports__["a"] = b;
    function b() {
     console.log('ES Modules');
    }
    /***/ })
    /******/ ]);

    我們可以看到它們都變成了嚴(yán)格模式,webpack自動采用的

    表現(xiàn)其實(shí)跟CommonJS相似,也是傳入export然后修改,在主模塊再require進(jìn)來,

    我們可以看到這個

    Object.defineProperty(__webpack_exports__, "__esModule", { value: true });

    這個干嘛用的?其實(shí)就是標(biāo)記當(dāng)前的exports是es模塊,還記得之前的__webpack_require__.n嗎,我們再拿出來看看

    /******/ // getDefaultExport function for compatibility with non-harmony modules 解決ES module和Common js module的沖突,ES則返回module['default']
    /******/ __webpack_require__.n = function(module) {
    /******/ var getter = module && module.__esModule ?
    /******/ function getDefault() { return module['default']; } :
    /******/ function getModuleExports() { return module; };
    /******/ __webpack_require__.d(getter, 'a', getter);
    /******/ return getter;
    /******/ };

    為了避免跟非ES Modules沖突?沖突在哪里呢?

    其實(shí)這部分如果你看到babel轉(zhuǎn)換ES Modules源碼就知道了,為了兼容模塊,會把ES Modules直接掛在exports.default上,然后加上__esModule屬性,引入的時候判斷一次是否是轉(zhuǎn)換模塊,是則引入module['default'],不是則引入module

    我們再多引入幾個ES Modules看看效果

    // src/es.js
    export function es() {
     console.log('ES Modules');
    }
    export function esTwo() {
     console.log('ES Modules Two');
    }
    export function esThree() {
     console.log('ES Modules Three');
    }
    export function esFour() {
     console.log('ES Modules Four');
    }

    我們多引入esTwo和esFour,但是不使用esFour

    // src/index.js
    import { es, esTwo, esFour} from './es.js';
    console.log('hello world');
    es();
    esTwo();

    得出

    /******/ (function(modules) { // webpackBootstrap
    // ...
    /******/ })
    /************************************************************************/
    /******/ ([
    /* 0 */
    /***/ (function(module, __webpack_exports__, __webpack_require__) {
    "use strict";
    Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
    /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__es_js__ = __webpack_require__(1);
    console.log('hello world');
    Object(__WEBPACK_IMPORTED_MODULE_0__es_js__["a" /* es */])();
    Object(__WEBPACK_IMPORTED_MODULE_0__es_js__["b" /* esTwo */])();
    /***/ }),
    /* 1 */
    /***/ (function(module, __webpack_exports__, __webpack_require__) {
    "use strict";
    /* harmony export (immutable) */ __webpack_exports__["a"] = es;
    /* harmony export (immutable) */ __webpack_exports__["b"] = esTwo;
    /* unused harmony export esThree */
    /* unused harmony export esFour */
    function es() {
     console.log('ES Modules');
    }
    function esTwo() {
     console.log('ES Modules Two');
    }
    function esThree() {
     console.log('ES Modules Three');
    }
    function esFour() {
     console.log('ES Modules Four');
    }
    /***/ })
    /******/ ]);

    嗯嗯其實(shí)跟前面是一樣的,舉出這個例子重點(diǎn)在哪里呢,有沒有注意到注釋中

    /* unused harmony export esThree */
    /* unused harmony export esFour */

    esThree是我們沒有引入的模塊,esFour是我們引用但是沒有使用的模塊,webpack均對它們做了unused的標(biāo)記,其實(shí)這個如果你使用了webpack插件uglify,通過標(biāo)記,就會把esThree和esFour這兩個未使用的代碼消除(其實(shí)它就是tree-shaking)

    AMD

    我們再來看看webpack怎么支持AMD

    新增src/amd.js

    // src/amd.js
    define([
    ],function(){
     return {
     amd:function(){
     console.log('AMD');
     }
     };
    });

    修改index.js

    // src/index.js
    define([
     './amd.js'
    ],function(amdModule){
     amdModule.amd();
    });

    得到

    /******/ (function(modules) { // webpackBootstrap
    // ... 省略代碼
    /******/ })
    /************************************************************************/
    /******/ ([
    /* 0 */
    /***/ (function(module, exports, __webpack_require__) {
    var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [
     __webpack_require__(1)
    ], __WEBPACK_AMD_DEFINE_RESULT__ = function(amdModule){
     amdModule.amd();
    }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
     __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
    /***/ }),
    /* 1 */
    /***/ (function(module, exports, __webpack_require__) {
    var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [
    ], __WEBPACK_AMD_DEFINE_RESULT__ = function(){
     return {
     amd:function(){
     console.log('AMD');
     }
     };
    }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
     __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
    /***/ })
    /******/ ]);

    先看amd.js整理一下代碼

    function(module, exports, __webpack_require__) {
     var __WEBPACK_AMD_DEFINE_ARRAY__,
     __WEBPACK_AMD_DEFINE_RESULT__;
     !(
     __WEBPACK_AMD_DEFINE_ARRAY__ = [],
     __WEBPACK_AMD_DEFINE_RESULT__ = function() {
     return {
     amd: function() {
     console.log('AMD');
     }
     };
     }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
     __WEBPACK_AMD_DEFINE_RESULT__ !== undefined &&
     (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)
     );
    })

    簡單來講收集define Array然后置入返回函數(shù),根據(jù)參數(shù)獲取依賴

    apply對數(shù)組拆解成一個一個參數(shù)

    再看index.js模塊部分

    function(module, exports, __webpack_require__) {
     var __WEBPACK_AMD_DEFINE_ARRAY__,
     __WEBPACK_AMD_DEFINE_RESULT__;
     !(
     __WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(1)],
     __WEBPACK_AMD_DEFINE_RESULT__ = function(amdModule) {
     amdModule.amd();
     }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
     __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && 
     (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)
     );
    }

    其實(shí)就是引入了amd.js暴露的{amd:[Function: amd]}

    css/image?

    css和image也可以成為webpack的模塊,這是令人震驚的,這就不能通過普通的hack commonjs或者函數(shù)調(diào)用簡單去調(diào)用了,這就是anything to JS,它就需要借助webpack loader去實(shí)現(xiàn)了

    像css就是轉(zhuǎn)換成一段js代碼,通過處理,調(diào)用時就是可以用js將這段css插入到style中,image也類似,這部分就不詳細(xì)闡述了,有興趣的讀者可以深入去研究

    webpack3新特性

    我們可以再順便看下webpack3新特性的表現(xiàn)

    具體可以看這里medium.com/webpack/web…

    Scope Hoisting

    我們可以發(fā)現(xiàn)模塊數(shù)組是一個一個獨(dú)立的函數(shù)然后閉包引用webpack主函數(shù)的相應(yīng)內(nèi)容,每個模塊都是獨(dú)立的,然后帶來的結(jié)果是在瀏覽器中執(zhí)行速度變慢,然后webpack3學(xué)習(xí)了Closure Compiler和RollupJS這兩個工具,連接所有閉包到一個閉包里,放入一個函數(shù),讓執(zhí)行速度更快,并且整體代碼體積也會有所縮小

    我們可以實(shí)際看一下效果(要注意的是這個特性只支持ES Modules,是不支持CommonJs和AMD的)

    使用上面的例子,配置webpack.config.js,增加new webpack.optimize.ModuleConcatenationPlugin()

    const path = require('path');
    const webpack = require('webpack');
    module.exports = {
     entry: './src/index.js',
     output: {
     filename: 'bundle.js',
     path: path.resolve(__dirname, 'dist')
     },
     module: {
     },
     plugins: [
     new webpack.optimize.ModuleConcatenationPlugin(),
     ]
    };

    打包

    /******/ (function(modules) { // webpackBootstrap
    // ... 省略代碼
    /******/ })
    /************************************************************************/
    /******/ ([
    /* 0 */
    /***/ (function(module, __webpack_exports__, __webpack_require__) {
    "use strict";
    Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
    // CONCATENATED MODULE: ./src/es.js
    function es() {
     console.log('ES Modules');
    }
    function esTwo() {
     console.log('ES Modules Two');
    }
    function esThree() {
     console.log('ES Modules Three');
    }
    function esFour() {
     console.log('ES Modules Four');
    }
    // CONCATENATED MODULE: ./src/index.js
    // src/index.js
    console.log('hello world');
    es();
    /***/ })
    /******/ ]);

    我們可以驚喜的發(fā)現(xiàn)沒有什么require了,它們拼接成了一個函數(shù),good!😃

    Magic Comments

    code splitting是webpack一個重點(diǎn)特性之一,涉及到要動態(tài)引入的時候,webpack可以使用 require.ensure去實(shí)現(xiàn),后來webpack2支持使用了符合 ECMAScript 提案 的 import() 語法,但是它有個不足之處,無法指定chunk的名稱chunkName,為了解決這個問題,出現(xiàn)了Magic Comments,支持用注釋的方式去指定,如下

    import(/* webpackChunkName: "my-chunk-name" */ 'module');

    小結(jié)

    webpack是一個強(qiáng)大的模塊打包工具,在處理依賴、模塊上都很優(yōu)秀,本文從bundle.js文件分析出發(fā)去探索了不同模塊方案的加載機(jī)制,初步去理解webpack,并且對webpack3特性進(jìn)行闡述,當(dāng)然,webpack還有很多地方需要去探索深究,敬請期待以后的文章吧~關(guān)于本文如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!

    聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

    文檔

    探索webpack模塊及webpack3新特性

    探索webpack模塊及webpack3新特性:本文從簡單的例子入手,從打包文件去分析以下三個問題:webpack打包文件是怎樣的?如何做到兼容各大模塊化方案的?webpack3帶來的新特性又是什么? 一個簡單的例子 webpack配置 // webpack.config.js module.exports = { entry: '.
    推薦度:
    標(biāo)簽: 模塊 新特性 探索
    • 熱門焦點(diǎn)

    最新推薦

    猜你喜歡

    熱門推薦

    專題
    Top