jsliang 求職系列 - 31 - Webpack

一 目錄

不折騰的前端,和鹹魚有什麼區別css

目錄
一 目錄
二 前言
三 Webpack 是什麼
四 Webpack 核心概念
五 Webpack 構建流程
六 entry
七 output
八 loader
8.1 關於文件處理常見的 loader
8.2 關於語法檢查常見 loader
8.3 關於 HTML 代碼處理常見的 loader
8.4 關於 CSS 代碼處理常見的 loader
8.5 關於 JS 代碼處理常見的 loader
九 plugin
9.1 常見 plugin
9.2 提升效率的 plugin
十 loader 和 plugin 的區別
十一 resolve
十二 從 0 開始配置 Webpack
12.1 技術選型
12.2 Loader 配置 - 處理 CSS、Less
12.3 Loader 配置 - 處理圖片
12.4 Loader 配置 - 處理字體
12.5 Loader 配置 - MPA 多頁面打包通用方案
12.6 SourceMap
12.7 WebpackDevServer
12.8 babel 解析
12.9 React
12.10 性能優化
12.11 其餘
十三 知識補充:懶加載
13.1 代碼分割
13.2 實現原理
13.3 Vue 按需加載
十四 知識補充:熱更新
14.1 開啓熱更新
14.2 熱更新原理
十五 知識補充:3 種 hash
十六 知識補充:source map
十七 知識補充:Webpack 打包原理
十八 參考文獻
18.1 Webpack 系列文章
18.2 Webpack 性能優化
18.3 Scope Hoisting
18.4 Tree Shaking
18.5 懶加載

二 前言

返回目錄

都 2020 了,不會點 Webpack 好像有點說不過去。html

可是事實上若是不是分配到【架構組】之類的團體中,感受接觸 Webpack 的機率會少點吧。前端

就比如 jsliang 在上家公司,就沒機會接觸 Webpack,都是用這別人已經配置好的方案,純粹作一個業務仔~vue

三 Webpack 是什麼

返回目錄

Webpack 是一個現代 JavaScript 應用程序的靜態模塊打包器(module bundler)。node

當 Webpack 處理應用程序時,它會遞歸地構建一個依賴關係圖(dependency graph),其中包含應用程序須要的每一個模塊,而後將全部這些模塊打包成一個或多個 bundlereact

因此,它的本質是一個模塊打包器,其工做是將每一個模塊打包成相應的 bundlewebpack

四 Webpack 核心概念

返回目錄
  • mode:模式。對應有開發模式、生產模式等
  • entry:入口
  • output:出口
  • loader:模塊轉換器,用於把模塊原內容按照需求轉換成新內容。Webpack 對於 .jpg.txt 等內容沒法處理,就須要 file-loaderurl-loader 等進行協助處理。
  • plugins:擴展插件,在 Webpack 構建流程中的特定時機注入拓展邏輯來改變構建結果或者作其餘你想作的事情。

五 Webpack 構建流程

返回目錄

Webpack 就像一條生產線,要通過一系列處理流程後才能將源文件轉換成輸出結果。nginx

這條生產線上的每一個處理流程的職責都是單一的,多個流程之間有存在依賴關係,只有完成當前處理後才能交給下一個流程去處理。 git

Webpack 的運行流程是一個串行的過程,從啓動到結束會依次執行如下流程:github

  • 初始化參數:從配置文件和 Shell 語句中讀取與合併參數,得出最終的參數
  • 開始編譯:用上一步獲得的參數初始化 Compiler 對象,加載全部配置的插件,執行對象的 run 方法開始執行編譯
  • 肯定入口:根據配置中的 entry 找出全部的入口文件
  • 編譯模塊:從入口文件出發,調用全部配置的 Loader 對模塊進行翻譯,再找出該模塊依賴的模塊,再遞歸本步驟直到全部入口依賴的文件都通過了本步驟的處理
  • 完成模塊編譯:在通過第 4 步使用 Loader 翻譯完全部模塊後,獲得了每一個模塊被翻譯後的最終內容以及它們之間的依賴關係
  • 輸出資源:根據入口和模塊之間的依賴關係,組裝成一個個包含多個模塊的 Chunk,再把每一個 Chunk 轉換成一個單獨的文件加入到輸出列表,這步是能夠修改輸出內容的最後機會
  • 輸出完成:在肯定好輸出內容後,根據配置肯定輸出的路徑和文件名,把文件內容寫入到文件系統

簡單來講:

  • 初始化:啓動構建,讀取與合併配置參數,加載 Plugin,實例化 Compiler(鉤子)
  • 編譯:從 Entry 出發,針對每一個 Module(模塊)串行調用對應的 Loader 去翻譯文件的內容,再找到該 Module 依賴的 Module,遞歸地進行編譯處理
  • 輸出:將編譯後的 Module 組合成 Chunk,將 Chunk 轉換成文件,輸出到文件系統中(Chunk 就是打包過程當中,入口模塊引用其餘模塊,模塊再引用模塊,這個關係鏈鏈接的 Module 就造成了 Chunk

在這個過程當中,Webpack 會在特定的時間點廣播出特定的事件,插件在監聽到感興趣的事件後會執行特定的邏輯,而且插件能夠調用 Webpack 提供的 API 改變 Webpack 的運行結果。

六 entry

返回目錄

指定打包⼊口文件,有三種不一樣的形式:string | object | array

一對一:一個入口、一個打包文件

module.exports = {
  entry: './src/index.js'
}

多對一:多個入口、一個打包文件

module.exports = {
  entry: [
    './src/index1.js',
    './src/index2.js',
  ]
}

多對多:多個入口、多打包文件

module.exports = {
  entry: {
    'index1': "./src/index1.js",
    'index2': "./src/index2.js"
  }
}

七 output

返回目錄

打包後的文件位置。

module.exports = {
  ...,
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bundle.js",
    filename: "[name].js"
  }
}
  • 能夠指定一個固定的文件名稱,若是是多入口多出口(entry 爲對象),則不能使用單文件出口,須要使用下面的方式
  • 經過 Webpack 內置的變量佔位符:[name]

八 loader

返回目錄

loader 的執行順序是從右向左執行的,也就是後面的 loader 先執行。

假若有配置:

// webpack.config.js
module.exports = {
  //...
  module: {
    rules: [
      {
        test: /\.(le|c)ss$/,
        use: ['style-loader', 'css-loader', 'less-loader'],
        exclude: /node_modules/,
      },
    ],
  },
};

那就是先處理 less-loader,再處理 css-loader,最後處理 style-loader

8.1 關於文件處理常見的 loader

返回目錄
  • file-loader:當引入的文件是 .png.txt 等時,能夠經過 file-loader 解析項目中的 url 引入。根據配置將文件拷貝到相應的路徑,並修改打包後文件的引入路徑,讓它指向正確的文件。
  • url-loaderurl-loader 封裝了 file-loader 且能夠不依賴於 file-loader 單獨使用,而且能夠配置 limit。對小於 limit 大小的圖片轉換成 Base64,大於 limit 的時候使用 file-loader 裏的方法。

8.2 關於語法檢查常見 loader

返回目錄
  • tslint-loader:經過 TSLint 檢查 TypeScript 代碼
  • eslint-loader:經過 ESLint 檢查 JavaScript 代碼

8.3 關於 HTML 代碼處理常見的 loader

返回目錄
  • html-withimg-loader:處理 HTML 中的圖片

8.4 關於 CSS 代碼處理常見的 loader

返回目錄
  • style-loader:動態建立 style 標籤,將 CSS 代碼插入到 head 中。
  • css-loader:負責處理 @importurl 等語句。例如 import css from 'file.css'url(image.png)
  • postcss-loader:負責進一步處理 CSS 文件,好比添加瀏覽器前綴,壓縮 CSS 等。
  • less-loader:將 .less 文件內容轉換成 CSS。
  • sass-loader:將 .sass 文件內容轉換成 CSS。

8.5 關於 JS 代碼處理常見的 loader

返回目錄
  • babel-loader:將 JS 代碼向低版本轉換,咱們須要使用 babel-loader
  • ts-loader:將 TypeScript 轉換成 JavaScript

九 plugin

返回目錄

9.1 常見 plugin

返回目錄
  • clean-webpack-plugin:打包前自動清理 dist 目錄,防止文件殘留。
  • copy-webpack-plugin:將單個文件或者整個目錄複製到構建目錄
  • mini-css-extract-plugin:將 CSS 抽離出來單獨打包而且經過配置能夠設置是否壓縮。
  • html-webpack-plugin:這個插件能夠配置生成一個 HTML5 文件,其中 script 標籤包含全部 Webpack 包。若是你設置多個入口點,你能夠據此實現多頁面應用打包。

9.2 提升效率的 plugin

返回目錄
  • webpack-dashboard:能夠更友好的展現相關打包信息。
  • webpack-merge:提取公共配置,減小重複配置代碼
  • speed-measure-webpack-plugin:簡稱 SMP,分析出 Webpack 打包過程當中 Loader 和 Plugin 的耗時,有助於找到構建過程當中的性能瓶頸。
  • size-plugin:監控資源體積變化,儘早發現問題
  • HotModuleReplacementPlugin:模塊熱替換

十 loader 和 plugin 的區別

返回目錄
  • Loader

Loader 本質上就是一個函數,對接收到的內容進行轉換,返回轉換後的結果。

由於 Webpack 只認識 JavaScript,因此 Loader 就成了翻譯官,對不一樣類型的資源進行處理。

就比如 file-loader 或者 url-loader,配置以後就能夠正確引用 png 等格式的圖片、txt 等格式文件。

又比如 style-loader 以及 css-loader,引用後就能夠對 CSS 內容進行預編譯處理。

  • Plugin

Plugin 就是插件,就比如 jsliang 編寫的 VS Code 插件同樣,Plugin 拓展了 Webpack 的功能。

Plugin 就是在 Webpack 的生命週期中進行各類操做,從而達到使用者目的插件。

就比如 html-webpack-plugin,配合多入口形式使用以後,就能夠實現多頁面應用的功能。

又比如 clean-webpack-plugin 實現打包以前清空 dist 目錄,copy-webpack-plugin 能夠將單個文件或者整個目錄複製到構建目錄。

十一 resolve

返回目錄

resolve 配置 Webpack 如何尋找模塊所對應的文件。

Webpack 內置 JavaScript 模塊化語法解析功能,默認會採用模塊化標準里約定好的規則去尋找,但你能夠根據本身的須要修改默認的規則。

// webpack.config.js
module.exports = {
  //....
  resolve: {
    modules: ['./src/components', 'node_modules'] // 從左到右依次查找
  }
}
  • resolve.modules:配置 Webpack 去哪些目錄下尋找第三方模塊,默認狀況下,只會去 node_modules 下尋找,若是你在項目中某個文件夾下的模塊常常被導入,不但願寫很長的路徑,那麼就能夠經過配置 resolve.modules 來簡化。
  • resolve.alias:配置項經過別名把原導入路徑映射成一個新的導入路徑。
  • resolve.extensions:適配多端的項目中,可能會出現 .web.js, .wx.js,例如在轉 Web 的項目中,咱們但願首先找 .web.js,若是沒有,再找 .jsextensions: ['web.js', '.js']

十二 從 0 開始配置 Webpack

返回目錄

如何從 0 開始配置一個屬於本身的 Webpack 腳手架呢?那就涉及到選型問題。

12.1 技術選型

返回目錄
  • 移動端 || PC
  • MPA || SPA
  • HTML || 模板引擎
  • CSS || 預處理器
  • JavaScript ES5 || ES6
  • 本地發佈服務(數據模擬)
  • React || Vue
  • 多人項目 || 單人項目
  • 語法規範 Eslint
  • 單元測試
  • 提交規範

12.2 Loader 配置 - 處理 CSS、Less

返回目錄
  • use: ['style-loader', 'css-loader', 'postcss-loader', 'less-loader']
  • less less-loader:解析 .less 文件
  • postcss-loader autoprefixer:對 flex 佈局等進行前綴補充

12.3 Loader 配置 - 處理圖片

返回目錄
  • file-loader:解析 .txt.png.md 等格式文件
  • url-loaderlimit: 1024,判斷大小是否處理成 base64 格式

12.4 Loader 配置 - 處理字體

返回目錄
  • url-loader

12.5 Loader 配置 - MPA 多頁面打包通用方案

返回目錄
  • 安裝 glob
  • entryhtmlwebpackplugin 動態生成

12.6 SourceMap

返回目錄
  • 開發環境配置:devtool: "cheap-module-eval-source-map
  • 線上生成配置(不推薦):devtool: "cheap-module-source-map"

12.7 WebpackDevServer

返回目錄
  • 安裝
  • 配置:devServer
  • HMR(熱模塊替換,Hot Module Replacement)
  • 開啓 JS 模塊的 HMR,須要 Webpack 配合

12.8 babel 解析

返回目錄
  • 安裝:npm i babel-loader @babel/core @babel-preset-env -D

    • @babel/corebabel 核心
    • babel-loaderbabelWebpack 的鏈接橋樑
    • babel-preset-env:輸出什麼樣的代碼,用它來解決
  • babel-loader:解析 ES6+
  • @babel/polyfill:墊片。包含全部 ES6+ 新特性代碼
  • .babelrc

12.9 React

返回目錄
  • 安裝:react react-dom
  • 使用:@babel/preset-react

12.10 性能優化

返回目錄
  • 縮小 loader 的文件範圍:loaderinclude 配置,能夠指定 src 目錄,減小檢查範圍。
  • 優化 resolve.modules 配置:配置 Webpack 去哪些目錄下尋找第三方模塊,默認 node_modules
  • 分離 CSS:MiniCssExtractPlugin
  • hashchunkhashcontenthash 區別

    • hash 做用域 JS、CSS,圖片的 hash 有區別,每次打包構建都會變化一次。
    • chunkhashchunk 爲單位,修改了那部分就改動哪部分的 hash。(同時依賴的模塊也會改變 hash
    • contenthash 只有本身內容發生改變,才發生改變(區別於 chunkhash
    • 因此 JS 適用於 chunkhash;CSS 適用於 contenthash;Image 適用於 hash
  • 壓縮 CSS:optimize-css-assets-webpack-plugincssnano
  • 壓縮 HTML:html-webpack-plugin
  • 壓縮圖片:img-webpack-loader
  • 分離 Webpack 配置:分離 base.configdev.configmpa.configpro.config 4 個,經過 merge 進行 config 配置的合併

更多看這裏:

Webpack 性能優化

12.11 其餘

返回目錄
  • 如何簡單編寫一個 Webpack 解析器
  • 如何編寫一個 Webpack loader
  • 如何編寫一個 Webpack plugin

十三 知識補充:懶加載

返回目錄

懶加載或者按需加載,是一種很好的優化網頁或應用的方式。

這種方式其實是先把你的代碼在一些邏輯斷點處分離開,而後在一些代碼塊中完成某些操做後,當即引用或即將引用另一些新的代碼塊。

這樣加快了應用的初始加載速度,減輕了它的整體體積,由於某些代碼塊可能永遠不會被加載。

13.1 代碼分割

返回目錄

代碼分割(code splitting)是指:將腳本中無需當即調用的代碼在代碼構建時轉變爲異步加載的過程。

在 Webpack 構建時,會避免加載已聲明要異步加載的代碼,異步代碼會被單獨分離出一個文件,當代碼實際調用時被加載至頁面。

代碼分割技術的核心是 異步加載資源

可喜的是,瀏覽器容許咱們這麼作,W3C stage 3 規範: whatwg/loader 對其進行了定義:你能夠經過 import() 關鍵字讓瀏覽器在程序執行時異步加載相關資源。

在 Vue 中,能夠直接使用 import() 關鍵字作到這一點,而在 React 中,你須要使用 react-loadable 去完成一樣的事。

13.2 實現原理

返回目錄
  1. 將須要進行懶加載的子模塊單獨打包成文件(children chunk
  2. 藉助函數來實現延遲進行子模塊的加載代碼(import
print.js
console.log('輸出 1');

export default () => {
  console.log('輸出 2');
};
index.js
const btn = document.querySelector('.btn');
btn.onclick = import('./print.js').then((module) => {
  const print = module.default;
  print();
});

13.3 Vue 按需加載

返回目錄

Vue 的特色就是 SPA - Single Page Application(單頁應用程序)。

只有第一次加載頁面,之後的每次頁面切換,只須要進行組件替換。

它減小了請求次數,加快頁面響應速度,下降對服務器壓力等等。

可是,由於 Vue 是 SPA,因此首頁第一次加載時會把全部組件以及組件相關資源所有加載,從而致使網站首頁打開速度變慢,下降用戶體驗。

Vue 項目中,能夠結合 Webpack,在 vue-router 經過 import 進行動態加載:

const routes = [{
  path: '/',
  name: 'Home',
  component: () => import('../views/Home.vue')
}];

十四 知識補充:熱更新

返回目錄

刷新咱們通常分爲兩種:

  • 一種是頁面刷新,不保留頁面狀態,就是簡單粗暴,直接 window.location.reload()
  • 另外一種是基於 WDSWebpack-dev-server)的模塊熱替換,只須要局部刷新頁面上發生變化的模塊,同時能夠保留當前的頁面狀態,好比複選框的選中狀態、輸入框的輸入等。

Webpack 的熱更新又稱熱替換(Hot Module Replacement),縮寫爲 HMR

這個機制能夠作到不用刷新瀏覽器而將新變動的模塊替換掉舊的模塊。

14.1 開啓熱更新

返回目錄

在 Webpack 的 webpack.config.js 中:

  1. 配置 devServerhottrue
  2. plugins 中增長 new webpack.HotModuleReplacementPlugin()
// webpack.config.js
const webpack = require('webpack');
module.exports = {
  //....
  devServer: {
    hot: true
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin() // 熱更新插件
  ]
}

而且在入口文件配置:

if(module && module.hot) {
  module.hot.accept()
}

此時修改代碼的時候,只有對應部分的內容纔會相應更新。

14.2 熱更新原理

返回目錄

HMR 的核心就是客戶端從服務端拉去更新後的文件,準確的說是 chunk diffchunk 須要更新的部分)。

實際上 webpack-dev-serverWDS)與瀏覽器之間維護了一個 Websocket,當本地資源發生變化時,WDS 會向瀏覽器推送更新,並帶上構建時的 hash,讓客戶端與上一次資源進行對比。

客戶端對比出差別後會向 WDS 發起 Ajax 請求來獲取更改內容(文件列表、hash),這樣客戶端就能夠再借助這些信息繼續向 WDS 發起 jsonp 請求獲取該 chunk 的增量更新。

後續的部分(拿到增量更新以後如何處理?哪些狀態該保留?哪些又須要更新?)由 HotModulePlugin 來完成,提供了相關 API 以供開發者針對自身場景進行處理,像 react-hot-loadervue-loader 都是藉助這些 API 實現 HMR

十五 知識補充:3 種 hash

返回目錄

文件指紋是打包後輸出的文件名的後綴,對應着 3 種 hash

  • hash 是跟整個項目的構建相關,只要項目裏有文件更改,整個項目構建的 hash 值都會更改,而且所有文件都共用相同的 hash 值。(粒度整個項目)
  • chunkhash 是根據不一樣的入口進行依賴文件解析,構建對應的 chunk(模塊),生成對應的 hash 值。只有被修改的 chunk(模塊)在從新構建以後纔會生成新的 hash 值,不會影響其它的 chunk。(粒度 entry 的每一個入口文件)
  • contenthash 是跟每一個生成的文件有關,每一個文件都有一個惟一的 hash 值。當要構建的文件內容發生改變時,就會生成新的 hash 值,且該文件的改變並不會影響和它同一個模塊下的其它文件。(粒度每一個文件的內容)

十六 知識補充:source map

返回目錄

source map 是將編譯、打包、壓縮後的代碼映射回源代碼的過程。打包壓縮後的代碼不具有良好的可讀性,想要調試源碼就須要 soucre map

map 文件只要不打開開發者工具,瀏覽器是不會加載的。

線上環境通常有三種處理方案:

  • hidden-source-map:藉助第三方錯誤監控平臺 Sentry 使用
  • nosources-source-map:只會顯示具體行數以及查看源代碼的錯誤棧。安全性比 source map
  • source map:經過 nginx 設置將 .map 文件只對白名單開放(公司內網)

注意:避免在生產中使用 inline-eval-,由於它們會增長 bundle 體積大小,並下降總體性能。

十七 知識補充:Webpack 打包原理

返回目錄

在 Webpack 簡單實現中,簡單的作了下如何將一份代碼進行打包:

  1. 利用 babel 完成代碼轉換,並生成單個文件的依賴
  2. 生成依賴圖譜
  3. 生成最後打包代碼

十八 參考文獻

返回目錄

本系列參考文獻有 51 篇文章。

18.1 Webpack 系列文章

返回目錄

其餘

2020 年文章

2019 年文章

2018 年文章

2017 文章

18.2 Webpack 性能優化

返回目錄

2019 年文章

2018 年文章

2017 年文章

18.3 Scope Hoisting

返回目錄

18.4 Tree Shaking

返回目錄

18.5 懶加載

返回目錄

jsliang 的文檔庫由 梁峻榮 採用 知識共享 署名-非商業性使用-相同方式共享 4.0 國際 許可協議 進行許可。<br/>基於 https://github.com/LiangJunrong/document-library 上的做品創做。<br/>本許可協議受權以外的使用權限能夠從 https://creativecommons.org/licenses/by-nc-sa/2.5/cn/ 處得到。
相關文章
相關標籤/搜索