webpack 筆記

參考連接:webpack打包分析javascript

webpack打包文件分析

commonjs規範打包文件分析

  • 源文件java

    // index.js
    const util = require('./util');
    console.log(util.hello())
    // util.js
    function hello () {
      return 'hello'
    }
    function bye () {
      return 'bye'
    }
    module.exports = {
        hello,
        bye
    }
  • 能夠看到webpack打包生成是一個當即執行函數,modules參數是各個模塊的代碼, 其中/* 1 */對應的是index.js,/* 2 */對應的是util.js,/* 0 */是執行1的主模塊文件,能夠看到模塊函數有三個參數moduleexports__webpack_reuire__,這些都是在當即執行函數內部傳遞的

    clipboard.png

  • 當即函數的內部主要定義了__webpack_require__函數及其屬性,最後一句話return __webpack_require__((__webpack_require__.s = 0));表示執行moduleId爲0的模塊

    clipboard.png

  • __webpack_require__函數中緩存已經調用的模塊,初始化exports等參數並將這些參數傳入module的執行中webpack

    var installedModules = {}; // 緩存已引入的模塊
    function __webpack_require__(moduleId) {
        if (installedModules[moduleId]) { //直接返回引入模塊
          return installedModules[moduleId].exports;
        }
        //初始化模塊
        var module = (installedModules[moduleId] = {
          i: moduleId,
          l: false,
          exports: {}
        });
        // 執行對應的模塊
        modules[moduleId].call(
          module.exports,
          module,
          module.exports,
          __webpack_require__
        );
        // 表示模塊已經加載
        module.l = true;
        return module.exports;
    }
    // 掛載模塊
    __webpack_require__.m = modules;
    
    // 掛載緩存
    __webpack_require__.c = installedModules;
    
    // 實際是Object.prototype.hasOwnProperty
    __webpack_require__.o = function(object, property) {
        return Object.prototype.hasOwnProperty.call(object, property);
    };
    // 定義對象取值的函數
    __webpack_require__.d = function(exports, name, getter) {
        if (!__webpack_require__.o(exports, name)) {
          Object.defineProperty(exports, name, {
            configurable: false,
            enumerable: true,
            get: getter
          });
        }
      };
    // 判斷是否爲es模塊,若是是則默認返回module['default'],不然返回module
    __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;
     };

小結

- webpack打包文件是一個當即執行函數IIFE
- 模塊文件在外包裹了一層函數,以數組的形式做爲參數傳入當即執行函數中
- IIFE內部有緩存已執行函數的機制,第二次執行直接返回exports
- IIFE 最後執行id爲0的模塊`__webpack_require__((__webpack_require__.s = 0));`將整個modules執行串聯起來

ES6模塊使用打包分析

  • 源文件
// // index.js
import addCounter from './util'

addCounter()
//util.js
let counter = 3;
export default function addCounter () {
  return counter++
}
// bundle.js
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__util__ = __webpack_require__(2);
// // index.js
Object(__WEBPACK_IMPORTED_MODULE_0__util__["a" /* default */])()

/***/ }),
/* 2 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (immutable) */ __webpack_exports__["a"] = addCounter;
//util.js
let counter = 3;
function addCounter () {
  return counter++
}
  • 在ES6打包中,引入其餘模塊文件的index.js有這樣一句代碼Object.defineProperty(__webpack_exports__, "__esModule", { value: true });標記當前引入import文件爲es6模塊。
  • 結合前面IIFE函數中的__webpack_require__.n__esModule爲true是,__webpack_require__.d(getter, "a", getter);獲取a的是module.default的值,上面打包文件中Object(__WEBPACK_IMPORTED_MODULE_0__util__["a" /* default */])()體現了這一點,保證 counter 變量取的是值的引用。

ES6和cjs值傳遞的區別

  • CommonJS 輸出是值的拷貝,ES6模塊輸出的是值的引用
  • es6值引用源文件git

    // // index.js
    import { counter, addCounter} from './util'
    console.log(counter);
    addCounter()
    console.log(counter);
    
    //util.js
    export let counter = 3;
    export function addCounter () {
      return counter++
    }
    // bundle.js
    /* 1 */
    /***/ (function(module, __webpack_exports__, __webpack_require__) {
    
    "use strict";
    Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
    /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__util__ = __webpack_require__(2);
    // // index.js
    
    console.log(__WEBPACK_IMPORTED_MODULE_0__util__["b" /* counter */]); // 3
    Object(__WEBPACK_IMPORTED_MODULE_0__util__["a" /* addCounter */])() 
    console.log(__WEBPACK_IMPORTED_MODULE_0__util__["b" /* counter */]); // 4
    
    /***/ }),
    /* 2 */
    /***/ (function(module, __webpack_exports__, __webpack_require__) {
    
    "use strict";
    /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return counter; });
    /* harmony export (immutable) */ __webpack_exports__["a"] = addCounter;
    //util.js
    let counter = 3;
    function addCounter () {
      return counter++
    }
    
    /***/ })
  • /* 2 */中counter(「b」)的定義使用了__webpack_require__.d方法,也就是直接賦值在exports的屬性上,import時獲取的也就是引用值了
  • cjs值引用源文件es6

    // index.js
    const {counter, addCounter} = require('./util');
    console.log(counter);
    addCounter()
    console.log(counter);
    
    //util.js
    let counter = 3;
    function addCounter () {
        counter = counter+1
    }
    module.exports = {
        counter,
        addCounter
    }
    // bundle.js
    /* 1 */
    /***/ (function(module, exports, __webpack_require__) {
    // index.js
    const {counter, addCounter} = __webpack_require__(2);
    console.log(counter); // 3
    addCounter()
    console.log(counter); //3
    
    
    /***/ }),
    /* 2 */
    /***/ (function(module, exports) {
    
    let counter = 3;
    function addCounter () {
        counter = counter+1
    }
    module.exports = {
        counter,
        addCounter
    }

混合cjs和es6打包分析

import引入cjs模塊

//index.js
import { bye } from './bye'
import hello from './hello'

bye();
hello();

//hello
function hello() {  
    console.log('hello')
}
module.exports = hello;

//bye.js
function bye() {  
    console.log('bye')
}
module.exports = {
    bye
};
// bundle.js
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__bye__ = __webpack_require__(2);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__bye___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__bye__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__hello__ = __webpack_require__(3);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__hello___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1__hello__);



Object(__WEBPACK_IMPORTED_MODULE_0__bye__["bye"])();
__WEBPACK_IMPORTED_MODULE_1__hello___default()();

/***/ }),
/* 2 */
/***/ (function(module, exports) {

function bye() {  
    console.log('bye')
}
module.exports = {
    bye
};

/***/ }),
/* 3 */
/***/ (function(module, exports) {

function hello() {  
    console.log('hello')
}
module.exports = hello;

/***/ })
/******/ ]);
  • 除了在index.js在es模塊設置__esModule標記,注意到調用時候的不用
  • Object(__WEBPACK_IMPORTED_MODULE_0__bye__["bye"])()引入bye是以一個對象的形式引入的。
  • __WEBPACK_IMPORTED_MODULE_1__hello___default()();在處理cjs時每一個模塊有一個defalut值,若是是cjs的狀況會返回module的getter,而源文件引入Hello是default值,直接執行返回的getter

cjs引入es模塊

  • 源文件
//index.js
const { bye } = require('./bye')
const hello = require('./hello')
bye();
hello.default();

//hello.js
function hello() {  
    console.log('hello')
}
export default hello;

//bye.js
export function bye() {  
    console.log('bye')
}
// bundle.js
/* 1 */
/***/ (function(module, exports, __webpack_require__) {

const { bye } = __webpack_require__(2)
const hello = __webpack_require__(3)
bye();
hello.default();

/***/ }),
/* 2 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["bye"] = bye;
function bye() {  
    console.log('bye')
}

/***/ }),
/* 3 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
function hello() {  
    console.log('hello')
}
/* harmony default export */ __webpack_exports__["default"] = (hello);

/***/ })
/******/ ]);
  • 源文件中的export function bye編譯成__webpack_exports__["bye"] = bye;export defalut編譯成__webpack_exports__["default"] = (hello)`;因此在默認引入hello調用時要手動調用default方法

連接:Webpack之treeShaking;tree shakinggithub

code spliting代碼分離

entry points入口分離

  • 最簡單最直接的分離代碼方式,可是會有重複的問題,好比說引入lodashweb

    • 順便說一下lodash引入的是一整個對象,若是你是用的是import {each} from 'lodash',仍是會引入整個lodash庫,目前最好的解決辦法是按fp引入好比:import each from 'lodash/fp/each';
    • 分別在js/index,js/bye中引入lodash,像下面的配置就會存在重複的each方法,若是不是按fp的方式引入lodash,則至關於引入了兩個lodash大小的文件是對資源的浪費。json

      module.exports = {
      entry: {
          index: './src/index.js',
          bye: './src/bye.js'
      },
      output: {
          filename: 'js/[name].js'
      },
  • 因此咱們在webpack4以前使用CommonsChunkPlugin來抽取公共組件,webpack4開始使用optimization選項來作簡化配置數組

    //webapck4以前
    new webpack.optimize.CommonsChunkPlugin({
        name: 'commons',
        // (公共 chunk(commnon chunk) 的名稱)
      
        filename: 'commons.js',
        // (公共chunk 的文件名)
      
        minChunks: 2,
        // (模塊必須被2個 入口chunk 共享)
    })

動態導入

  • webpack 提供了兩種方式進行動態導入,符合 ECMAScript 提案的import()和webpack特定的方法require.ensure()promise

    import() 調用會在內部用到 promises。若是在舊有版本瀏覽器中使用 import(),記得使用 一個 polyfill 庫(例如 es6-promise 或 promise-polyfill),來 shim Promise。
  • 簡單的例子:在index.js中動態引入hello.js,打包後會生成一個0.js和index.js

    //index,js
    import('./hello').then(hello => {
        hello.default();
    })
  • 先來看index.js中[modules]引入的模塊,能夠發現引入的動態引入的hello.js文件並不存在index.js,實際是在新生成的0.js文件中,這裏先按下不提,/* 1 */中最重要的兩個函數__webpack_require__.e__webpack_require__.bind成爲動態import的關鍵

    [
    /* 0 */,
    /* 1 */
    /***/ (function (module, exports, __webpack_require__) {
    
                __webpack_require__.e/* import() */(0/* duplicate */).then(__webpack_require__.bind(null, 0))
                .then(hello => {
                    hello.default();
                })
    
                /***/
            })
    /******/]
  • 分析__webpack_require__.e(0)

    /******/     __webpack_require__.e = function requireEnsure(chunkId) {
    /******/         var installedChunkData = installedChunks[chunkId];
    /******/         if (installedChunkData === 0) {
    /******/             return new Promise(function (resolve) { resolve(); });
                /******/
            }
    /******/
    /******/         // a Promise means "currently loading".
    /******/         if (installedChunkData) {
    /******/             return installedChunkData[2];
                /******/
            }
    /******/
    /******/         var promise = new Promise(function (resolve, reject) {
    /******/             installedChunkData = installedChunks[chunkId] = [resolve, reject];
                /******/
            });
    /******/         installedChunkData[2] = promise;
    /******/
    /******/         // start chunk loading
    /******/         var head = document.getElementsByTagName('head')[0];
    /******/         var script = document.createElement('script');
    /******/         script.type = "text/javascript";
    /******/         script.charset = 'utf-8';
    /******/         script.async = true;
    /******/         script.timeout = 120000;
    /******/
    /******/         if (__webpack_require__.nc) {
    /******/             script.setAttribute("nonce", __webpack_require__.nc);
                /******/
            }
    /******/         script.src = __webpack_require__.p + "js/" + chunkId + ".js";
    /******/         var timeout = setTimeout(onScriptComplete, 120000);
    /******/         script.onerror = script.onload = onScriptComplete;
    /******/         function onScriptComplete() {
    /******/             // avoid mem leaks in IE.
    /******/             script.onerror = script.onload = null;
    /******/             clearTimeout(timeout);
    /******/             var chunk = installedChunks[chunkId];
    /******/             if (chunk !== 0) {
    /******/                 if (chunk) {
    /******/                     chunk[1](new Error('Loading chunk ' + chunkId + ' failed.'));
                        /******/
                    }
    /******/                 installedChunks[chunkId] = undefined;
                    /******/
                }
                /******/
            };
    /******/         head.appendChild(script);
    /******/
    /******/         return promise;
            /******/
        };
    • installedChunks對象用來標記模塊是否加載過,當installedChunks[chunkId]爲0時表示已經加載成功
    • installedChunkData保存新建的promise,用於判斷模塊是否處於加載中狀態
    • 經過建立script標籤插入head的方式來加載模塊
    • 函數最後返回promise
  • 通過script標籤的方法,代碼會引入0.js

    webpackJsonp([0],[
        /* 0 */
        /***/ (function(module, __webpack_exports__, __webpack_require__) {
        
        "use strict";
        Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
        function hello() {  
            console.log('hello')
        }
        /* harmony default export */ __webpack_exports__["default"] = (hello);
        
        /***/ })
    ]);
    • 能夠看到是執行了一個webpackJsonp方法,分析下這個方法
    var parentJsonpFunction = window["webpackJsonp"];
    /******/     window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {
    /******/         // add "moreModules" to the modules object,
    /******/         // then flag all "chunkIds" as loaded and fire callback
    /******/         var moduleId, chunkId, i = 0, resolves = [], result;
    /******/         for (; i < chunkIds.length; i++) {
    /******/             chunkId = chunkIds[i];
    /******/             if (installedChunks[chunkId]) {
    /******/                 resolves.push(installedChunks[chunkId][0]);
                    /******/
                }
    /******/             installedChunks[chunkId] = 0;
                /******/
            }
    /******/         for (moduleId in moreModules) {
    /******/             if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
    /******/                 modules[moduleId] = moreModules[moduleId];
                    /******/
                }
                /******/
            }
    /******/         if (parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);
    /******/         while (resolves.length) {
    /******/             resolves.shift()();
                /******/
            }
            /******/
            /******/
        };
    • 將webpackJsonp賦值前,先把以前的webpackJson存儲爲parentJsonpFunction,當多個入口文件的時候會出現這種狀況
    • 判斷是否已經加載過有緩存,若是沒有加載過則將resolve方法push進一個數組中存儲
    • 將modules對應的id值賦值爲引入進來的模塊
    • 執行resolves隊列
  • __webpack_require__.e(0)返回的promise後執行__webpack_require__.bind(null, 0),前面咱們分析過__webpack_require__方法,主要做用是緩存已經調用的模塊,初始化exports等參數並將這些參數傳入module的執行中。後一個promise鏈then方法真正執行hello方法。

Tree shaking

一些經常使用概念

  • AST (abstract Syntax Tree)把js的每個語句都轉化爲樹中的一個節點
  • DCE(dead code elimination)保證代碼結果不變的前提下,去除無用代碼

    DCE{
        優勢:減小程序體積、執行時間
         Dead Code{
            程序中沒有執行的代碼(不可能進入的分支、return以後的語句)
            致使dead variable的代碼(寫入變量後再也不讀取的代碼)
        }
    }
  • tree shaking是DCE的一種方式,在打包時忽略沒有用到的代碼
  • tree shaking經過掃描全部ES6的export。找出全部import標記爲有使用和無使用,在後續壓縮時進行剔除。所以必須遵循es6的模塊規範(import&export)
  • webpack2支持tree-shaking須要在babel處理中不能將es6轉成commonjs,設置babel-preset-es2015的modules爲false,表示不對es6模塊進行處理。webpack3/webpack4能夠不加這個配置。

    clipboard.png

Tree shaking步驟

  • 全部import標記爲harmony import
  • 被使用過的export標記爲harmony export [type]

    • webpack4爲了加快開發狀態下的編譯速度,mode=development時都認爲是被使用的,不會出現unused harmony export詳情
    • 沒被使用過的export標記爲harmony export [FuncName],其中[FuncName]爲export的方法名稱
    • 以後在Uglifyjs(或者其餘相似工具進行代碼精簡),就能夠把unused部分去掉

方法的處理

//math.js
export function square(x) {
    return x * x;
}

export function cube(x) {
    return x * x * x;
}

//index.js
import { cube } from './math.js';

function component() {
    var element = document.createElement('pre');

    element.innerHTML = [
        'Hello webpack!',
        '5 cubed is equal to ' + cube(5)
    ].join('\n\n');

    return element;
}

document.body.appendChild(component());
//bundle.js
  (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 /* unused harmony export square */
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return cube; });
 function square(x) {
     return x * x;
 }
 
 function cube(x) {
     return x * x * x;
 }
 
 /***/ }),
 /* 1 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var _math_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
 
 
 function component() {
     var element = document.createElement('pre');
 
     element.innerHTML = [
         'Hello webpack!',
         '5 cubed is equal to ' + Object(_math_js__WEBPACK_IMPORTED_MODULE_0__[/* cube */ "a"])(5)
     ].join('\n\n');
 
     return element;
 }
 
 document.body.appendChild(component());
 
 /***/ })
 /******/ ]);

class的處理

  • tree shaking對於類是總體標記爲導出的,代表webpack tree shaking只處理頂層內容,類和對象內部都不會再被分別處理。由於類調用屬性方法有不少狀況,好比util[Math.random() > 0.5 ? 'hello' : 'bye']這種判斷型的字符串方式調用

    // index.js
       import Util from './util'
       
       let util = new Util()
       let result1 = util.hello()
       
       console.log(result1)
       
       // util.js
       export default class Util {
           hello() {
               return 'hello'
           }
       
           bye() {
               return 'bye'
           }
       }
       // util.js
       export default class Util {
           hello() {
               return 'hello'
           }
       
           bye() {
               return 'bye'
           }
       }
    //bundle.js
       (function(module, __webpack_exports__, __webpack_require__) {
       
       "use strict";
       /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return Util; });
       // util.js
       class Util {
       hello() {
           return 'hello'
       }
       
       bye() {
           return 'bye'
       }
       }
       
       /***/ }),
       /* 1 */
       /***/ (function(module, __webpack_exports__, __webpack_require__) {
       
       "use strict";
       __webpack_require__.r(__webpack_exports__);
       /* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
       // index.js
       
       let util = new _util__WEBPACK_IMPORTED_MODULE_0__[/* default */ "a"]()
       let result1 = util.hello()
       console.log(result1)
       
       /***/ })
       /******/ ]);

反作用(side effects)

在執行某個方法或者文件時會對全局其餘內容產生影響的代碼。在導入時會執行特殊行爲的代碼,而不是僅僅暴露一個 export 或多個 export。舉例說明,例如 polyfill,在各種prototype假如方法,它影響全局做用域,而且一般不提供 export。

模塊引入帶來的反作用

//index.js
import Util from './util'
console.log('Util unused')

//util.js
console.log('This is Util class')
export default class Util {
  hello () {
    return 'hello'
  }

  bye () {
    return 'bye'
  }
}
Array.prototype.hello = () => 'hello'
//bundle.js
function(e, t, o) {
    e.exports = o(1);
  },
  function(e, t, o) {
    "use strict";
    Object.defineProperty(t, "__esModule", { value: !0 });
    o(2);
    console.log("Util unused");
  },
  function(e, t, o) {
    "use strict";
    console.log("This is Util class");
    Array.prototype.hello = () => "hello";
  }
  • webpack依然會把未使用標記Util爲unused harmony export default,在壓縮時去處class Util的引入,可是先後的兩句代碼依舊保留。由於編譯器不知道這兩句代碼時什麼做用,爲了保證代碼執行效果不變,默認指定全部代碼都有反作用。

方法帶來的反作用

//index.js
import {hello, bye} from './util'
let result1 = hello()
let result2 = bye()
console.log(result1)

// util.js
export function hello () {
  return 'hello'
}
export function bye () {
  return 'bye'
}
//bundle.js
function(e, t, n) {
    e.exports = n(1);
  },
  function(e, t, n) {
    "use strict";
    Object.defineProperty(t, "__esModule", { value: !0 });
    var r = n(2);
    let o = Object(r.b)();
    Object(r.a)();
    console.log(o);
  },
  function(e, t, n) {
    "use strict";
    (t.b = function() {
      return "hello";
    }),
      (t.a = function() {
        return "bye";
      });
  }
  • bye方法調用後的返回值result2沒有被使用,webpack刪除了result2的這個變量,bye方法由於webpack調用了會影響什麼,內部執行的代碼有可能影響全局,因此即便執行後沒有用到返回值也不能刪除。

如何解決反作用

一、 pure_funcs配置,在webpack.config.js中增長沒有反作用的函數名

// index.js
import {hello, bye} from './util'
let result1 = hello()
let a = 1
let b = 2
let result2 = Math.floor(a / b)
console.log(result1)
plugins: [
    new UglifyJSPlugin({
      uglifyOptions: {
        compress: {
          pure_funcs: ['Math.floor']
        }
      }
    })
  ],
//bundle.js 未使用pure_funcs
function(e, t, n) {
    "use strict";
    Object.defineProperty(t, "__esModule", { value: !0 });
    var r = n(2);
    let o = Object(r.a)();
    Math.floor(0.5);
    console.log(o);
  },
//bundle.js pure_funcs
function(e, t, n) {
    "use strict";
    Object.defineProperty(t, "__esModule", { value: !0 });
    var r = n(2);
    let o = Object(r.a)();
    console.log(o);
  },
  • 這裏之全部要換indexjs的內容正是由於pure_function的侷限性,在uglify後函數大多變了名字,只有Math.floor這種全局方法不會被重命名。纔會生效。

二、 webpack4 package.json裏面配置sideEffects

{
  "name": "your-project",
  "sideEffects": false
}
{
  "name": "your-project",
  "sideEffects": [
    "./src/some-side-effectful-file.js"
  ]
}
  • false表示代碼所有沒有反作用,或者經過一個數組表示沒有反作用的文件
  • concatenateModule做用域提高;在webpack3能夠加入webpack.optimize.ModuleConcatenateModulePlugin()對bundle文件進行優化串聯起各個模塊到一個閉包中,在webpack4中這種配置已經在mode=production中默認配置了
  • 開啓concatenateModule後bye的調用和本體都被消除了,這個功能的原理是將全部模塊輸出到一個方法內部,這樣像bye這種沒有反作用的方法即可以被輕易識別出來
  • 過去 webpack 打包時的一個取捨是將 bundle 中各個模塊單獨打包成閉包。這些打包函數使你的 JavaScript 在瀏覽器中處理的更慢。相比之下,一些工具像 Closure Compiler 和 RollupJS 能夠提高(hoist)或者預編譯全部模塊到一個閉包中,提高你的代碼在瀏覽器中的執行速度。

tree shaking小結

  • 使用es6模塊
  • 工具函數儘可能使用單獨導出不要使用一個類或者對象
  • 聲明sideEffects
相關文章
相關標籤/搜索