本節課重點內容#
- 理解前端 "工程化" 概念、工具、目標
- 一個團隊總要有那麼幾個人熟悉 Webpack, 某種程度上可以成為個人的核心競爭力
- 高階前端必經之路
課程目標:
- 理解 Webpack 的基本用法
- 通過介紹 Webpack 功能、Loader 與 Plugin 組件設計,建立一個知識體系
- 不會事無巨細,介紹 Webpack 所有
- 也不是深入源碼,講解底層實現原理
什麼是 Webpack#
前端項目由什麼構成?—— 資源
舊時代手動管理這些資源,但有以下幾個對開發效率影響非常大的缺點:
- 依賴手工,比如有 50 個 JS 文件……操作過程繁瑣
- 當代碼文件之間有依賴的時候,就得嚴格按依賴順序書寫
- 開發與生產環境一致,難以接入 TS 或 JS 新特性
- 比較難接入 Less、Sass 等工具
- JS、 圖片、CSS 資源管理模型不一致
後來,出現了很多前端工程化工具,特別是 Webpack
Web 本質上是一種前端資源編譯、打包工具
- 多份資源文件打包成一個 Bundle
- 支持
- Babel、Eslint、 TS、 CoffeScript、Less、 Sass
- 支持模塊化處理 css、圖片等資源文件
- 支持 HMR + 開發服務器
- 支持持續監聽、持續構建
- 支持代碼分離
- 支持 Tree-shaking
- 支持 Sourcemap
- ...
Webpack 打包核心流程#
示例#
-
安裝(注意用管理員權限打開命令行)
npm i -D webpack webpack-cli
-
編輯配置文件 webpack.config.js
module.exports = { entry: 'main.js', // 定義目前項目的入口文件 output: { // 定義目前項目的輸出文件 filename: "[name].js", path: path.join(__dirname, "./dist"), }, module: {// 定義一些loader相關的內容,可在下文看到 rules: [{ test: /\.less$/i, use: ['style-loader', 'css-loader', 'less-loader'] }] } }
-
執行編譯命令
npx webpack
步驟#
Entry => Dependencies Lookup => Transform => Bundle => Output
極度簡化版:
- 從 entry 中的入口文件開始啟動編譯
- 依賴解析:根據
require
或者import
等語句找到依賴資源 - 根據
module
配置,調用資源轉移器將非 JS 資源編譯為 JS 內容,直至所有資源處理完畢 - 資源合併打包:將轉譯後的資源內容合併打包為可直接在瀏覽器運行的 JS 文件
模塊化 + 一致性
- 多個文件資源合併成一個,減少 http 請求數
- 支持模塊化開發
- 支持高級 JS 特性
- 支持 Typescript、 CoffeeScript 方言
- 統一圖片、CSS、字體等其他資源的處理模型
- Etc...
關鍵配置項(如何使用?)#
關於 Webpack 的使用方法,基本都圍繞 配置 展開,而這些配置大致可劃分為兩類:
- 流程類:作用於流程中某個或若干個環節,直接影響打包效果的配置項
- 工具類:主流程之外,提供更多工程化能力的配置項
ps:官網文檔確實,看不太懂()
配置總覽:
按使用頻率,主要有以下幾大配置項
-
entry/output—— 程序輸入輸出,必需的
-
module/plugins
-
如圖,比如我這個項目需要加載 less 文件,需要導入以下 loader 等
-
-
mode
-
watch/devServer/devtool
使用 Webpack 處理 CSS/less 等#
-
安裝 Loader
npm add -D css-loader style-loader
-
添加 module 處理 css 文件
思考題#
- Loader 有什麼作用?為什麼這裡需要用到 css-loader、style-loader
- 與舊時代,在 HTML 文件中維護 CSS 相比,這種方式會有什麼優劣處?
- 有沒有接觸過 Less、Sass、 Stylus 這一類 CSS 預編譯框架?如何在 Webpack 接入這些工具?
- 答:接觸過 less
參考資料:
使用 Webpack 接入 Babel#
-
安裝依賴
npm -D @babel/core @babel/preset-env babel-loader
-
聲明入口
entry
& 產物出口output
-
添加 module 處理 css 文件
module:{ rules:[ { test:/\.js?$/, use:[{ loader: 'babel-loader', options: { presets: [ [ '@babel/preset-env' ] } } }] } ] }
-
執行
npx webpack
思考題#
- Babel 具體有什麼功能
- Babel 與 Webpack 分別解決了什麼問題?為何兩者能協作到一起了?
參考資料:
使用 Webpack 生成 html#
-
安裝依賴
npm i -D html-webpack-plugin
-
配置
const path = require( "path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: "./src/index",
output: {
filename:" [name]. js",
path: path.join(__dirname, "./dist"),
},
plugins: [new HtmlWebpackPlugin()]
};
- 執行
npx webpack
思考題#
- 相比於手工維護 HTML 內容,這種自動生成的方式有什麼優缺點?
參考資料:
使用 Webpack——HMR#
Hot Module Replacement—— 模塊熱替換(寫的代碼會被立刻更新到瀏覽器上~)
-
開啟 HMR
module.exports={ // ... devServer: { hot:true; // 必需 } };
-
啟動 webpack
原理可參考:Webpack 原理系列十:HMR 原理全解析 (qq.com)
使用 Webpack——Tree-Shaking#
Tree-Shaking - 樹搖,用於刪除Dead Code
Dead Code
- 代碼沒有被用到,不可到達
- 代碼的執行結果不會被用到
- 代碼只讀不寫
Tree-Shaking
- 模塊導出了,但未被其他模塊使用
開啟步驟?
- mode: "production"
- optimization: {usedExports: true}
module.exports = {
entry: "./src/ index",
mode: "production",
devtool: false,
optimization: {
usedExports: true,
},
};
ps:對工具類庫如 Lodash 有奇效,能大大減小產物體積
其他工具#
- 緩存
- Sourcemap (前面的課程中有提過)
- 性能監控
- 日誌
- 代碼壓縮
- 分包
- ...
思考題#
- 除上面提到的內容,還有哪些配置可劃分為 “流程類” 配置?
- 工具類配置具體有什麼作用?包括 devtool/cache/stat 等
理解 Loader#
Loader 核心功能:將非 JS 資源轉換為 JS 資源
-
安裝 Loader
npm add -D css-loader style-loader less-loader
-
添加 module 處理 less 文件
module.exports = {
module: {
rules: [
{
test: /\.less$/i,
use:[
"style-loader",
"css-loader",
"less-loader",
],
},
],
},
};
認識 Loader:鏈式調用#
- less-loader: 實現less => css 的轉換
- css-loader: 實現css => js 的轉換,將 CSS 包裝成類似 module.exports = "${css}" 的內容,包裝後的內容符合 JavaScript 語法
- style-loader:將 css 模塊包進 require 語句,並在運行時調用injectStyle等函數將內容注入到頁面的 style 標籤
認識 Loader:其他特性#
特點
- 鏈式執行
- 支持異步執行
- 分 normal、pitch 兩種模式
- 參考:Webpack 原理系列七:如何編寫 loader (qq.com)
module.exports = function(source, sourceMap?, data?) {
// source 為loader的輸入
//可能是文件內容,也可能是上個loader處理結果
return source;
};
常用 Loader#
- 站在使用角度,建議掌握這些常見 Loader 的功能、配置方法
思考題#
- Loader 輸入是什麼?要求的輸出是什麼?
- Loader 的鏈式調用是什麼意思?如何串聯多個 Loader?
- Loader 中如何處理異步場景?要拋一個異常的話要怎麼拋
理解插件 Plugin#
什麼是插件#
- 很多知名工具,如:
- VS Code、Web Storm、Chrome、Firefox
- Babel、Webpack、 Rollup、 Eslint
- Vue、Redux、 Quill、 Axios
- 等等,都設計了所謂 " 插件” 架構,為什麼?
插件可以提升整個應用的拓展性
假設一個應用沒有任何插件,整個就是特別複雜的過程,那麼:
- 新人需要了解整個流程細節,上手成本高
- 功能迭代成本高,牽一發動全身
- 功能僵化,作為開源項目而言缺乏成長性
心智成本高 => 可維護性低 => 生命力弱
插件架構精髓:對擴展開放,對修改封閉,其實就是開閉原則
甚至,Webpack 本身的很多功能也是基於插件實現的
如何編寫插件#
首先:插件圍繞 鉤子 展開
class SomePlugin {
apply(compile) {
compiler.hooks.thisCompilation.tap('SomePlugin', (compilation) => {
})
}
}
鉤子#
-
時機:編譯過程的特定節點,Webpack 會以鉤子形式通知插件此刻正在發生什麼事情;
-
上下文:通過 tapable 提供的回調機制,以參數方式傳遞上下文信息;
-
交互:在上下文參數對象中附帶了很多存在 side effect 的交互接口,插件可以通過這些接口改變
時機:compier.hooks.compilation
參數:compilation 等
交互:dependencyFactories.set
思考題#
- Loader 與插件有什麼區同點?
- " 鉤子 “有什麼作用?如何監聽鉤子函數?
參考資料:
如何學習 Webpack#
入門級:學會靈活應用#
- 理解打包流程
- 熟練掌握常用配置項、Loader、 插件的使用方法,能夠靈活搭建集成 Vue、React、 Babel、 Eslint、 Less、 Sass、 圖片處理等工具的 Webpack 環境
- 掌握常見腳手架工具的用法,例如: Vue-cli、 create-react- app、@angular/cli
進階:學會擴展 Webpack#
- 理解 Loader、 Plugin 機制,能夠自行開發 Webpack 組件
- 理解常見性能優化手段,並能用於解決實際問題
- 理解前端工程化概念與生態現狀
大師:源碼級理解 Webpack 打包編譯原理#
- 閱讀源碼,理解 Webpack 編譯、打包原理,甚至能夠參與共建
總結感想#
這節課詳細講了 Webpack 的 Webpack 的作用、配置結構及其關鍵配置項,還講了 Loader 中的鏈式調用、插件的實現原理與一些其他特性等,可以看出老師對 Webpack 的研究非常詳細透徹,課上都是直接進行調試進入 loader 內部等幫助我們理解。老師還總結了 Webpack 知識體系:Webpack 5 知識體系 - GitMind。
Q&A#
Q:面試要掌握到什麼程度
A:網上很多面試主要圍繞三種主題
- Loader 有什麼作用?怎麼寫 loader?常用 loader 有哪些
- 常用 loader:css-loader、style-loader、vue-loader、file-loader、eslint-loader、babel-loader 等
- 插件有什麼用?怎麼寫插件?編譯原理?
- Bundle、chunk、module 分別是什麼含義?
一些資源:深入淺出 Webpack
本文引用的大部分內容來自范文杰老師的課,歡迎關注老師的公眾號:Tecvan