《webpack 實(shí)戰(zhàn) 入門(mén)份企、進(jìn)階與調(diào)優(yōu)》總結(jié)

這里只是簡(jiǎn)單的對(duì)書(shū)中每章的重點(diǎn)進(jìn)行了總結(jié)甸鸟。

總結(jié)

第一章 webpack 簡(jiǎn)介

  1. 何為 webpack(一切皆模塊)

打包工具蓝牲,將任何一種資源都看作為一個(gè)模塊

  1. 為什么需要 webpack

script 引入腳本的缺點(diǎn)

  • 手動(dòng)維護(hù)模塊間(js 腳本)的依賴(lài)關(guān)系成本高

  • 每一個(gè) script 腳本都需要向服務(wù)器發(fā)送一個(gè)請(qǐng)求

  • 每個(gè) script 腳本的作用域都是全局的趟脂,所以會(huì)造成全局環(huán)境的污染

選擇 webpack 的理由

  • 支持多種模塊語(yǔ)法 es6, commonjs...

  • code spliting

  • 可以處理各種類(lèi)型的資源 (圖片,樣式例衍,文件...)

  • 社區(qū)強(qiáng)大

  1. 安裝

webpack webpack-cli(webpack 的命令行工具)

  • 全局

  • 局部(項(xiàng)目中 ---- 推薦)

  • 打包命令

webpack --entry=./index.js --output-filename=bundle.js --mode=development
  • webpack-dev-server 啟動(dòng)開(kāi)發(fā)服務(wù)器 live-reload

在瀏覽器和服務(wù)器之間維護(hù)一個(gè) websocket 監(jiān)聽(tīng)文件變化昔期,當(dāng)打包結(jié)果發(fā)生變化時(shí)向?yàn)g覽器發(fā)送消息

作用:

  1. 讓 webpck 進(jìn)行打包(并不會(huì)將打包結(jié)果輸出到 output.path 目錄下,而是存放在內(nèi)存中)佛玄,并處理打包結(jié)果的資源請(qǐng)求

  2. 作為 web server 處理對(duì)靜態(tài)資源的請(qǐng)求

第二章 模塊打包

  1. common js (node)

內(nèi)部有一個(gè) module 對(duì)象存儲(chǔ)模塊信息

導(dǎo)出

module.exports = exports

導(dǎo)入

require

  • 動(dòng)態(tài)導(dǎo)出 (支持條件導(dǎo)入)運(yùn)行時(shí)才能確定依賴(lài)關(guān)系

  • 值的拷貝

  • 無(wú)法 tree shaking

  • 第一次 require 的時(shí)候會(huì)執(zhí)行這個(gè)模塊的代碼硼一,后面每次 require 都會(huì)直接返回之前的執(zhí)行結(jié)果(module 對(duì)象上有一個(gè) loaded 屬性來(lái)判斷該模塊是否已經(jīng)被加載過(guò))

  • 無(wú)法解決循環(huán)依賴(lài)(產(chǎn)生依賴(lài)時(shí)導(dǎo)入的就是 module.exports 也就是 {})

  1. es6 module

導(dǎo)出

export

export default

導(dǎo)入

import

  • 靜態(tài)導(dǎo)入

  • 值的引用,只讀

  • 可以進(jìn)行 tree shaking 編譯時(shí)即可確定依賴(lài)關(guān)系

  • 可以通過(guò) es6 module 導(dǎo)出的是值的引用來(lái)解決循環(huán)依賴(lài)的問(wèn)題

  • 導(dǎo)入語(yǔ)句只能在頂層作用域梦抢,不能在代碼塊中

  1. 模塊打包原理
  • 打包后的 bundle 最外層有一個(gè)匿名函數(shù)包裹般贼,構(gòu)成自己的作用域

  • installedChunks 存儲(chǔ)已將被執(zhí)行過(guò)的模塊的結(jié)果

  • __webpack_require__ 實(shí)現(xiàn)模塊加載的函數(shù)(require)

  • __webpack_modules__ 一個(gè)對(duì)象 以 key-value 的形式存放工程中所有產(chǎn)生依賴(lài)的模塊

    1. key 為 module 的路徑
    2. value 匿名函數(shù)包裹的模塊實(shí)體,匿名函數(shù)的參數(shù)賦予了每個(gè)模塊的導(dǎo)入導(dǎo)出的能力
/******/ (() => { // webpackBootstrap
/******/    var __webpack_modules__ = ({

/***/ "./src/index.js":
/*!**********************!*\
  !*** ./src/index.js ***!
  \**********************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index_ts__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index.ts */ \"./src/index.ts\");\n/* harmony import */ var _index_ts__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_index_ts__WEBPACK_IMPORTED_MODULE_0__);\n\n__webpack_require__.e(/*! import() | bar */ \"bar\").then(__webpack_require__.bind(__webpack_require__, /*! ./app */ \"./src/app.js\")).then(function () {\n  console.log(\"load app\");\n});\n\nvar fn = function fn() {\n  return 1;\n};\n\nconsole.log(fn());\nconsole.log(_index_ts__WEBPACK_IMPORTED_MODULE_0__.sum);\n\n//# sourceURL=webpack://Chapter06/./src/index.js?");

/***/ }),

/***/ "./src/index.ts":
/*!**********************!*\
  !*** ./src/index.ts ***!
  \**********************/
/***/ (() => {

eval("throw new Error(\"Module build failed (from ../node_modules/happypack/loader.js):\\nError: You may be using an old version of webpack; please check you're using at least version 4\\n    at Object.initializeInstance (/Users/guoxx03/Desktop/test/webpack-demo/note_Webpack實(shí)戰(zhàn) 入門(mén)惑申、進(jìn)階與調(diào)優(yōu)/example/node_modules/ts-loader/dist/instances.js:275:19)\\n    at successLoader (/Users/guoxx03/Desktop/test/webpack-demo/note_Webpack實(shí)戰(zhàn) 入門(mén)具伍、進(jìn)階與調(diào)優(yōu)/example/node_modules/ts-loader/dist/index.js:26:17)\\n    at Object.loader (/Users/guoxx03/Desktop/test/webpack-demo/note_Webpack實(shí)戰(zhàn) 入門(mén)、進(jìn)階與調(diào)優(yōu)/example/node_modules/ts-loader/dist/index.js:23:5)\\n    at applySyncOrAsync (/Users/guoxx03/Desktop/test/webpack-demo/note_Webpack實(shí)戰(zhàn) 入門(mén)圈驼、進(jìn)階與調(diào)優(yōu)/example/node_modules/happypack/lib/applyLoaders.js:350:21)\\n    at apply (/Users/guoxx03/Desktop/test/webpack-demo/note_Webpack實(shí)戰(zhàn) 入門(mén)、進(jìn)階與調(diào)優(yōu)/example/node_modules/happypack/lib/applyLoaders.js:277:5)\\n    at /Users/guoxx03/Desktop/test/webpack-demo/note_Webpack實(shí)戰(zhàn) 入門(mén)望几、進(jìn)階與調(diào)優(yōu)/example/node_modules/happypack/lib/applyLoaders.js:135:7\\n    at applyPitchLoader (/Users/guoxx03/Desktop/test/webpack-demo/note_Webpack實(shí)戰(zhàn) 入門(mén)绩脆、進(jìn)階與調(diào)優(yōu)/example/node_modules/happypack/lib/applyLoaders.js:188:14)\\n    at applyPitchLoader (/Users/guoxx03/Desktop/test/webpack-demo/note_Webpack實(shí)戰(zhàn) 入門(mén)、進(jìn)階與調(diào)優(yōu)/example/node_modules/happypack/lib/applyLoaders.js:196:14)\\n    at applyPitchLoaders (/Users/guoxx03/Desktop/test/webpack-demo/note_Webpack實(shí)戰(zhàn) 入門(mén)橄抹、進(jìn)階與調(diào)優(yōu)/example/node_modules/happypack/lib/applyLoaders.js:226:4)\\n    at applyLoaders (/Users/guoxx03/Desktop/test/webpack-demo/note_Webpack實(shí)戰(zhàn) 入門(mén)靴迫、進(jìn)階與調(diào)優(yōu)/example/node_modules/happypack/lib/applyLoaders.js:120:3)\");\n\n//# sourceURL=webpack://Chapter06/./src/index.ts?");

/***/ }),

/***/ "dll-reference dllExample":
/*!*****************************!*\
  !*** external "dllExample" ***!
  \*****************************/
/***/ ((module) => {

"use strict";
module.exports = dllExample;

/***/ })

/******/    });
/************************************************************************/
/******/    // The module cache
/******/    var __webpack_module_cache__ = {};
/******/
/******/    // The require function
/******/    function __webpack_require__(moduleId) {
/******/        // Check if module is in cache
/******/        if(__webpack_module_cache__[moduleId]) {
/******/            return __webpack_module_cache__[moduleId].exports;
/******/        }
/******/        // Create a new module (and put it into the cache)
/******/        var module = __webpack_module_cache__[moduleId] = {
/******/            // no module.id needed
/******/            // no module.loaded needed
/******/            exports: {}
/******/        };
/******/
/******/        // Execute the module function
/******/        __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/        // Return the exports of the module
/******/        return module.exports;
/******/    }
/******/
/******/    // expose the modules object (__webpack_modules__)
/******/    __webpack_require__.m = __webpack_modules__;
/******/
/************************************************************************/
/******/    /* webpack/runtime/compat get default export */
/******/    (() => {
/******/        // getDefaultExport function for compatibility with non-harmony modules
/******/        __webpack_require__.n = (module) => {
/******/            var getter = module && module.__esModule ?
/******/                () => (module['default']) :
/******/                () => (module);
/******/            __webpack_require__.d(getter, { a: getter });
/******/            return getter;
/******/        };
/******/    })();
/******/
/******/    /* webpack/runtime/define property getters */
/******/    (() => {
/******/        // define getter functions for harmony exports
/******/        __webpack_require__.d = (exports, definition) => {
/******/            for(var key in definition) {
/******/                if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/                    Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/                }
/******/            }
/******/        };
/******/    })();
/******/
/******/    /* webpack/runtime/ensure chunk */
/******/    (() => {
/******/        __webpack_require__.f = {};
/******/        // This file contains only the entry chunk.
/******/        // The chunk loading function for additional chunks
/******/        __webpack_require__.e = (chunkId) => {
/******/            return Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) => {
/******/                __webpack_require__.f[key](chunkId, promises);
/******/                return promises;
/******/            }, []));
/******/        };
/******/    })();
/******/
/******/    /* webpack/runtime/get javascript chunk filename */
/******/    (() => {
/******/        // This function allow to reference async chunks
/******/        __webpack_require__.u = (chunkId) => {
/******/            // return url for filenames based on template
/******/            return "" + chunkId + ".js";
/******/        };
/******/    })();
/******/
/******/    /* webpack/runtime/hasOwnProperty shorthand */
/******/    (() => {
/******/        __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/    })();
/******/
/******/    /* webpack/runtime/load script */
/******/    (() => {
/******/        var inProgress = {};
/******/        var dataWebpackPrefix = "Chapter06:";
/******/        // loadScript function to load a script via script tag
/******/        __webpack_require__.l = (url, done, key, chunkId) => {
/******/            if(inProgress[url]) { inProgress[url].push(done); return; }
/******/            var script, needAttach;
/******/            if(key !== undefined) {
/******/                var scripts = document.getElementsByTagName("script");
/******/                for(var i = 0; i < scripts.length; i++) {
/******/                    var s = scripts[i];
/******/                    if(s.getAttribute("src") == url || s.getAttribute("data-webpack") == dataWebpackPrefix + key) { script = s; break; }
/******/                }
/******/            }
/******/            if(!script) {
/******/                needAttach = true;
/******/                script = document.createElement('script');
/******/
/******/                script.charset = 'utf-8';
/******/                script.timeout = 120;
/******/                if (__webpack_require__.nc) {
/******/                    script.setAttribute("nonce", __webpack_require__.nc);
/******/                }
/******/                script.setAttribute("data-webpack", dataWebpackPrefix + key);
/******/                script.src = url;
/******/            }
/******/            inProgress[url] = [done];
/******/            var onScriptComplete = (prev, event) => {
/******/                // avoid mem leaks in IE.
/******/                script.onerror = script.onload = null;
/******/                clearTimeout(timeout);
/******/                var doneFns = inProgress[url];
/******/                delete inProgress[url];
/******/                script.parentNode && script.parentNode.removeChild(script);
/******/                doneFns && doneFns.forEach((fn) => (fn(event)));
/******/                if(prev) return prev(event);
/******/            }
/******/            ;
/******/            var timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), 120000);
/******/            script.onerror = onScriptComplete.bind(null, script.onerror);
/******/            script.onload = onScriptComplete.bind(null, script.onload);
/******/            needAttach && document.head.appendChild(script);
/******/        };
/******/    })();
/******/
/******/    /* webpack/runtime/make namespace object */
/******/    (() => {
/******/        // define __esModule on exports
/******/        __webpack_require__.r = (exports) => {
/******/            if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/                Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/            }
/******/            Object.defineProperty(exports, '__esModule', { value: true });
/******/        };
/******/    })();
/******/
/******/    /* webpack/runtime/publicPath */
/******/    (() => {
/******/        __webpack_require__.p = "/dist/";
/******/    })();
/******/
/******/    /* webpack/runtime/jsonp chunk loading */
/******/    (() => {
/******/        // no baseURI
/******/
/******/        // object to store loaded and loading chunks
/******/        // undefined = chunk not loaded, null = chunk preloaded/prefetched
/******/        // Promise = chunk loading, 0 = chunk loaded
/******/        var installedChunks = {
/******/            "main": 0
/******/        };
/******/
/******/
/******/        __webpack_require__.f.j = (chunkId, promises) => {
/******/                // JSONP chunk loading for javascript
/******/                var installedChunkData = __webpack_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined;
/******/                if(installedChunkData !== 0) { // 0 means "already installed".
/******/
/******/                    // a Promise means "currently loading".
/******/                    if(installedChunkData) {
/******/                        promises.push(installedChunkData[2]);
/******/                    } else {
/******/                        if(true) { // all chunks have JS
/******/                            // setup Promise in chunk cache
/******/                            var promise = new Promise((resolve, reject) => {
/******/                                installedChunkData = installedChunks[chunkId] = [resolve, reject];
/******/                            });
/******/                            promises.push(installedChunkData[2] = promise);
/******/
/******/                            // start chunk loading
/******/                            var url = __webpack_require__.p + __webpack_require__.u(chunkId);
/******/                            // create error before stack unwound to get useful stacktrace later
/******/                            var error = new Error();
/******/                            var loadingEnded = (event) => {
/******/                                if(__webpack_require__.o(installedChunks, chunkId)) {
/******/                                    installedChunkData = installedChunks[chunkId];
/******/                                    if(installedChunkData !== 0) installedChunks[chunkId] = undefined;
/******/                                    if(installedChunkData) {
/******/                                        var errorType = event && (event.type === 'load' ? 'missing' : event.type);
/******/                                        var realSrc = event && event.target && event.target.src;
/******/                                        error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')';
/******/                                        error.name = 'ChunkLoadError';
/******/                                        error.type = errorType;
/******/                                        error.request = realSrc;
/******/                                        installedChunkData[1](error);
/******/                                    }
/******/                                }
/******/                            };
/******/                            __webpack_require__.l(url, loadingEnded, "chunk-" + chunkId, chunkId);
/******/                        } else installedChunks[chunkId] = 0;
/******/                    }
/******/                }
/******/        };
/******/
/******/        // no prefetching
/******/
/******/        // no preloaded
/******/
/******/        // no HMR
/******/
/******/        // no HMR manifest
/******/
/******/        // no deferred startup
/******/
/******/        // install a JSONP callback for chunk loading
/******/        var webpackJsonpCallback = (parentChunkLoadingFunction, data) => {
/******/            var [chunkIds, moreModules, runtime] = data;
/******/            // add "moreModules" to the modules object,
/******/            // then flag all "chunkIds" as loaded and fire callback
/******/            var moduleId, chunkId, i = 0, resolves = [];
/******/            for(;i < chunkIds.length; i++) {
/******/                chunkId = chunkIds[i];
/******/                if(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) {
/******/                    resolves.push(installedChunks[chunkId][0]);
/******/                }
/******/                installedChunks[chunkId] = 0;
/******/            }
/******/            for(moduleId in moreModules) {
/******/                if(__webpack_require__.o(moreModules, moduleId)) {
/******/                    __webpack_require__.m[moduleId] = moreModules[moduleId];
/******/                }
/******/            }
/******/            if(runtime) runtime(__webpack_require__);
/******/            if(parentChunkLoadingFunction) parentChunkLoadingFunction(data);
/******/            while(resolves.length) {
/******/                resolves.shift()();
/******/            }
/******/
/******/        }
/******/
/******/        var chunkLoadingGlobal = self["webpackChunkChapter06"] = self["webpackChunkChapter06"] || [];
/******/        chunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));
/******/        chunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));
/******/
/******/        // no deferred startup
/******/    })();
/******/
/************************************************************************/
/******/    // startup
/******/    // Load entry module
/******/    __webpack_require__("./src/index.js");
/******/    // This entry module used 'exports' so it can't be inlined
/******/ })()
  1. bundle 的執(zhí)行順序

和模塊的加載順序一致

  • 最外面的匿名函數(shù)會(huì)初始化瀏覽器的環(huán)境

  • 加載入口模塊(每一個(gè) bundle 有且只有一個(gè)入口模塊)

  • 執(zhí)行模塊代碼

    1. 遇到 module.exports 則記錄模塊的導(dǎo)出值

    2. 遇到 require 函數(shù)交出執(zhí)行權(quán),進(jìn)入__webpack_require__函數(shù)體內(nèi)進(jìn)行加載其他模塊的邏輯楼誓;

  • 在__webpack_require__函數(shù)體內(nèi)會(huì)判斷即將加載的模塊是否存在于 installedChunks 中

    1. 若存在玉锌,直接取值

    2. 若不存在,執(zhí)行該模塊代碼疟羹。獲取到導(dǎo)出值

  • 所有依賴(lài)都執(zhí)行完畢主守,則回到入口模塊中禀倔,當(dāng)入口模塊代碼執(zhí)行到結(jié)尾也就意味著 bundle 執(zhí)行完畢

3,4 部是一個(gè)循環(huán)執(zhí)行的過(guò)程参淫,webpack 不會(huì)改變代碼的執(zhí)行順序救湖,代碼和模塊的加載順序完全一直,且是同步的

第三章 資源輸入和輸出

  1. 資源處理流程
  • 根據(jù)指定的一個(gè)/多個(gè)入口(entry)涎才,告訴 webpack 從哪個(gè)目錄的哪個(gè)文件開(kāi)始打包

  • webpack 會(huì)從入口模塊開(kāi)始檢索鞋既,并將具有依賴(lài)關(guān)系的模塊構(gòu)成一個(gè)(樹(shù)) chunk(一般來(lái)說(shuō)一個(gè)入口及其依賴(lài)會(huì)產(chǎn)生一個(gè) chunk)

  • 最終會(huì)將 chunk 打包為 bundle

  1. 配置資源入口(entry,context)

  2. context 入口的 path 是一個(gè)絕對(duì)路徑耍铜,為了讓 entry 更加簡(jiǎn)潔

  3. entry

    • 可以是字符串 chunk name -> main

    • 可以是數(shù)組 chunk name -> main

    會(huì)將多個(gè)資源進(jìn)行合并邑闺,然后將最后一個(gè)模塊作為入口

    • 可以對(duì)象(多入口)對(duì)象的 value 可以是字符串或數(shù)組 chunk name -> 對(duì)象的 key

    • 函數(shù)

    函數(shù)的返回值可以是上面的任意值

  4. 配置資源出口(output)

    • output.path 絕對(duì)路徑,打包后的目錄 默認(rèn)為 dist

    • output.filename 打包后的文件名

    1. [name] -- chunk name

    2. [hash] -- hash 值棕兼,每次 build 產(chǎn)生的 hash 值都不同

    3. [chunkhash] -- hash 值检吆, 由入口文件及其依賴(lài)的內(nèi)容生成 -- 推薦使用

    4. [id] -- chunk id

    • output.publicPath 會(huì)為請(qǐng)求的靜態(tài)資源加上這個(gè)前綴

建議 output.path 和 webpack-dev-server 的 publicPath 保持一致

第四章 預(yù)處理器

loader --- 就是一個(gè)函數(shù),接受源碼字符串

每個(gè) loader 本質(zhì)都是一個(gè)函數(shù)程储。output = loader(input)蹭沛;(loader 之間傳遞數(shù)據(jù):webpack4 之前都為字符串,webpack4 之后支持抽象語(yǔ)法樹(shù) AST)

出現(xiàn)的原因是因?yàn)檎吕穑瑆ebpack 默認(rèn)只能處理 js 代碼

具體配置可以直接參考官網(wǎng)摊灭,這里值寫(xiě)出,不同的資源需要的 loader

  • exclude/include 縮小 loader 的作用范圍 exclude 的優(yōu)先級(jí)更高

  • enforce -- 設(shè)置 loader 的執(zhí)行順序 pre post normal inline

配置 module.rules

  1. js
  • es6+ 轉(zhuǎn) es5

babel-loader @babel/core @babel/preset-env

'babel-loader?cacheDirectory'

exclude: /node_modules/

@babel/preset-env 會(huì)默認(rèn)將 es6 module 轉(zhuǎn)為 commonjs module败徊,導(dǎo)致 webpack 的 tree shaking 失效帚呼,將@babel/preset-env 的 modules 配置項(xiàng)設(shè)為 false 禁用掉模塊語(yǔ)句轉(zhuǎn)換;

  1. css

use: ['style-loader','css-loader']

  • css-loader:處理 css 的各種加載語(yǔ)法(@import 和 url()函數(shù)等)

  • style-loader:把樣式插入頁(yè)面皱蹦,將 css-loader 處理過(guò)后的字符串煤杀,創(chuàng)建一個(gè) style 標(biāo)簽插入到 head 中

  1. ts -- ts-loader

  2. 文件(圖片) file-loader/url-loader(需要安裝 file-loader)

  3. less -- less & less-loader

  4. sass -- node-sass & sass-loader

  5. postcss-loader postcss.config.js 文件(必須,否則會(huì)報(bào)錯(cuò))

  • autoprefixer 自動(dòng)添加瀏覽器兼容前綴

  • postcss-cssnext 幫助我們可以使用最新的 css 語(yǔ)法

第五章 樣式處理

  1. 分離樣式

默認(rèn)我們?cè)?js 文件中 import 的樣式會(huì)被打包到 js 中沪哺,而我們有事后希望可以將樣式提取到單獨(dú)的文件

extract-text-webpack-plugin webpack4 之前使用

mini-css-extract-plugin webpack4 及其之后的版本使用

  1. css module 直接配置 css-loader 的 option 就可以實(shí)現(xiàn)

隔離作用域沈自,防止沖突

第六章 代碼分片

實(shí)現(xiàn)高性能應(yīng)用其中重要的一點(diǎn)就是盡可能地讓用戶(hù)每次只加載必要的資源,優(yōu)先級(jí)不太高的資源則采用延遲加載等技術(shù)漸進(jìn)式地獲取辜妓,這樣可以保證頁(yè)面的首屏速度枯途。

  1. 通過(guò)入口來(lái)劃分

配置多個(gè) entry(提取 vendor)

  1. CommonsChunkPlugin webpack4 之前使用

缺點(diǎn):

  • 一個(gè) CommonsChunkPlugin 只能提取一個(gè) vendor,假如我們想提取多個(gè) vendor 則需要配置多個(gè)插件

  • 會(huì)使瀏覽器多加載一個(gè)資源籍滴,這對(duì)于頁(yè)面渲染速度不友好 manifest.js

  • CommonsChunkPlugin 在提取公共模塊的時(shí)候會(huì)破壞掉原有 chunk 中的依賴(lài)關(guān)系酪夷,導(dǎo)致難以進(jìn)行更多的優(yōu)化

  1. hash 緩存

使用 CommonsChunkPlugin 提取公共模塊是,提取后的資源內(nèi)部不僅僅是模塊的代碼孽惰,往往還包含 Webpack 的運(yùn)行時(shí)(runtime)晚岭,即初始化環(huán)境的代碼,如創(chuàng)建模塊緩存對(duì)象勋功、聲明模塊加載函數(shù)等坦报,為了使緩存有效库说,需要單獨(dú)提取

const webpack = require('webpack');
const path = require('path');

module.exports = {
  entry: {
    app: './src/app.js',
    vendor: ['vue']
  },
  output: {
    filename: '[name].js',
    path: path.join(__dirname, 'dist')
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest', // 必須出現(xiàn)在最后,否則webpack將無(wú)法正常提取模塊
    })
  ],
  devServer: {
    publicPath: './dist',
  }
}
  1. optimization.SplitChunks webpack4 及其之后的版本使用 解決了 CommonsChunkPlugin 的問(wèn)題

一般直接配置

splitChunks:{
    chunks: 'all'
}

即可

  1. import() 異步加載資源燎竖,默認(rèn)可以被提取公共模塊

原理:通過(guò) js 動(dòng)態(tài)地在頁(yè)面 head 標(biāo)簽內(nèi)插入一個(gè) script 標(biāo)簽璃弄,并通過(guò) publicPath 拼接其 url

第七章 生產(chǎn)環(huán)境配置

  1. 環(huán)境變量

設(shè)置了 mode 屬性之后

process.env.NODE_ENV 就是 mode 的值

webapck.DefinePlugin

new webpack.DefinePlugin ({
            ENV: JSON.stringify('production'),
            IS_PRODUCTION: true,
            ENV_ID: 130912098 ,
            CONSTANTS: JSON.stringify({ // JSON.stringify? DefinePlugin在替換環(huán)境變量時(shí)對(duì)于字符串類(lèi)型的值進(jìn)行的時(shí)完全替換。
                TYPES: ['foo','bar']
            })
        })
  1. source map

devtool 屬性

  1. 資源壓縮
  • js 壓縮

webpack4 及其以上版本 mode: production 的時(shí)候會(huì)自動(dòng)壓縮打包后的 bundle 默認(rèn)使用的是 terser

// mode 不是production時(shí)构回,不會(huì)自動(dòng)壓縮夏块,需要我們手動(dòng)配置
optimization: {
    minimize: true
  },

webpack4 之前的版本使用 uglifyJS (只支持 es5)

  • css 壓縮

需要先將 css 提取到單獨(dú)的文件中

optimize-css-assets-webpck-plugin

  1. 緩存
  • 以 hash 作為文件名的一部分

  • html-webpack-plugin 實(shí)現(xiàn) js 自動(dòng)注入

  1. bundle 大小監(jiān)控
  • webpack-bundle-analyzer

  • vscode -- import cost

  • bundlesize 工具監(jiān)控

第八章 打包優(yōu)化

  1. happypack 多進(jìn)程打包 thread-loader

適用于轉(zhuǎn)換工作比較多的 loader (babel-loader/ts-loader)

工作原理:HappyPack 位于 webpack 和主要源文件(例如 JS 源)之間,在該文件中纤掸,大量的加載程序發(fā)生轉(zhuǎn)換脐供。每次 webpack 解析模塊時(shí),HappyPack 都會(huì)獲取該模塊及其所有依賴(lài)項(xiàng)借跪,并將這些文件分發(fā)到多個(gè)工作程序“線(xiàn)程”政己。

  1. 減少作用域
  • exclude/include

  • noParse 不會(huì)解析,但是會(huì)打包

  • IgnorePlugin 不會(huì)被打包

  • Cache:有些 loader 會(huì)有一個(gè) cache 配置項(xiàng)掏愁,通過(guò)配置 Cache歇由,可以緩存編譯后的代碼,在下一次編譯時(shí)直接使用上次緩存的結(jié)果果港,提升打包速度沦泌。

  1. 動(dòng)態(tài)鏈接庫(kù) dll
  • Dllplugin 和 Code Splitting 的區(qū)別:

    1. 可以用來(lái)提取公共模塊;

    2. Code Splitting 是設(shè)置一些特定規(guī)則并在打包的過(guò)程中根據(jù)這些規(guī)則提取模塊

    3. DllPlugin 是將 vendor 完全拆出來(lái)辛掠,有自己的一整套 Webpack 配置并獨(dú)立打包谢谦,在實(shí)際工程構(gòu)建時(shí)不用再對(duì)它進(jìn)行任何處理,直接取出即可萝衩。速度上更快回挽,但也更復(fù)雜。

  1. tree shaking 只對(duì) es module 生效

如果使用 babel-loader猩谊,一定要禁用它的模塊依賴(lài)解析(因?yàn)樗麜?huì)將 es module 轉(zhuǎn)換為 common js module 從而導(dǎo)致 tree shaking 失效)千劈,modules: false

tree shaking 只是標(biāo)記,需要壓縮工具去除死代碼

第九章 開(kāi)發(fā)環(huán)境調(diào)優(yōu)

hmr 針對(duì)開(kāi)發(fā)環(huán)境 需要 webpack-dev-server 配合

  1. hmr 模塊熱替換 不需要刷新瀏覽器即可看到更改

  2. 開(kāi)啟 HMR

  • 手動(dòng):入口文件添加 module.hot.accept()预柒,則 HMR 對(duì)于入口文件和其依賴(lài)的所有模塊都會(huì)生效队塘;

  • 借助工具:react-hot-loader、vue-loader 等宜鸯;

  1. 原理 拉取的是 chunk diff 即 chunk 需要更新的部分
  • webpack-dev-server(WDS)與瀏覽器間維護(hù)一個(gè) websocket,當(dāng)本地資源發(fā)生變化時(shí)遮怜,WDS 會(huì)向?yàn)g覽器推送更新事件淋袖,并帶上本次構(gòu)建的 hash,讓客戶(hù)端與上一次資源進(jìn)行對(duì)比—防止冗余更新的出現(xiàn)(eg:添加一個(gè)文件末尾空行等)锯梁;

  • 客戶(hù)端知道了兩次構(gòu)建的差異即碗,就會(huì)向 WDS 發(fā)起一個(gè)請(qǐng)求來(lái)獲取更改文件的列表焰情;

    請(qǐng)求:[hash].hot-update.json;

    返回值:hash 值 + chunk name剥懒;

  • 客戶(hù)端獲取了更新文件的信息内舟,繼續(xù)向 WDS 獲取該 chunk 的增量更新;

    請(qǐng)求:需要更新的 chunk name+版本信息初橘;

    返回值:增量更新內(nèi)容验游;

  • 客戶(hù)端拿到了 chunk 的更新,利用 webpack 提供的 API 進(jìn)行處理保檐;

第十章 更多 js 打包工具

都是需要全局安裝

  1. rollup --- 專(zhuān)門(mén)打包 js 可選多種輸出格式

  2. parcel --- 零配置 而且可以以 html 作為入口文件 編譯流程間通過(guò)傳遞 AST 節(jié)省大量時(shí)間(webpack4 之后 loader 之間也可以直接傳遞 AST耕蝉,之前只能是字符串)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市夜只,隨后出現(xiàn)的幾起案子垒在,更是在濱河造成了極大的恐慌,老刑警劉巖扔亥,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件场躯,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡旅挤,警方通過(guò)查閱死者的電腦和手機(jī)踢关,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)谦铃,“玉大人耘成,你說(shuō)我怎么就攤上這事【匀颍” “怎么了瘪菌?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)嘹朗。 經(jīng)常有香客問(wèn)我师妙,道長(zhǎng),這世上最難降的妖魔是什么屹培? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任默穴,我火速辦了婚禮,結(jié)果婚禮上褪秀,老公的妹妹穿的比我還像新娘蓄诽。我一直安慰自己,他們只是感情好媒吗,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布仑氛。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪锯岖。 梳的紋絲不亂的頭發(fā)上介袜,一...
    開(kāi)封第一講書(shū)人閱讀 51,443評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音出吹,去河邊找鬼遇伞。 笑死,一個(gè)胖子當(dāng)著我的面吹牛捶牢,可吹牛的內(nèi)容都是我干的鸠珠。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼叫确,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼跳芳!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起竹勉,我...
    開(kāi)封第一講書(shū)人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤飞盆,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后次乓,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體吓歇,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年票腰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了城看。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡杏慰,死狀恐怖测柠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情缘滥,我是刑警寧澤轰胁,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站朝扼,受9級(jí)特大地震影響赃阀,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜擎颖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一榛斯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧搂捧,春花似錦驮俗、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至吮蛹,卻和暖如春荤崇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背潮针。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工术荤, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人每篷。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓瓣戚,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親焦读。 傳聞我的和親對(duì)象是個(gè)殘疾皇子子库,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容