這里只是簡(jiǎn)單的對(duì)書(shū)中每章的重點(diǎn)進(jìn)行了總結(jié)甸鸟。
總結(jié)
第一章 webpack 簡(jiǎn)介
- 何為 webpack(一切皆模塊)
打包工具蓝牲,將任何一種資源都看作為一個(gè)模塊
- 為什么需要 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)大
- 安裝
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ā)送消息
作用:
讓 webpck 進(jìn)行打包(并不會(huì)將打包結(jié)果輸出到 output.path 目錄下,而是存放在內(nèi)存中)佛玄,并處理打包結(jié)果的資源請(qǐng)求
作為 web server 處理對(duì)靜態(tài)資源的請(qǐng)求
第二章 模塊打包
- 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 也就是 {})
- 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ǔ)句只能在頂層作用域梦抢,不能在代碼塊中
- 模塊打包原理
打包后的 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)的模塊
- key 為 module 的路徑
- 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
/******/ })()
- bundle 的執(zhí)行順序
和模塊的加載順序一致
最外面的匿名函數(shù)會(huì)初始化瀏覽器的環(huán)境
加載入口模塊(每一個(gè) bundle 有且只有一個(gè)入口模塊)
-
執(zhí)行模塊代碼
遇到 module.exports 則記錄模塊的導(dǎo)出值
遇到 require 函數(shù)交出執(zhí)行權(quán),進(jìn)入__webpack_require__函數(shù)體內(nèi)進(jìn)行加載其他模塊的邏輯楼誓;
-
在__webpack_require__函數(shù)體內(nèi)會(huì)判斷即將加載的模塊是否存在于 installedChunks 中
若存在玉锌,直接取值
若不存在,執(zhí)行該模塊代碼疟羹。獲取到導(dǎo)出值
所有依賴(lài)都執(zhí)行完畢主守,則回到入口模塊中禀倔,當(dāng)入口模塊代碼執(zhí)行到結(jié)尾也就意味著 bundle 執(zhí)行完畢
3,4 部是一個(gè)循環(huán)執(zhí)行的過(guò)程参淫,webpack 不會(huì)改變代碼的執(zhí)行順序救湖,代碼和模塊的加載順序完全一直,且是同步的
第三章 資源輸入和輸出
- 資源處理流程
根據(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
配置資源入口(entry,context)
context 入口的 path 是一個(gè)絕對(duì)路徑耍铜,為了讓 entry 更加簡(jiǎn)潔
-
entry
可以是字符串 chunk name -> main
可以是數(shù)組 chunk name -> main
會(huì)將多個(gè)資源進(jìn)行合并邑闺,然后將最后一個(gè)模塊作為入口
可以對(duì)象(多入口)對(duì)象的 value 可以是字符串或數(shù)組 chunk name -> 對(duì)象的 key
函數(shù)
函數(shù)的返回值可以是上面的任意值
-
配置資源出口(output)
output.path 絕對(duì)路徑,打包后的目錄 默認(rèn)為 dist
output.filename 打包后的文件名
[name] -- chunk name
[hash] -- hash 值棕兼,每次 build 產(chǎn)生的 hash 值都不同
[chunkhash] -- hash 值检吆, 由入口文件及其依賴(lài)的內(nèi)容生成 -- 推薦使用
[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
- 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)換;
- 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 中
ts -- ts-loader
文件(圖片) file-loader/url-loader(需要安裝 file-loader)
less -- less & less-loader
sass -- node-sass & sass-loader
postcss-loader postcss.config.js 文件(必須,否則會(huì)報(bào)錯(cuò))
autoprefixer 自動(dòng)添加瀏覽器兼容前綴
postcss-cssnext 幫助我們可以使用最新的 css 語(yǔ)法
第五章 樣式處理
- 分離樣式
默認(rèn)我們?cè)?js 文件中 import 的樣式會(huì)被打包到 js 中沪哺,而我們有事后希望可以將樣式提取到單獨(dú)的文件
extract-text-webpack-plugin webpack4 之前使用
mini-css-extract-plugin webpack4 及其之后的版本使用
- css module 直接配置 css-loader 的 option 就可以實(shí)現(xiàn)
隔離作用域沈自,防止沖突
第六章 代碼分片
實(shí)現(xiàn)高性能應(yīng)用其中重要的一點(diǎn)就是盡可能地讓用戶(hù)每次只加載必要的資源,優(yōu)先級(jí)不太高的資源則采用延遲加載等技術(shù)漸進(jìn)式地獲取辜妓,這樣可以保證頁(yè)面的首屏速度枯途。
- 通過(guò)入口來(lái)劃分
配置多個(gè) entry(提取 vendor)
- 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)化
- 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',
}
}
- optimization.SplitChunks webpack4 及其之后的版本使用 解決了 CommonsChunkPlugin 的問(wèn)題
一般直接配置
splitChunks:{
chunks: 'all'
}
即可
- 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)境配置
- 環(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']
})
})
- source map
devtool 屬性
- 資源壓縮
- 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
- 緩存
以 hash 作為文件名的一部分
html-webpack-plugin 實(shí)現(xiàn) js 自動(dòng)注入
- bundle 大小監(jiān)控
webpack-bundle-analyzer
vscode -- import cost
bundlesize 工具監(jiān)控
第八章 打包優(yōu)化
- 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)程”政己。
- 減少作用域
exclude/include
noParse 不會(huì)解析,但是會(huì)打包
IgnorePlugin 不會(huì)被打包
Cache:有些 loader 會(huì)有一個(gè) cache 配置項(xiàng)掏愁,通過(guò)配置 Cache歇由,可以緩存編譯后的代碼,在下一次編譯時(shí)直接使用上次緩存的結(jié)果果港,提升打包速度沦泌。
- 動(dòng)態(tài)鏈接庫(kù) dll
-
Dllplugin 和 Code Splitting 的區(qū)別:
可以用來(lái)提取公共模塊;
Code Splitting 是設(shè)置一些特定規(guī)則并在打包的過(guò)程中根據(jù)這些規(guī)則提取模塊
DllPlugin 是將 vendor 完全拆出來(lái)辛掠,有自己的一整套 Webpack 配置并獨(dú)立打包谢谦,在實(shí)際工程構(gòu)建時(shí)不用再對(duì)它進(jìn)行任何處理,直接取出即可萝衩。速度上更快回挽,但也更復(fù)雜。
- 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 配合
hmr 模塊熱替換 不需要刷新瀏覽器即可看到更改
開(kāi)啟 HMR
手動(dòng):入口文件添加 module.hot.accept()预柒,則 HMR 對(duì)于入口文件和其依賴(lài)的所有模塊都會(huì)生效队塘;
借助工具:react-hot-loader、vue-loader 等宜鸯;
- 原理 拉取的是 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 打包工具
都是需要全局安裝
rollup --- 專(zhuān)門(mén)打包 js 可選多種輸出格式
parcel --- 零配置 而且可以以 html 作為入口文件 編譯流程間通過(guò)傳遞 AST 節(jié)省大量時(shí)間(webpack4 之后 loader 之間也可以直接傳遞 AST耕蝉,之前只能是字符串)