JAVASCRIPT前端框架WEBPACK5從入門到精通
當前位置:點晴教程→知識管理交流
→『 技術文檔交流 』
前言 webpack是什么? 摘自官網的一段話:webpack 是一個用于現(xiàn)代 JavaScript 應用程序的 靜態(tài)模塊打包工具。當 webpack 處理應用程序時,它會在內部從一個或多個入口點構建一個 依賴圖(dependency graph),然后將你項目中所需的每一個模塊組合成一個或多個 bundles,它們均為靜態(tài)資源,用于展示你的內容。 官網鏈接:https://webpack.docschina.org/concepts/? 為什么需要打包程序 在日常的開發(fā)中,我們會使用框架(React、Vue),ES6 模塊化語法,Less/Sass 等 css 預處理器等語法進行開發(fā)。這樣的代碼要想在瀏覽器運行必須經過編譯成瀏覽器能識別的 JS、Css 等語法,才能運行,所以我們需要打包工具幫我們做完這些事。除此之外,打包工具還能壓縮代碼、做兼容性處理、提升代碼性能等。 正文 概念 在這里我將webpack為了幾個要點,也是webpack核心的幾個點 entry(入口) 需要打包的入口文件,可以為一個入口,也可以為多個入口(有幾個入口就有幾個輸出),Webpack 本身功能比較少,只能處理 js 資源,一旦遇到 css 等其他資源就會報錯output(輸出) 可以通過配置 output 選項,告知 webpack 如何向硬盤寫入編譯文件。注意,即使可以存在多個 entry 起點,但只能指定一個 output 配置。loaders(模塊解析器) loader 用于對模塊的源代碼進行轉換。loader 可以使你在 import 或 “l(fā)oad(加載)” 模塊時預處理文件。因此,loader 類似于其他構建工具中“任務(task)”,并提供了處理前端構建步驟的得力方式。loader 可以將文件從不同的語言(如 TypeScript)轉換為 JavaScript 或將內聯(lián)圖像轉換為 data URL。loader 甚至允許你直接在 JavaScript 模塊中 import CSS 文件!Plugins(插件) 插件 是 webpack 的 支柱 功能。Webpack 自身也是構建于你在 webpack 配置中用到的 相同的插件系統(tǒng) 之上!插件目的在于解決 loader 無法實現(xiàn)的其他事mode(模式) 指示 Webpack 使用相應模式的配置。 development 開發(fā)模式:會將 process.env.NODE_ENV 的值設為 development。啟用 NameChunksPlugin 和 NameModulesPlugin。特點是能讓代碼本地調試運行的環(huán)境。 production 生產模式:會將 process.env.NODE_ENV 的值設為 production。啟用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 UglifyJsPlugin。特點是能讓代碼優(yōu)化上線運行的環(huán)境。 起步 新建文件,創(chuàng)建一個新的項目 下載我們的依賴 構建項目目錄文件 webpack_document # 項目根目錄(所有指令必須在這個目錄運行) └── src # 項目源碼目錄 ├── js # js文件目錄 │ ├── count.js │ └── sum.js ├── css # css文件目錄 └── index.js # 項目主文件 創(chuàng)建我們的配置文件webpack.config.js 運行指令 觀察生成的dist的文件,就是我們打包后的文件了 Webpack 將來都通過 webpack.config.js 文件進行配置,來增強 Webpack 的功能 開發(fā)模式介紹 開發(fā)模式顧名思義就是我們開發(fā)代碼時使用的模式。 這個模式下我們主要做兩件事: 編譯代碼,使瀏覽器能識別運行開發(fā)時我們有樣式資源、字體圖標、圖片資源、html 資源等,webpack 默認都不能處理這些資源,所以我們要加載配置來編譯這些資源代碼質量檢查,樹立代碼規(guī)范提前檢查代碼的一些隱患,讓代碼運行時能更加健壯。提前檢查代碼規(guī)范和格式,統(tǒng)一團隊編碼風格,讓代碼更優(yōu)雅美觀。 處理樣式資源 由于webpack只能處理js、json文件,并不能處理css文件,所以我們需要借助相應loader解析器來增強我們的功能,在webpack的官網中,為我們提供了常用的loader,如果不能滿足我們的日常需要,也可以到社區(qū)中去尋找想要的loader [webpack官網loader] 處理css資源 下載 說明 css-loader:負責將 Css 文件編譯成 Webpack 能識別的模塊style-loader:會動態(tài)創(chuàng)建一個 Style 標簽,里面放置 Webpack 中 Css 模塊內容 此時樣式就會以 Style 標簽的形式在頁面上生效 配置 使用 在src目錄下創(chuàng)建我們的css文件,并在index.js中進行引入,然后進行打包,觀察我們的dist文件里面的輸出結果 為了方便我們觀察效果,我們創(chuàng)建我們靜態(tài)頁面,并引入我們打包后dist文件下面的js文件,文件目錄結構如下 頁面效果如下: 處理less資源 下載 說明 less-loader:負責將 Less 文件編譯成 Css 文件 配置 使用 在src目錄下,創(chuàng)建我們的less文件夾,并生成index.less文件,寫入樣式,在index.js,引入我們的less文件,然后在pubilc的index.html文件中,創(chuàng)建對應的box文件,執(zhí)行npx webpack,觀察打包后的結果 頁面效果如下: 處理sass/scss資源 下載 說明 sass-loader:負責將 Sass 文件編譯成 css 文件sass:sass-loader 依賴 sass 進行編譯 配置 使用 在src目錄下,創(chuàng)建我們的sass文件夾,并生成index.sass 和 index.scss 文件,寫入樣式,在index.js,引入我們的sass、scss文件,然后在pubilc的index.html文件中,創(chuàng)建對應的box文件,執(zhí)行npx webpack,觀察打包后的結果 頁面效果如下: 處理 Styl 資源 說明 stylus-loader:負責將 Styl 文件編譯成 Css 文件 配置 使用 在src目錄下,創(chuàng)建我們的styl文件夾,并生成index.styl 和 index.styl 文件,寫入樣式,在index.js,引入我們的styl文件,然后在pubilc的index.html文件中,創(chuàng)建對應的box文件,執(zhí)行npx webpack,觀察打包后的結果 頁面效果如下: 資源模塊 以下摘自官網的一段話 資源模塊(asset module)是一種模塊類型,它允許使用資源文件(字體,圖標等)而無需配置額外 loader。 在 webpack 5 之前,通常使用: raw-loader 將文件導入為字符串 url-loader 將文件作為 data URI 內聯(lián)到 bundle 中 file-loader 將文件發(fā)送到輸出目錄 資源模塊類型(asset module type),通過添加 4 種新的模塊類型,來替換所有這些 loader: asset/resource 發(fā)送一個單獨的文件并導出 URL。之前通過使用 file-loader 實現(xiàn)。 asset/inline 導出一個資源的 data URI。之前通過使用 url-loader 實現(xiàn)。 asset/source 導出資源的源代碼。之前通過使用 raw-loader 實現(xiàn)。 asset 在導出一個 data URI 和發(fā)送一個單獨的文件之間自動選擇。之前通過使用 url-loader,并且配置資源體積限制實現(xiàn)。 當在 webpack 5 中使用舊的 assets loader(如 file-loader/url-loader/raw-loader 等)和 asset 模塊時,你可能想停止當前 asset 模塊的處理,并再次啟動處理,這可能會導致 asset 重復,你可以通過將 asset 模塊的類型設置為 ‘javascript/auto’ 來解決。 處理圖片資源 配置 使用 在src目錄下,創(chuàng)建我們的images文件夾,添加圖片文件,筆者這里添加了jpe,png,gif三種格式的圖片,分別在不同的樣式中進行引入,執(zhí)行npx webpack,觀察打包后的結果 此時如果查看 dist 目錄的話,會發(fā)現(xiàn)多了三張圖片資源 因為 Webpack 會將所有打包好的資源輸出到 dist 目錄下 為什么樣式資源沒有呢? 因為經過 style-loader 的處理,樣式資源打包到 main.js 里面去了,所以沒有額外輸出出來 頁面效果 圖片資源處理優(yōu)化 將小于某個大小的圖片轉化成 data URI 形式(Base64 格式) 優(yōu)點:減少請求數(shù)量 缺點:體積變得更大,但是10kb以下的圖片,轉換為base64格式的情形下,只有增加1-2kb(所以我們出來10kb以下的) 配置 修改輸出資源的名稱和路徑 我們發(fā)現(xiàn)打包完成后,圖片直接輸出到dist的根目錄下了,沒有規(guī)范起來,我們希望輸出到dist的static/images目錄下,同時js文件輸出到dist的static/js目錄下,這樣就比較規(guī)范了 配置 使用 通過以上配置后,dist文件輸出的文件被規(guī)范起來了,這個時候需要修改一下我們index.html的引入路徑,打開頁面也是同樣的效果 處理字體圖標資源 當我們在項目中使用字體圖標的時候,我們也希望打包的時候,將這一部分內容進行打包輸出 配置 // path 為 Node.js的核心模塊,專門用來處理文件路徑 const path = require("path"); module.exports = { // 入口 entry: { // 需要一個相對路徑 index: "./src/index.js", }, // 輸出 output: { // 需要一個絕對路徑 path: path.resolve(__dirname, "dist"), clean: true, filename: "static/js/index.js", // 將 js 文件輸出到 static/js 目錄中 }, // 解析器 module: { rules: [ { test: /\.css$/, // loader的執(zhí)行順序是從右往左的,所以這里先寫style-loader,再寫css-loader // 如果只使用一個loader的話,可以使用loader屬性代替use,如下 // loader:"style-loader" use: ["style-loader", "css-loader"], }, { // 正則匹配所有已.less文件結尾的文件 test: /\.less$/, // loader的執(zhí)行順序是從右往左的,所以這里先寫style-loader,再寫css-loader // 如果只使用一個loader的話,可以使用loader屬性代替use,如下 // loader:"style-loader" use: ["style-loader", "css-loader", "less-loader"], }, { test: /\.s[ac]ss$/, use: ["style-loader", "css-loader", "sass-loader"], }, { test: /\.styl$/, use: ["style-loader", "css-loader", "stylus-loader"], }, { test: /\.(png|jpe?g|gif|webp)$/, type: "asset", // 類似于 module.generator,你可以用 module.parser 在一個地方配置所有解析器的選項。 parser: { // 如果一個模塊源碼大小小于 maxSize,那么模塊會被作為一個 Base64 編碼的字符串注入到包中, 否則模塊文件會被生成到輸出的目標目錄中。 dataUrlCondition: { maxSize: 10 * 1024, // 小于10kb的圖片會被base64處理 }, }, // 可以使用 module.generator 在一個地方配置所有生成器的選項 generator: { // 將圖片文件輸出到 static/imgs 目錄中 // 將圖片文件命名 [hash:8][ext][query] // [hash:8]: hash值取8位 // [ext]: 使用之前的文件擴展名 // [query]: 添加之前的query參數(shù) filename: "static/imgs/[hash:8][ext][query]", }, }, { test: /\.(ttf|woff2?|svg)$/, // 發(fā)送一個單獨的文件并導出 URL type: "asset/resource", generator: { filename: "static/media/[hash:8][ext][query]", }, }, ], }, plugins: [], mode: "production", }; 使用 在src目錄下,創(chuàng)建我們的fonts文件夾,在阿里巴巴適量庫中下載字體圖標(筆者這里為了演示,使用的是font-class的形式),然后引入到fonts文件夾中,在index.js,然后在pubilc的index.html文件中,創(chuàng)建對應的span標簽,添加class類名,執(zhí)行npx webpack,觀察打包后的結果 頁面效果 處理其他資源 開發(fā)中可能還存在一些其他資源,如音視頻等,我們也一起處理了 就是在處理字體圖標資源基礎上增加其他文件類型,統(tǒng)一處理即可 配置 js資源處理 這里有同學可能會問,js 資源 Webpack 不能已經處理了嗎,為什么我們還要處理呢? 原因是 Webpack 對 js 處理是有限的,只能編譯 js 中 ES 模塊化語法,不能編譯其他語法,導致 js 不能在 IE 等瀏覽器運行,所以我們希望做一些兼容性處理。 其次開發(fā)中,團隊對代碼格式是有嚴格要求的,我們不能由肉眼去檢測代碼格式,需要使用專業(yè)的工具來檢測。 針對 js 兼容性處理,我們使用 Babel 來完成 針對代碼格式,我們使用 Eslint 來完成 我們先完成 Eslint,檢測代碼格式無誤后,在由 Babel 做代碼兼容性處理 ESLint 介紹 簡介:可組裝的 JavaScript 和 JSX 檢查工具。 這句話意思就是:它是用來檢測 js 和 jsx 語法的工具,可以配置各項功能 我們使用 Eslint,關鍵是寫 Eslint 配置文件,里面寫上各種 rules 規(guī)則,將來運行 Eslint 時就會以寫的規(guī)則對代碼進行檢查 配置文件 配置文件由很多種寫法: .eslintrc.*:新建文件,位于項目根目錄 .eslintrc .eslintrc.js .eslintrc.json 區(qū)別在于配置格式不一樣 package.json 中 eslintConfig:不需要創(chuàng)建文件,在原有文件基礎上寫 ESLint 會查找和自動讀取它們,所以以上配置文件只需要存在一個即可 具體配置 我們以.eslintrc.js為例子進行配置 具體eslint的配置參考官網和規(guī)則文檔,這里給出相關鏈接 ESlint規(guī)則 ESlint官網 下載 npm i eslint-webpack-plugin eslint -D 在webpack中使用 vsocde安裝eslint插件 安裝eslint插件后,配置需要忽略檢查的文件 // .eslintignore # 忽略dist目錄下所有文件 dist Babel 介紹 JavaScript 編譯器。 主要用于將 ES6 語法編寫的代碼轉換為向后兼容的 JavaScript 語法,以便能夠運行在當前和舊版本的瀏覽器或其他環(huán)境中 配置文件 配置文件由很多種寫法: babel.config.*:新建文件,位于項目根目錄 babel.config.js babel.config.json .babelrc.*:新建文件,位于項目根目錄 .babelrc .babelrc.js .babelrc.json package.json 中 babel:不需要創(chuàng)建文件,在原有文件基礎上寫 Babel 會查找和自動讀取它們,所以以上配置文件只需要存在一個即可 具體配置 我們以babel.config.js為例子 // babel.config.js module.exports = { // 預設 presets: [], }; presets 預設 簡單理解:就是一組 Babel 插件, 擴展 Babel 功能 @babel/preset-env: 一個智能預設,允許您使用最新的 JavaScript。 @babel/preset-react:一個用來編譯 React jsx 語法的預設 @babel/preset-typescript:一個用來編譯 TypeScript 語法的預設 下載 npm i babel-loader @babel/core @babel/preset-env -D 配置 HTML資源處理 在實際的開發(fā)工作中,我們希望使用一個html模塊,然后將我們打包后的js文件自動引入到html模版中,這樣我們開發(fā)的時候就不用手動引入或者修改打包后的文件了 插件 下載 npm i html-webpack-plugin -D 配置 css資源處理 Css 文件目前被打包到 js 文件中,當 js 文件加載時,會創(chuàng)建一個 style 標簽來生成樣式 這樣對于網站來說,會出現(xiàn)閃屏現(xiàn)象,用戶體驗不好 我們應該是單獨的 Css 文件,通過 link 標簽加載性能才好 提取css為單獨文件 下載插件 npm i mini-css-extract-plugin -D 配置 css兼容性處理 下載 npm i postcss-loader postcss postcss-preset-env -D 配置 配置需要兼容的瀏覽器列表 // package.json "browserslist": [ "last 2 version", "> 1%", "not dead" ] } css壓縮 下載 npm i css-minimizer-webpack-plugin -D 配置 合并樣式處理配置 開發(fā)服務器&自動化 在我們的日常開發(fā)中我們希望我改了代碼后,會自動更新我們更改后的內容,而不用每次打包后才能看到效果,所以webpack提供了一個devServer的配置 下載 npm i webpack-dev-server -D 配置 // path 為 Node.js的核心模塊,專門用來處理文件路徑 const path = require('path') const ESLintWebpackPlugin = require("eslint-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = { // 入口 entry: { // 需要一個相對路徑 index: './src/index.js' }, // 輸出 output: { // 需要一個絕對路徑 path: path.resolve(__dirname, 'dist'), clean: true, filename: "static/js/index.js", // 將 js 文件輸出到 static/js 目錄中 }, // 解析器 module: { rules: [ { test: /\.css$/, // loader的執(zhí)行順序是從右往左的,所以這里先寫style-loader,再寫css-loader // 如果只使用一個loader的話,可以使用loader屬性代替use,如下 // loader:"style-loader" use: ["style-loader", "css-loader"], }, { // 正則匹配所有已.less文件結尾的文件 test: /\.less$/, // loader的執(zhí)行順序是從右往左的,所以這里先寫style-loader,再寫css-loader // 如果只使用一個loader的話,可以使用loader屬性代替use,如下 // loader:"style-loader" use: ["style-loader", "css-loader", "less-loader"], }, { test: /\.s[ac]ss$/, use: ["style-loader", "css-loader", "sass-loader"], }, { test: /\.styl$/, use: ["style-loader", "css-loader", "stylus-loader"], }, { test: /\.(png|jpe?g|gif|webp)$/, type: "asset", // 類似于 module.generator,你可以用 module.parser 在一個地方配置所有解析器的選項。 parser: { // 如果一個模塊源碼大小小于 maxSize,那么模塊會被作為一個 Base64 編碼的字符串注入到包中, 否則模塊文件會被生成到輸出的目標目錄中。 dataUrlCondition: { maxSize: 10 * 1024 // 小于10kb的圖片會被base64處理 } }, // 可以使用 module.generator 在一個地方配置所有生成器的選項 generator: { // 將圖片文件輸出到 static/imgs 目錄中 // 將圖片文件命名 [hash:8][ext][query] // [hash:8]: hash值取8位 // [ext]: 使用之前的文件擴展名 // [query]: 添加之前的query參數(shù) filename: "static/imgs/[hash:8][ext][query]", }, }, { test: /\.(ttf|woff2?|svg)$/, // 發(fā)送一個單獨的文件并導出 URL type: "asset/resource", generator: { filename: "static/media/[hash:8][ext][query]", }, }, { test: /\.js$/, exclude: /node_modules/, // 排除node_modules代碼不編譯 loader: "babel-loader", }, ] }, plugins: [ new ESLintWebpackPlugin({ // 指定檢查文件的根目錄 context: path.resolve(__dirname, "src"), }), new HtmlWebpackPlugin({ // 以 public/index.html 為模板創(chuàng)建文件 // 新的html文件有兩個特點:1. 內容和源文件一致 2. 自動引入打包生成的js等資源 template: path.resolve(__dirname, "public/index.html"), }) ], // 開發(fā)服務器 devServer: { host: "localhost", // 啟動服務器域名 port: "3000", // 啟動服務器端口號 open: true, // 是否自動打開瀏覽器 }, mode: 'production' } 運行 npx webpack serve 并且當你使用開發(fā)服務器時,所有代碼都會在內存中編譯打包,并不會輸出到 dist 目錄下。 開發(fā)時我們只關心代碼能運行,有效果即可,至于代碼被編譯成什么樣子,我們并不需要知道。 開發(fā)模式與生產模式 在我們日常開發(fā)中,我們總是希望我們開發(fā)完成后,今天一系列打包優(yōu)化,性能提升到最好,所以webpack這里也給我們提供了使用不同的配置文件進行配置 開發(fā)模式 適用于dev環(huán)境,配合devServe配置,進行開發(fā),并自動實現(xiàn)代碼更新,所有代碼都會在內存中編譯打包,并不會輸出到 dist 目錄下。 生產模式 生產模式是開發(fā)完成代碼后,我們需要得到代碼將來部署上線。 這個模式下我們主要對代碼進行優(yōu)化,讓其運行性能更好。 優(yōu)化主要從兩個角度出發(fā): 優(yōu)化代碼運行性能優(yōu)化代碼打包速度 使用 我們分別準備兩個配置文件來放不同的配置,分別對應開發(fā)模式和生產模式 目錄配置 ├── webpack-document (項目根目錄) ├── config (Webpack配置文件目錄) │ ├── webpack.dev.js(開發(fā)模式配置文件) │ └── webpack.prod.js(生產模式配置文件) ├── node_modules (下載包存放目錄) ├── src (項目源碼目錄,除了html其他都在src里面) │ └── 略 ├── public (項目html文件) │ └── index.html ├── .eslintrc.js(Eslint配置文件) ├── babel.config.js(Babel配置文件) └── package.json (包的依賴管理配置文件) 生產不同的webpack配置,并做里面的絕對路徑進行相應的修改 webpack.dev.js配置 // path 為 Node.js的核心模塊,專門用來處理文件路徑 const path = require('path') const ESLintWebpackPlugin = require("eslint-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); // 獲取處理樣式的Loaders const getStyleLoaders = (preProcessor) => { return [ MiniCssExtractPlugin.loader, "css-loader", { loader: "postcss-loader", options: { postcssOptions: { plugins: [ "postcss-preset-env", // 能解決大多數(shù)樣式兼容性問題 ], }, }, }, preProcessor, ].filter(Boolean); }; module.exports = { // 入口 entry: { // 需要一個相對路徑 index: './src/index.js' }, // 輸出 output: { path: undefined, // 開發(fā)模式沒有輸出,不需要指定輸出目錄 filename: "static/js/main.js", // 將 js 文件輸出到 static/js 目錄中 // clean: true, // 開發(fā)模式沒有輸出,不需要清空輸出結果 }, // Performance 這些選項可以控制 webpack 如何通知「資源(asset)和入口起點超過指定文件限制」。 performance: { hints: false }, // 解析器 module: { rules: [ { test: /\.css$/, // loader的執(zhí)行順序是從右往左的,所以這里先寫style-loader,再寫css-loader // 如果只使用一個loader的話,可以使用loader屬性代替use,如下 // loader:"style-loader" use: getStyleLoaders(), }, { // 正則匹配所有已.less文件結尾的文件 test: /\.less$/, // loader的執(zhí)行順序是從右往左的,所以這里先寫style-loader,再寫css-loader // 如果只使用一個loader的話,可以使用loader屬性代替use,如下 // loader:"style-loader" use: getStyleLoaders("less-loader"), }, { test: /\.s[ac]ss$/, use: getStyleLoaders("sass-loader"), }, { test: /\.styl$/, use: getStyleLoaders("stylus-loader"), }, { test: /\.(png|jpe?g|gif|webp)$/, type: "asset", // 類似于 module.generator,你可以用 module.parser 在一個地方配置所有解析器的選項。 parser: { // 如果一個模塊源碼大小小于 maxSize,那么模塊會被作為一個 Base64 編碼的字符串注入到包中, 否則模塊文件會被生成到輸出的目標目錄中。 dataUrlCondition: { maxSize: 10 * 1024 // 小于10kb的圖片會被base64處理 } }, // 可以使用 module.generator 在一個地方配置所有生成器的選項 generator: { // 將圖片文件輸出到 static/imgs 目錄中 // 將圖片文件命名 [hash:8][ext][query] // [hash:8]: hash值取8位 // [ext]: 使用之前的文件擴展名 // [query]: 添加之前的query參數(shù) filename: "static/imgs/[hash:8][ext][query]", }, }, { test: /\.(ttf|woff2?|svg)$/, // 發(fā)送一個單獨的文件并導出 URL type: "asset/resource", generator: { filename: "static/media/[hash:8][ext][query]", }, }, { test: /\.js$/, exclude: /node_modules/, // 排除node_modules代碼不編譯 loader: "babel-loader", }, ] }, plugins: [ new ESLintWebpackPlugin({ // 指定檢查文件的根目錄 context: path.resolve(__dirname, "../src"), }), new HtmlWebpackPlugin({ // 以 public/index.html 為模板創(chuàng)建文件 // 新的html文件有兩個特點:1. 內容和源文件一致 2. 自動引入打包生成的js等資源 template: path.resolve(__dirname, "../public/index.html"), }), // 提取css成單獨文件 new MiniCssExtractPlugin({ // 定義輸出文件名和目錄 filename: "static/css/index.css", }), new CssMinimizerPlugin() ], // 開發(fā)服務器 devServer: { host: "localhost", // 啟動服務器域名 port: "3000", // 啟動服務器端口號 open: true, // 是否自動打開瀏覽器 }, mode: 'development' } webpack.prod.js配置 // path 為 Node.js的核心模塊,專門用來處理文件路徑 const path = require('path') const ESLintWebpackPlugin = require("eslint-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); // 獲取處理樣式的Loaders const getStyleLoaders = (preProcessor) => { return [ MiniCssExtractPlugin.loader, "css-loader", { loader: "postcss-loader", options: { postcssOptions: { plugins: [ "postcss-preset-env", // 能解決大多數(shù)樣式兼容性問題 ], }, }, }, preProcessor, ].filter(Boolean); }; module.exports = { // 入口 entry: { // 需要一個相對路徑 index: './src/index.js' }, // 輸出 output: { // 需要一個絕對路徑 path: path.resolve(__dirname, '../dist'), clean: true, filename: "static/js/index.js", // 將 js 文件輸出到 static/js 目錄中 }, // Performance 這些選項可以控制 webpack 如何通知「資源(asset)和入口起點超過指定文件限制」。 performance: { hints: false }, // 解析器 module: { rules: [ { test: /\.css$/, // loader的執(zhí)行順序是從右往左的,所以這里先寫style-loader,再寫css-loader // 如果只使用一個loader的話,可以使用loader屬性代替use,如下 // loader:"style-loader" use: getStyleLoaders(), }, { // 正則匹配所有已.less文件結尾的文件 test: /\.less$/, // loader的執(zhí)行順序是從右往左的,所以這里先寫style-loader,再寫css-loader // 如果只使用一個loader的話,可以使用loader屬性代替use,如下 // loader:"style-loader" use: getStyleLoaders("less-loader"), }, { test: /\.s[ac]ss$/, use: getStyleLoaders("sass-loader"), }, { test: /\.styl$/, use: getStyleLoaders("stylus-loader"), }, { test: /\.(png|jpe?g|gif|webp)$/, type: "asset", // 類似于 module.generator,你可以用 module.parser 在一個地方配置所有解析器的選項。 parser: { // 如果一個模塊源碼大小小于 maxSize,那么模塊會被作為一個 Base64 編碼的字符串注入到包中, 否則模塊文件會被生成到輸出的目標目錄中。 dataUrlCondition: { maxSize: 10 * 1024 // 小于10kb的圖片會被base64處理 } }, // 可以使用 module.generator 在一個地方配置所有生成器的選項 generator: { // 將圖片文件輸出到 static/imgs 目錄中 // 將圖片文件命名 [hash:8][ext][query] // [hash:8]: hash值取8位 // [ext]: 使用之前的文件擴展名 // [query]: 添加之前的query參數(shù) filename: "static/imgs/[hash:8][ext][query]", }, }, { test: /\.(ttf|woff2?|svg)$/, // 發(fā)送一個單獨的文件并導出 URL type: "asset/resource", generator: { filename: "static/media/[hash:8][ext][query]", }, }, { test: /\.js$/, exclude: /node_modules/, // 排除node_modules代碼不編譯 loader: "babel-loader", }, ] }, plugins: [ new ESLintWebpackPlugin({ // 指定檢查文件的根目錄 context: path.resolve(__dirname, "../src"), }), new HtmlWebpackPlugin({ // 以 public/index.html 為模板創(chuàng)建文件 // 新的html文件有兩個特點:1. 內容和源文件一致 2. 自動引入打包生成的js等資源 template: path.resolve(__dirname, "../public/index.html"), }), // 提取css成單獨文件 new MiniCssExtractPlugin({ // 定義輸出文件名和目錄 filename: "static/css/index.css", }), new CssMinimizerPlugin() ], // 開發(fā)服務器 // devServer: { // host: "localhost", // 啟動服務器域名 // port: "3000", // 啟動服務器端口號 // open: true, // 是否自動打開瀏覽器 // }, mode: 'production' } package.json配置 { "name": "webpack_document", "version": "1.0.0", "description": "", "main": "index.js", // 為了方便運行不同模式的指令,我們將指令定義在 package.json 中 scripts 里面 "scripts": { "start": "npx webpack serve --config ./config/webpack.dev.js", "dev": "npx webpack serve --config ./config/webpack.dev.js", "build": "npx webpack --config ./config/webpack.prod.js", "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "devDependencies": { "@babel/core": "^7.20.12", "@babel/preset-env": "^7.20.2", "babel-loader": "^9.1.2", "css-loader": "^6.7.3", "eslint": "^8.33.0", "eslint-webpack-plugin": "^3.2.0", "html-webpack-plugin": "^5.5.0", "less-loader": "^11.1.0", "sass": "^1.58.0", "sass-loader": "^13.2.0", "style-loader": "^3.3.1", "stylus-loader": "^7.1.0", "webpack": "^5.75.0", "webpack-cli": "^5.0.1", "webpack-dev-server": "^4.11.1" } } 開發(fā)模式:npm start 或 npm run dev 生產模式:npm run build 基礎總結 如果你學習到這里,恭喜你現(xiàn)在已經是一個webpack小能手了 兩種開發(fā)模式 開發(fā)模式:代碼能編譯自動化運行 生產模式:代碼編譯優(yōu)化輸出 Webpack 基本功能 開發(fā)模式:可以編譯 ES Module 語法 生產模式:可以編譯 ES Module 語法,壓縮 js 代碼 Webpack 配置文件 5 個核心概念 entry output loader plugins mode devServer 配置 Webpack 腳本指令用法 webpack 直接打包輸出 webpack serve 啟動開發(fā)服務器,內存編譯打包沒有輸出 高級 提升開發(fā)體驗之sourceMap 在開發(fā)過程中,我們運行的代碼是經過webpack編譯后的代碼,如果代碼出錯了,我們想看到代碼的映射為我們自己的代碼,這個時候就需要sourceMap 了 介紹 SourceMap(源代碼映射)是一個用來生成源代碼與構建后代碼一一映射的文件的方案。 它會生成一個 xxx.map 文件,里面包含源代碼和構建后代碼每一行、每一列的映射關系。當構建后代碼出錯了,會通過 xxx.map 文件,從構建后代碼出錯位置找到映射后源代碼出錯位置,從而讓瀏覽器提示源代碼文件出錯位置,幫助我們更快的找到錯誤根源。 使用 通過查看Webpack DevTool 文檔文檔可知,SourceMap 的值有很多種情況. 但實際開發(fā)時我們只需要關注兩種情況即可: 開發(fā)模式:cheap-module-source-map 優(yōu)點:打包編譯速度快,只包含行映射 缺點:沒有列映射 module.exports = { // 其他省略 mode: "development", devtool: "cheap-module-source-map", }; 生產模式:source-map 優(yōu)點:包含行/列映射 缺點:打包編譯速度更慢 module.exports = { // 其他省略 mode: "production", devtool: "source-map", }; 提升打包構建速度 介紹 開發(fā)時我們修改了其中一個模塊代碼,Webpack 默認會將所有模塊全部重新打包編譯,速度很慢。 所以我們需要做到修改某個模塊代碼,就只有這個模塊代碼需要重新打包編譯,其他模塊不變,這樣打包速度就能很快。 HotModuleReplacement(HMR/熱模塊替換):在程序運行中,替換、添加或刪除模塊,而無需重新加載整個頁面。 使用 module.exports = { // 其他省略 devServer: { host: "localhost", // 啟動服務器域名 port: "3000", // 啟動服務器端口號 open: true, // 是否自動打開瀏覽器 hot: true, // 開啟HMR功能(只能用于開發(fā)環(huán)境,生產環(huán)境不需要了) }, }; 此時 css 樣式經過 style-loader 處理,已經具備 HMR 功能了。 但是 js 還不行。 JS 配置需要手動進行屬性需要進行熱模塊使用的功能 // 判斷是否支持HMR功能 if (module.hot) { module.hot.accept("./js/sum"); } 這樣如果我們自己搭建腳手架,每個引入的文件都需要進行這樣更新一次,這里我們推薦使用社區(qū)的loader進行解決,比如:vue-loader, react-hot-loader。 使用oneof對loader進行處理 介紹 打包時每個文件都會經過所有 loader 處理,雖然因為 test 正則原因實際沒有處理上,但是都要過一遍。比較慢。 規(guī)則數(shù)組,當規(guī)則匹配時,只使用第一個匹配規(guī)則。 使用 我們的生產模式和開發(fā)模式都需要配置,這里筆者展示開發(fā)模式的配置 // path 為 Node.js的核心模塊,專門用來處理文件路徑 const path = require('path') const ESLintWebpackPlugin = require("eslint-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); // 獲取處理樣式的Loaders const getStyleLoaders = (preProcessor) => { return [ MiniCssExtractPlugin.loader, "css-loader", { loader: "postcss-loader", options: { postcssOptions: { plugins: [ "postcss-preset-env", // 能解決大多數(shù)樣式兼容性問題 ], }, }, }, preProcessor, ].filter(Boolean); }; module.exports = { // 入口 entry: { // 需要一個相對路徑 index: './src/index.js' }, // 輸出 output: { path: undefined, // 開發(fā)模式沒有輸出,不需要指定輸出目錄 filename: "static/js/main.js", // 將 js 文件輸出到 static/js 目錄中 // clean: true, // 開發(fā)模式沒有輸出,不需要清空輸出結果 }, // Performance 這些選項可以控制 webpack 如何通知「資源(asset)和入口起點超過指定文件限制」。 performance: { hints: false }, devtool: "cheap-module-source-map", // 解析器 module: { rules: [ { // 每個文件只能被其中的一個loader處理 oneOf: [ { test: /\.css$/, // loader的執(zhí)行順序是從右往左的,所以這里先寫style-loader,再寫css-loader // 如果只使用一個loader的話,可以使用loader屬性代替use,如下 // loader:"style-loader" use: getStyleLoaders(), }, { // 正則匹配所有已.less文件結尾的文件 test: /\.less$/, // loader的執(zhí)行順序是從右往左的,所以這里先寫style-loader,再寫css-loader // 如果只使用一個loader的話,可以使用loader屬性代替use,如下 // loader:"style-loader" use: getStyleLoaders("less-loader"), }, { test: /\.s[ac]ss$/, use: getStyleLoaders("sass-loader"), }, { test: /\.styl$/, use: getStyleLoaders("stylus-loader"), }, { test: /\.(png|jpe?g|gif|webp)$/, type: "asset", // 類似于 module.generator,你可以用 module.parser 在一個地方配置所有解析器的選項。 parser: { // 如果一個模塊源碼大小小于 maxSize,那么模塊會被作為一個 Base64 編碼的字符串注入到包中, 否則模塊文件會被生成到輸出的目標目錄中。 dataUrlCondition: { maxSize: 10 * 1024 // 小于10kb的圖片會被base64處理 } }, // 可以使用 module.generator 在一個地方配置所有生成器的選項 generator: { // 將圖片文件輸出到 static/imgs 目錄中 // 將圖片文件命名 [hash:8][ext][query] // [hash:8]: hash值取8位 // [ext]: 使用之前的文件擴展名 // [query]: 添加之前的query參數(shù) filename: "static/imgs/[hash:8][ext][query]", }, }, { test: /\.(ttf|woff2?|svg)$/, // 發(fā)送一個單獨的文件并導出 URL type: "asset/resource", generator: { filename: "static/media/[hash:8][ext][query]", }, }, { test: /\.js$/, exclude: /node_modules/, // 排除node_modules代碼不編譯 loader: "babel-loader", }, ] } ] }, plugins: [ new ESLintWebpackPlugin({ // 指定檢查文件的根目錄 context: path.resolve(__dirname, "../src"), }), new HtmlWebpackPlugin({ // 以 public/index.html 為模板創(chuàng)建文件 // 新的html文件有兩個特點:1. 內容和源文件一致 2. 自動引入打包生成的js等資源 template: path.resolve(__dirname, "../public/index.html"), }), // 提取css成單獨文件 new MiniCssExtractPlugin({ // 定義輸出文件名和目錄 filename: "static/css/index.css", }), new CssMinimizerPlugin() ], // 開發(fā)服務器 devServer: { host: "localhost", // 啟動服務器域名 port: "3000", // 啟動服務器端口號 open: true, // 是否自動打開瀏覽器 hot: true, // 開啟HMR功能(只能用于開發(fā)環(huán)境,生產環(huán)境不需要了) }, mode: 'development' } Include/Exclude 介紹 開發(fā)時我們需要使用第三方的庫或插件,所有文件都下載到 node_modules 中了。而這些文件是不需要編譯可以直接使用的。 所以我們在對 js 文件處理時,要排除 node_modules 下面的文件。(只針對js文件進行處理,只有babel和eslint在進行工作) 注意:以下兩個配置只能寫一種 include 包含,只處理 xxx 文件 exclude 排除,除了 xxx 文件以外其他文件都處理 配置 // path 為 Node.js的核心模塊,專門用來處理文件路徑 const path = require('path') const ESLintWebpackPlugin = require("eslint-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); // 獲取處理樣式的Loaders const getStyleLoaders = (preProcessor) => { return [ MiniCssExtractPlugin.loader, "css-loader", { loader: "postcss-loader", options: { postcssOptions: { plugins: [ "postcss-preset-env", // 能解決大多數(shù)樣式兼容性問題 ], }, }, }, preProcessor, ].filter(Boolean); }; module.exports = { // 入口 entry: { // 需要一個相對路徑 index: './src/index.js' }, // 輸出 output: { // 需要一個絕對路徑 path: path.resolve(__dirname, '../dist'), clean: true, filename: "static/js/index.js", // 將 js 文件輸出到 static/js 目錄中 }, // Performance 這些選項可以控制 webpack 如何通知「資源(asset)和入口起點超過指定文件限制」。 performance: { hints: false }, devtool: "source-map", // 解析器 module: { rules: [ { oneOf: [ { test: /\.css$/, // loader的執(zhí)行順序是從右往左的,所以這里先寫style-loader,再寫css-loader // 如果只使用一個loader的話,可以使用loader屬性代替use,如下 // loader:"style-loader" use: getStyleLoaders(), }, { // 正則匹配所有已.less文件結尾的文件 test: /\.less$/, // loader的執(zhí)行順序是從右往左的,所以這里先寫style-loader,再寫css-loader // 如果只使用一個loader的話,可以使用loader屬性代替use,如下 // loader:"style-loader" use: getStyleLoaders("less-loader"), }, { test: /\.s[ac]ss$/, use: getStyleLoaders("sass-loader"), }, { test: /\.styl$/, use: getStyleLoaders("stylus-loader"), }, { test: /\.(png|jpe?g|gif|webp)$/, type: "asset", // 類似于 module.generator,你可以用 module.parser 在一個地方配置所有解析器的選項。 parser: { // 如果一個模塊源碼大小小于 maxSize,那么模塊會被作為一個 Base64 編碼的字符串注入到包中, 否則模塊文件會被生成到輸出的目標目錄中。 dataUrlCondition: { maxSize: 10 * 1024 // 小于10kb的圖片會被base64處理 } }, // 可以使用 module.generator 在一個地方配置所有生成器的選項 generator: { // 將圖片文件輸出到 static/imgs 目錄中 // 將圖片文件命名 [hash:8][ext][query] // [hash:8]: hash值取8位 // [ext]: 使用之前的文件擴展名 // [query]: 添加之前的query參數(shù) filename: "static/imgs/[hash:8][ext][query]", }, }, { test: /\.(ttf|woff2?|svg)$/, // 發(fā)送一個單獨的文件并導出 URL type: "asset/resource", generator: { filename: "static/media/[hash:8][ext][query]", }, }, { test: /\.js$/, // exclude: /node_modules/, // 排除node_modules代碼不編譯 include: path.resolve(__dirname, "../src"), // 也可以用包含 loader: "babel-loader", }, ] } ] }, plugins: [ new ESLintWebpackPlugin({ // 指定檢查文件的根目錄 context: path.resolve(__dirname, "../src"), exclude: "node_modules", // 默認值 }), new HtmlWebpackPlugin({ // 以 public/index.html 為模板創(chuàng)建文件 // 新的html文件有兩個特點:1. 內容和源文件一致 2. 自動引入打包生成的js等資源 template: path.resolve(__dirname, "../public/index.html"), }), // 提取css成單獨文件 new MiniCssExtractPlugin({ // 定義輸出文件名和目錄 filename: "static/css/index.css", }), new CssMinimizerPlugin() ], // 開發(fā)服務器 // devServer: { // host: "localhost", // 啟動服務器域名 // port: "3000", // 啟動服務器端口號 // open: true, // 是否自動打開瀏覽器 // }, mode: 'production' } cache緩存 介紹 每次打包時 js 文件都要經過 Eslint 檢查 和 Babel 編譯,速度比較慢。 我們可以緩存之前的 Eslint 檢查 和 Babel 編譯結果,這樣第二次打包時速度就會更快了。 對 Eslint 檢查 和 Babel 編譯結果進行緩存。 配置 我們的生產模式和開發(fā)模式都需要配置,這里筆者展示開發(fā)模式的配置 // path 為 Node.js的核心模塊,專門用來處理文件路徑 const path = require('path') const ESLintWebpackPlugin = require("eslint-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); // 獲取處理樣式的Loaders const getStyleLoaders = (preProcessor) => { return [ MiniCssExtractPlugin.loader, "css-loader", { loader: "postcss-loader", options: { postcssOptions: { plugins: [ "postcss-preset-env", // 能解決大多數(shù)樣式兼容性問題 ], }, }, }, preProcessor, ].filter(Boolean); }; module.exports = { // 入口 entry: { // 需要一個相對路徑 index: './src/index.js' }, // 輸出 output: { path: undefined, // 開發(fā)模式沒有輸出,不需要指定輸出目錄 filename: "static/js/main.js", // 將 js 文件輸出到 static/js 目錄中 // clean: true, // 開發(fā)模式沒有輸出,不需要清空輸出結果 }, // Performance 這些選項可以控制 webpack 如何通知「資源(asset)和入口起點超過指定文件限制」。 performance: { hints: false }, devtool: "cheap-module-source-map", // 解析器 module: { rules: [ { // 每個文件只能被其中的一個loader處理 oneOf: [ { test: /\.css$/, // loader的執(zhí)行順序是從右往左的,所以這里先寫style-loader,再寫css-loader // 如果只使用一個loader的話,可以使用loader屬性代替use,如下 // loader:"style-loader" use: getStyleLoaders(), }, { // 正則匹配所有已.less文件結尾的文件 test: /\.less$/, // loader的執(zhí)行順序是從右往左的,所以這里先寫style-loader,再寫css-loader // 如果只使用一個loader的話,可以使用loader屬性代替use,如下 // loader:"style-loader" use: getStyleLoaders("less-loader"), }, { test: /\.s[ac]ss$/, use: getStyleLoaders("sass-loader"), }, { test: /\.styl$/, use: getStyleLoaders("stylus-loader"), }, { test: /\.(png|jpe?g|gif|webp)$/, type: "asset", // 類似于 module.generator,你可以用 module.parser 在一個地方配置所有解析器的選項。 parser: { // 如果一個模塊源碼大小小于 maxSize,那么模塊會被作為一個 Base64 編碼的字符串注入到包中, 否則模塊文件會被生成到輸出的目標目錄中。 dataUrlCondition: { maxSize: 10 * 1024 // 小于10kb的圖片會被base64處理 } }, // 可以使用 module.generator 在一個地方配置所有生成器的選項 generator: { // 將圖片文件輸出到 static/imgs 目錄中 // 將圖片文件命名 [hash:8][ext][query] // [hash:8]: hash值取8位 // [ext]: 使用之前的文件擴展名 // [query]: 添加之前的query參數(shù) filename: "static/imgs/[hash:8][ext][query]", }, }, { test: /\.(ttf|woff2?|svg)$/, // 發(fā)送一個單獨的文件并導出 URL type: "asset/resource", generator: { filename: "static/media/[hash:8][ext][query]", }, }, { test: /\.js$/, // exclude: /node_modules/, // 排除node_modules代碼不編譯 include: path.resolve(__dirname, "../src"), // 也可以用包含 loader: "babel-loader", options: { cacheDirectory: true, // 開啟babel編譯緩存 cacheCompression: false, // 緩存文件不要壓縮 }, }, ] } ] }, plugins: [ new ESLintWebpackPlugin({ // 指定檢查文件的根目錄 context: path.resolve(__dirname, "../src"), exclude: "node_modules", // 默認值 cache: true, // 開啟緩存 // 緩存目錄 cacheLocation: path.resolve( __dirname, "../node_modules/.cache/.eslintcache" ), }), new HtmlWebpackPlugin({ // 以 public/index.html 為模板創(chuàng)建文件 // 新的html文件有兩個特點:1. 內容和源文件一致 2. 自動引入打包生成的js等資源 template: path.resolve(__dirname, "../public/index.html"), }), // 提取css成單獨文件 new MiniCssExtractPlugin({ // 定義輸出文件名和目錄 filename: "static/css/index.css", }), new CssMinimizerPlugin() ], // 開發(fā)服務器 devServer: { host: "localhost", // 啟動服務器域名 port: "3000", // 啟動服務器端口號 open: true, // 是否自動打開瀏覽器 hot: true, // 開啟HMR功能(只能用于開發(fā)環(huán)境,生產環(huán)境不需要了) }, mode: 'development' } Thead多進程打包 介紹 當項目越來越龐大時,打包速度越來越慢,甚至于需要一個下午才能打包出來代碼。這個速度是比較慢的。 我們想要繼續(xù)提升打包速度,其實就是要提升 js 的打包速度,因為其他文件都比較少。 而對 js 文件處理主要就是 eslint 、babel、Terser 三個工具,所以我們要提升它們的運行速度。 我們可以開啟多進程同時處理 js 文件,這樣速度就比之前的單進程打包更快了。 多進程打包:開啟電腦的多個進程同時干一件事,速度更快。 需要注意:請僅在特別耗時的操作中使用,因為每個進程啟動就有大約為 600ms 左右開銷。 使用 下載 npm i thread-loader -D 獲取cpu核數(shù) // nodejs核心模塊,直接使用 const os = require("os"); // cpu核數(shù) const threads = os.cpus().length; 配置 我們的生產模式和開發(fā)模式都需要配置,這里筆者展示開發(fā)模式的配置 // path 為 Node.js的核心模塊,專門用來處理文件路徑 const path = require('path') const os = require("os"); const ESLintWebpackPlugin = require("eslint-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); const TerserPlugin = require("terser-webpack-plugin"); // cpu核數(shù) const threads = os.cpus().length; // 獲取處理樣式的Loaders const getStyleLoaders = (preProcessor) => { return [ MiniCssExtractPlugin.loader, "css-loader", { loader: "postcss-loader", options: { postcssOptions: { plugins: [ "postcss-preset-env", // 能解決大多數(shù)樣式兼容性問題 ], }, }, }, preProcessor, ].filter(Boolean); }; module.exports = { // 入口 entry: { // 需要一個相對路徑 index: './src/index.js' }, // 輸出 output: { path: undefined, // 開發(fā)模式沒有輸出,不需要指定輸出目錄 filename: "static/js/main.js", // 將 js 文件輸出到 static/js 目錄中 // clean: true, // 開發(fā)模式沒有輸出,不需要清空輸出結果 }, // Performance 這些選項可以控制 webpack 如何通知「資源(asset)和入口起點超過指定文件限制」。 performance: { hints: false }, devtool: "cheap-module-source-map", // 解析器 module: { rules: [ { // 每個文件只能被其中的一個loader處理 oneOf: [ { test: /\.css$/, // loader的執(zhí)行順序是從右往左的,所以這里先寫style-loader,再寫css-loader // 如果只使用一個loader的話,可以使用loader屬性代替use,如下 // loader:"style-loader" use: getStyleLoaders(), }, { // 正則匹配所有已.less文件結尾的文件 test: /\.less$/, // loader的執(zhí)行順序是從右往左的,所以這里先寫style-loader,再寫css-loader // 如果只使用一個loader的話,可以使用loader屬性代替use,如下 // loader:"style-loader" use: getStyleLoaders("less-loader"), }, { test: /\.s[ac]ss$/, use: getStyleLoaders("sass-loader"), }, { test: /\.styl$/, use: getStyleLoaders("stylus-loader"), }, { test: /\.(png|jpe?g|gif|webp)$/, type: "asset", // 類似于 module.generator,你可以用 module.parser 在一個地方配置所有解析器的選項。 parser: { // 如果一個模塊源碼大小小于 maxSize,那么模塊會被作為一個 Base64 編碼的字符串注入到包中, 否則模塊文件會被生成到輸出的目標目錄中。 dataUrlCondition: { maxSize: 10 * 1024 // 小于10kb的圖片會被base64處理 } }, // 可以使用 module.generator 在一個地方配置所有生成器的選項 generator: { // 將圖片文件輸出到 static/imgs 目錄中 // 將圖片文件命名 [hash:8][ext][query] // [hash:8]: hash值取8位 // [ext]: 使用之前的文件擴展名 // [query]: 添加之前的query參數(shù) filename: "static/imgs/[hash:8][ext][query]", }, }, { test: /\.(ttf|woff2?|svg)$/, // 發(fā)送一個單獨的文件并導出 URL type: "asset/resource", generator: { filename: "static/media/[hash:8][ext][query]", }, }, { test: /\.js$/, // exclude: /node_modules/, // 排除node_modules代碼不編譯 include: path.resolve(__dirname, "../src"), // 也可以用包含 use: [ { loader: "thread-loader", // 開啟多進程 options: { workers: threads, // 數(shù)量 }, }, { loader: "babel-loader", options: { cacheDirectory: true, // 開啟babel編譯緩存 }, }, ], options: { cacheDirectory: true, // 開啟babel編譯緩存 cacheCompression: false, // 緩存文件不要壓縮 }, }, ] } ] }, plugins: [ new ESLintWebpackPlugin({ // 指定檢查文件的根目錄 context: path.resolve(__dirname, "../src"), exclude: "node_modules", // 默認值 cache: true, // 開啟緩存 // 緩存目錄 cacheLocation: path.resolve( __dirname, "../node_modules/.cache/.eslintcache" ), threads, // 開啟多進程 }), new HtmlWebpackPlugin({ // 以 public/index.html 為模板創(chuàng)建文件 // 新的html文件有兩個特點:1. 內容和源文件一致 2. 自動引入打包生成的js等資源 template: path.resolve(__dirname, "../public/index.html"), }), // 提取css成單獨文件 new MiniCssExtractPlugin({ // 定義輸出文件名和目錄 filename: "static/css/index.css", }), // new CssMinimizerPlugin() ], // 推薦將壓縮操作放在這里 optimization: { minimize: true, minimizer: [ // css壓縮也可以寫到optimization.minimizer里面,效果一樣的 new CssMinimizerPlugin(), // 當生產模式會默認開啟TerserPlugin,但是我們需要進行其他配置,就要重新寫了 new TerserPlugin({ parallel: threads // 開啟多進程 }) ], }, // 開發(fā)服務器 devServer: { host: "localhost", // 啟動服務器域名 port: "3000", // 啟動服務器端口號 open: true, // 是否自動打開瀏覽器 hot: true, // 開啟HMR功能(只能用于開發(fā)環(huán)境,生產環(huán)境不需要了) }, mode: 'development' } 減少代碼體積 Tree Shaking 介紹 開發(fā)時我們定義了一些工具函數(shù)庫,或者引用第三方工具函數(shù)庫或組件庫。 如果沒有特殊處理的話我們打包時會引入整個庫,但是實際上可能我們可能只用上極小部分的功能。 這樣將整個庫都打包進來,體積就太大了。 Tree Shaking 是一個術語,通常用于描述移除 JavaScript 中的沒有使用上的代碼。 注意:它依賴 ES Module 使用 Webpack 已經默認開啟了這個功能,無需其他配置。 減少babel文件生成的體積 介紹 Babel 為編譯的每個文件都插入了輔助代碼,使代碼體積過大! Babel 對一些公共方法使用了非常小的輔助代碼,比如 _extend。默認情況下會被添加到每一個需要它的文件中。 你可以將這些輔助代碼作為一個獨立模塊,來避免重復引入。 @babel/plugin-transform-runtime: 禁用了 Babel 自動對每個文件的 runtime 注入,而是引入 @babel/plugin-transform-runtime 并且使所有輔助代碼從這里引用。 使用 下載 npm i @babel/plugin-transform-runtime -D 配置 我們的生產模式和開發(fā)模式都需要配置,這里筆者展示開發(fā)模式的配置 // path 為 Node.js的核心模塊,專門用來處理文件路徑 const path = require('path') const os = require("os"); const ESLintWebpackPlugin = require("eslint-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); const TerserPlugin = require("terser-webpack-plugin"); // cpu核數(shù) const threads = os.cpus().length; // 獲取處理樣式的Loaders const getStyleLoaders = (preProcessor) => { return [ MiniCssExtractPlugin.loader, "css-loader", { loader: "postcss-loader", options: { postcssOptions: { plugins: [ "postcss-preset-env", // 能解決大多數(shù)樣式兼容性問題 ], }, }, }, preProcessor, ].filter(Boolean); }; module.exports = { // 入口 entry: { // 需要一個相對路徑 index: './src/index.js' }, // 輸出 output: { path: undefined, // 開發(fā)模式沒有輸出,不需要指定輸出目錄 filename: "static/js/main.js", // 將 js 文件輸出到 static/js 目錄中 // clean: true, // 開發(fā)模式沒有輸出,不需要清空輸出結果 }, // Performance 這些選項可以控制 webpack 如何通知「資源(asset)和入口起點超過指定文件限制」。 performance: { hints: false }, devtool: "cheap-module-source-map", // 解析器 module: { rules: [ { // 每個文件只能被其中的一個loader處理 oneOf: [ { test: /\.css$/, // loader的執(zhí)行順序是從右往左的,所以這里先寫style-loader,再寫css-loader // 如果只使用一個loader的話,可以使用loader屬性代替use,如下 // loader:"style-loader" use: getStyleLoaders(), }, { // 正則匹配所有已.less文件結尾的文件 test: /\.less$/, // loader的執(zhí)行順序是從右往左的,所以這里先寫style-loader,再寫css-loader // 如果只使用一個loader的話,可以使用loader屬性代替use,如下 // loader:"style-loader" use: getStyleLoaders("less-loader"), }, { test: /\.s[ac]ss$/, use: getStyleLoaders("sass-loader"), }, { test: /\.styl$/, use: getStyleLoaders("stylus-loader"), }, { test: /\.(png|jpe?g|gif|webp)$/, type: "asset", // 類似于 module.generator,你可以用 module.parser 在一個地方配置所有解析器的選項。 parser: { // 如果一個模塊源碼大小小于 maxSize,那么模塊會被作為一個 Base64 編碼的字符串注入到包中, 否則模塊文件會被生成到輸出的目標目錄中。 dataUrlCondition: { maxSize: 10 * 1024 // 小于10kb的圖片會被base64處理 } }, // 可以使用 module.generator 在一個地方配置所有生成器的選項 generator: { // 將圖片文件輸出到 static/imgs 目錄中 // 將圖片文件命名 [hash:8][ext][query] // [hash:8]: hash值取8位 // [ext]: 使用之前的文件擴展名 // [query]: 添加之前的query參數(shù) filename: "static/imgs/[hash:8][ext][query]", }, }, { test: /\.(ttf|woff2?|svg)$/, // 發(fā)送一個單獨的文件并導出 URL type: "asset/resource", generator: { filename: "static/media/[hash:8][ext][query]", }, }, { test: /\.js$/, // exclude: /node_modules/, // 排除node_modules代碼不編譯 include: path.resolve(__dirname, "../src"), // 也可以用包含 use: [ { loader: "thread-loader", // 開啟多進程 options: { workers: threads, // 數(shù)量 }, }, { loader: "babel-loader", options: { cacheDirectory: true, // 開啟babel編譯緩存 cacheCompression: false, // 緩存文件不要壓縮 plugins: ["@babel/plugin-transform-runtime"], // 減少代碼體積 }, }, ], }, ] } ] }, plugins: [ new ESLintWebpackPlugin({ // 指定檢查文件的根目錄 context: path.resolve(__dirname, "../src"), exclude: "node_modules", // 默認值 cache: true, // 開啟緩存 // 緩存目錄 cacheLocation: path.resolve( __dirname, "../node_modules/.cache/.eslintcache" ), threads, // 開啟多進程 }), new HtmlWebpackPlugin({ // 以 public/index.html 為模板創(chuàng)建文件 // 新的html文件有兩個特點:1. 內容和源文件一致 2. 自動引入打包生成的js等資源 template: path.resolve(__dirname, "../public/index.html"), }), // 提取css成單獨文件 new MiniCssExtractPlugin({ // 定義輸出文件名和目錄 filename: "static/css/index.css", }), // new CssMinimizerPlugin() ], // 推薦將壓縮操作放在這里 optimization: { minimize: true, minimizer: [ // css壓縮也可以寫到optimization.minimizer里面,效果一樣的 new CssMinimizerPlugin(), // 當生產模式會默認開啟TerserPlugin,但是我們需要進行其他配置,就要重新寫了 new TerserPlugin({ parallel: threads // 開啟多進程 }) ], }, // 開發(fā)服務器 devServer: { host: "localhost", // 啟動服務器域名 port: "3000", // 啟動服務器端口號 open: true, // 是否自動打開瀏覽器 hot: true, // 開啟HMR功能(只能用于開發(fā)環(huán)境,生產環(huán)境不需要了) }, mode: 'development' } Image Minimizer 圖片壓縮 介紹 開發(fā)如果項目中引用了較多圖片,那么圖片體積會比較大,將來請求速度比較慢。 我們可以對圖片進行壓縮,減少圖片體積。 注意:如果項目中圖片都是在線鏈接,那么就不需要了。本地項目靜態(tài)圖片才需要進行壓縮。 image-minimizer-webpack-plugin: 用來壓縮圖片的插件 使用 下載 npm i image-minimizer-webpack-plugin imagemin -D 還有剩下包需要下載,有兩種模式:(如果npm下載不下來,使用cnpm下載) 無損壓縮 npm install imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo -D 有損壓縮 npm install imagemin-gifsicle imagemin-mozjpeg imagemin-pngquant imagemin-svgo -D 配置 以無損壓縮為例子,(生產模式與開發(fā)模式都需要配置) // path 為 Node.js的核心模塊,專門用來處理文件路徑 const path = require('path') const os = require("os"); const ESLintWebpackPlugin = require("eslint-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); const TerserPlugin = require("terser-webpack-plugin"); const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin"); // cpu核數(shù) const threads = os.cpus().length; // 獲取處理樣式的Loaders const getStyleLoaders = (preProcessor) => { return [ MiniCssExtractPlugin.loader, "css-loader", { loader: "postcss-loader", options: { postcssOptions: { plugins: [ "postcss-preset-env", // 能解決大多數(shù)樣式兼容性問題 ], }, }, }, preProcessor, ].filter(Boolean); }; module.exports = { // 入口 entry: { // 需要一個相對路徑 index: './src/index.js' }, // 輸出 output: { path: undefined, // 開發(fā)模式沒有輸出,不需要指定輸出目錄 filename: "static/js/main.js", // 將 js 文件輸出到 static/js 目錄中 // clean: true, // 開發(fā)模式沒有輸出,不需要清空輸出結果 }, // Performance 這些選項可以控制 webpack 如何通知「資源(asset)和入口起點超過指定文件限制」。 performance: { hints: false }, devtool: "cheap-module-source-map", // 解析器 module: { rules: [ { // 每個文件只能被其中的一個loader處理 oneOf: [ { test: /\.css$/, // loader的執(zhí)行順序是從右往左的,所以這里先寫style-loader,再寫css-loader // 如果只使用一個loader的話,可以使用loader屬性代替use,如下 // loader:"style-loader" use: getStyleLoaders(), }, { // 正則匹配所有已.less文件結尾的文件 test: /\.less$/, // loader的執(zhí)行順序是從右往左的,所以這里先寫style-loader,再寫css-loader // 如果只使用一個loader的話,可以使用loader屬性代替use,如下 // loader:"style-loader" use: getStyleLoaders("less-loader"), }, { test: /\.s[ac]ss$/, use: getStyleLoaders("sass-loader"), }, { test: /\.styl$/, use: getStyleLoaders("stylus-loader"), }, { test: /\.(png|jpe?g|gif|webp)$/, type: "asset", // 類似于 module.generator,你可以用 module.parser 在一個地方配置所有解析器的選項。 parser: { // 如果一個模塊源碼大小小于 maxSize,那么模塊會被作為一個 Base64 編碼的字符串注入到包中, 否則模塊文件會被生成到輸出的目標目錄中。 dataUrlCondition: { maxSize: 10 * 1024 // 小于10kb的圖片會被base64處理 } }, // 可以使用 module.generator 在一個地方配置所有生成器的選項 generator: { // 將圖片文件輸出到 static/imgs 目錄中 // 將圖片文件命名 [hash:8][ext][query] // [hash:8]: hash值取8位 // [ext]: 使用之前的文件擴展名 // [query]: 添加之前的query參數(shù) filename: "static/imgs/[hash:8][ext][query]", }, }, { test: /\.(ttf|woff2?|svg)$/, // 發(fā)送一個單獨的文件并導出 URL type: "asset/resource", generator: { filename: "static/media/[hash:8][ext][query]", }, }, { test: /\.js$/, // exclude: /node_modules/, // 排除node_modules代碼不編譯 include: path.resolve(__dirname, "../src"), // 也可以用包含 use: [ { loader: "thread-loader", // 開啟多進程 options: { workers: threads, // 數(shù)量 }, }, { loader: "babel-loader", options: { cacheDirectory: true, // 開啟babel編譯緩存 cacheCompression: false, // 緩存文件不要壓縮 plugins: ["@babel/plugin-transform-runtime"], // 減少代碼體積 }, }, ], }, ] } ] }, plugins: [ new ESLintWebpackPlugin({ // 指定檢查文件的根目錄 context: path.resolve(__dirname, "../src"), exclude: "node_modules", // 默認值 cache: true, // 開啟緩存 // 緩存目錄 cacheLocation: path.resolve( __dirname, "../node_modules/.cache/.eslintcache" ), threads, // 開啟多進程 }), new HtmlWebpackPlugin({ // 以 public/index.html 為模板創(chuàng)建文件 // 新的html文件有兩個特點:1. 內容和源文件一致 2. 自動引入打包生成的js等資源 template: path.resolve(__dirname, "../public/index.html"), }), // 提取css成單獨文件 new MiniCssExtractPlugin({ // 定義輸出文件名和目錄 filename: "static/css/index.css", }), // new CssMinimizerPlugin() ], // 推薦將壓縮操作放在這里 optimization: { minimize: true, minimizer: [ // css壓縮也可以寫到optimization.minimizer里面,效果一樣的 new CssMinimizerPlugin(), // 當生產模式會默認開啟TerserPlugin,但是我們需要進行其他配置,就要重新寫了 new TerserPlugin({ parallel: threads // 開啟多進程 }), // 壓縮圖片 new ImageMinimizerPlugin({ minimizer: { implementation: ImageMinimizerPlugin.imageminGenerate, options: { plugins: [ ["gifsicle", { interlaced: true }], ["jpegtran", { progressive: true }], ["optipng", { optimizationLevel: 5 }], [ "svgo", { plugins: [ "preset-default", "prefixIds", { name: "sortAttrs", params: { xmlnsOrder: "alphabetical", }, }, ], }, ], ], }, }, }), ], }, // 開發(fā)服務器 devServer: { host: "localhost", // 啟動服務器域名 port: "3000", // 啟動服務器端口號 open: true, // 是否自動打開瀏覽器 hot: true, // 開啟HMR功能(只能用于開發(fā)環(huán)境,生產環(huán)境不需要了) }, mode: 'development' } 優(yōu)化代碼運行性能 Code Split 介紹 打包代碼時會將所有 js 文件打包到一個文件中,體積太大了。我們如果只要渲染首頁,就應該只加載首頁的 js 文件,其他文件不應該加載。 所以我們需要將打包生成的文件進行代碼分割,生成多個 js 文件,渲染哪個頁面就只加載某個 js 文件,這樣加載的資源就少,速度就更快。 代碼分割(Code Split)主要做了兩件事: 分割文件:將打包生成的文件進行分割,生成多個 js 文件。 按需加載:需要哪個文件就加載哪個文件。 使用 單頁面配置 如果我們使用了動態(tài)導入的語法,那么在eslint里面需要支持一下 下載 npm i eslint-plugin-import -D 配置 // .eslintrc.js module.exports = { // 繼承 Eslint 規(guī)則 extends: ["eslint:recommended"], env: { node: true, // 啟用node中全局變量 browser: true, // 啟用瀏覽器中全局變量 }, plugins: ["import"], // 解決動態(tài)導入import語法報錯問題 --> 實際使用eslint-plugin-import的規(guī)則解決的 parserOptions: { ecmaVersion: 6, sourceType: "module", }, rules: { "no-var": 2, // 不能使用 var 定義變量 }, }; Code Split統(tǒng)一命名 我們的配置中對于輸出文件的配置有很多,比如入口文件,圖片文件,字體圖標文件、css文件、動態(tài)引入文件等等,我們希望統(tǒng)一寫在一個地方 配置 這里以生產模式為例子 // path 為 Node.js的核心模塊,專門用來處理文件路徑 const path = require('path') const os = require("os"); const ESLintWebpackPlugin = require("eslint-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); const TerserPlugin = require("terser-webpack-plugin"); const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin"); // cpu核數(shù) const threads = os.cpus().length; // 獲取處理樣式的Loaders const getStyleLoaders = (preProcessor) => { return [ MiniCssExtractPlugin.loader, "css-loader", { loader: "postcss-loader", options: { postcssOptions: { plugins: [ "postcss-preset-env", // 能解決大多數(shù)樣式兼容性問題 ], }, }, }, preProcessor, ].filter(Boolean); }; module.exports = { // 入口 entry: { // 需要一個相對路徑 index: './src/index.js' }, // 輸出 output: { // 需要一個絕對路徑 path: path.resolve(__dirname, '../dist'), clean: true, // chunk的name是啥呢? 比如: entry中xxx: "./src/xxx.js", name就是xxx。注意是前面的xxx,和文件名無關。 // 為什么需要這樣命名呢?如果還是之前寫法main.js,那么打包生成兩個js文件都會叫做main.js會發(fā)生覆蓋。(實際上會直接報錯的) filename: "static/js/[name].js", // 入口文件打包輸出資源命名方式 chunkFilename: "static/js/[name].chunk.js", // 動態(tài)導入輸出資源命名方式 assetModuleFilename: "static/media/[name].[hash][ext]", // 圖片、字體等資源命名方式(注意用hash) }, // Performance 這些選項可以控制 webpack 如何通知「資源(asset)和入口起點超過指定文件限制」。 performance: { hints: false }, devtool: "source-map", // 解析器 module: { rules: [ { oneOf: [ { test: /\.css$/, // loader的執(zhí)行順序是從右往左的,所以這里先寫style-loader,再寫css-loader // 如果只使用一個loader的話,可以使用loader屬性代替use,如下 // loader:"style-loader" use: getStyleLoaders(), }, { // 正則匹配所有已.less文件結尾的文件 test: /\.less$/, // loader的執(zhí)行順序是從右往左的,所以這里先寫style-loader,再寫css-loader // 如果只使用一個loader的話,可以使用loader屬性代替use,如下 // loader:"style-loader" use: getStyleLoaders("less-loader"), }, { test: /\.s[ac]ss$/, use: getStyleLoaders("sass-loader"), }, { test: /\.styl$/, use: getStyleLoaders("stylus-loader"), }, { test: /\.(png|jpe?g|gif|webp)$/, type: "asset", // 類似于 module.generator,你可以用 module.parser 在一個地方配置所有解析器的選項。 parser: { // 如果一個模塊源碼大小小于 maxSize,那么模塊會被作為一個 Base64 編碼的字符串注入到包中, 否則模塊文件會被生成到輸出的目標目錄中。 dataUrlCondition: { maxSize: 10 * 1024 // 小于10kb的圖片會被base64處理 } }, // 可以使用 module.generator 在一個地方配置所有生成器的選項 // generator: { // // 將圖片文件輸出到 static/imgs 目錄中 // // 將圖片文件命名 [hash:8][ext][query] // // [hash:8]: hash值取8位 // // [ext]: 使用之前的文件擴展名 // // [query]: 添加之前的query參數(shù) // filename: "static/imgs/[hash:8][ext][query]", // }, }, { test: /\.(ttf|woff2?|svg)$/, // 發(fā)送一個單獨的文件并導出 URL type: "asset/resource", // generator: { // filename: "static/media/[hash:8][ext][query]", // }, }, { test: /\.js$/, // exclude: /node_modules/, // 排除node_modules代碼不編譯 include: path.resolve(__dirname, "../src"), // 也可以用包含 use: [ { loader: "thread-loader", // 開啟多進程 options: { workers: threads, // 數(shù)量 }, }, { loader: "babel-loader", options: { cacheDirectory: true, // 開啟babel編譯緩存 cacheCompression: false, // 緩存文件不要壓縮 plugins: ["@babel/plugin-transform-runtime"], // 減少代碼體積 }, }, ], }, ] } ] }, plugins: [ new ESLintWebpackPlugin({ // 指定檢查文件的根目錄 context: path.resolve(__dirname, "../src"), exclude: "node_modules", // 默認值 cache: true, // 開啟緩存 // 緩存目錄 cacheLocation: path.resolve( __dirname, "../node_modules/.cache/.eslintcache" ), threads, // 開啟多進程 }), new HtmlWebpackPlugin({ // 以 public/index.html 為模板創(chuàng)建文件 // 新的html文件有兩個特點:1. 內容和源文件一致 2. 自動引入打包生成的js等資源 template: path.resolve(__dirname, "../public/index.html"), }), // 提取css成單獨文件 new MiniCssExtractPlugin({ // 定義輸出文件名和目錄 filename: "static/css/[name].css", chunkFilename: "static/css/[name].chunk.css", }), // new CssMinimizerPlugin() ], // 推薦將壓縮操作放在這里 optimization: { minimize: true, minimizer: [ // css壓縮也可以寫到optimization.minimizer里面,效果一樣的 new CssMinimizerPlugin(), // 當生產模式會默認開啟TerserPlugin,但是我們需要進行其他配置,就要重新寫了 new TerserPlugin({ parallel: threads // 開啟多進程 }), // 壓縮圖片 new ImageMinimizerPlugin({ minimizer: { implementation: ImageMinimizerPlugin.imageminGenerate, options: { plugins: [ ["gifsicle", { interlaced: true }], ["jpegtran", { progressive: true }], ["optipng", { optimizationLevel: 5 }], [ "svgo", { plugins: [ "preset-default", "prefixIds", { name: "sortAttrs", params: { xmlnsOrder: "alphabetical", }, }, ], }, ], ], }, }, }), ], splitChunks: { chunks: "all" } }, // 開發(fā)服務器 // devServer: { // host: "localhost", // 啟動服務器域名 // port: "3000", // 啟動服務器端口號 // open: true, // 是否自動打開瀏覽器 // }, mode: 'production' } Preload / Prefetch 預處理 介紹 我們前面已經做了代碼分割,同時會使用 import 動態(tài)導入語法來進行代碼按需加載(我們也叫懶加載,比如路由懶加載就是這樣實現(xiàn)的)。 但是加載速度還不夠好,比如:是用戶點擊按鈕時才加載這個資源的,如果資源體積很大,那么用戶會感覺到明顯卡頓效果。 我們想在瀏覽器空閑時間,加載后續(xù)需要使用的資源。我們就需要用上 Preload 或 Prefetch 技術 Preload:告訴瀏覽器立即加載資源。 Prefetch:告訴瀏覽器在空閑時才開始加載資源。(開發(fā)中這個用的最多) 它們共同點: 都只會加載資源,并不執(zhí)行。 都有緩存。 它們區(qū)別: Preload加載優(yōu)先級高,Prefetch加載優(yōu)先級低。 Preload只能加載當前頁面需要使用的資源,Prefetch可以加載當前頁面資源,也可以加載下一個頁面需要使用的資源。 總結: 當前頁面優(yōu)先級高的資源用 Preload 加載。 下一個頁面需要使用的資源用 Prefetch 加載。 它們的問題:兼容性較差。 我們可以去 Can I Use 網站查詢 API 的兼容性問題。 Preload 相對于 Prefetch 兼容性好一點。 使用 下載 npm i @vue/preload-webpack-plugin -D 配置 以生產模式為例子 // path 為 Node.js的核心模塊,專門用來處理文件路徑 const path = require('path') const os = require("os"); const ESLintWebpackPlugin = require("eslint-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); const TerserPlugin = require("terser-webpack-plugin"); const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin"); const PreloadWebpackPlugin = require("@vue/preload-webpack-plugin"); // cpu核數(shù) const threads = os.cpus().length; // 獲取處理樣式的Loaders const getStyleLoaders = (preProcessor) => { return [ MiniCssExtractPlugin.loader, "css-loader", { loader: "postcss-loader", options: { postcssOptions: { plugins: [ "postcss-preset-env", // 能解決大多數(shù)樣式兼容性問題 ], }, }, }, preProcessor, ].filter(Boolean); }; module.exports = { // 入口 entry: { // 需要一個相對路徑 index: './src/index.js' }, // 輸出 output: { // 需要一個絕對路徑 path: path.resolve(__dirname, '../dist'), clean: true, // chunk的name是啥呢? 比如: entry中xxx: "./src/xxx.js", name就是xxx。注意是前面的xxx,和文件名無關。 // 為什么需要這樣命名呢?如果還是之前寫法main.js,那么打包生成兩個js文件都會叫做main.js會發(fā)生覆蓋。(實際上會直接報錯的) filename: "static/js/[name].js", // 入口文件打包輸出資源命名方式 chunkFilename: "static/js/[name].chunk.js", // 動態(tài)導入輸出資源命名方式 assetModuleFilename: "static/media/[name].[hash][ext]", // 圖片、字體等資源命名方式(注意用hash) }, // Performance 這些選項可以控制 webpack 如何通知「資源(asset)和入口起點超過指定文件限制」。 performance: { hints: false }, devtool: "source-map", // 解析器 module: { rules: [ { oneOf: [ { test: /\.css$/, // loader的執(zhí)行順序是從右往左的,所以這里先寫style-loader,再寫css-loader // 如果只使用一個loader的話,可以使用loader屬性代替use,如下 // loader:"style-loader" use: getStyleLoaders(), }, { // 正則匹配所有已.less文件結尾的文件 test: /\.less$/, // loader的執(zhí)行順序是從右往左的,所以這里先寫style-loader,再寫css-loader // 如果只使用一個loader的話,可以使用loader屬性代替use,如下 // loader:"style-loader" use: getStyleLoaders("less-loader"), }, { test: /\.s[ac]ss$/, use: getStyleLoaders("sass-loader"), }, { test: /\.styl$/, use: getStyleLoaders("stylus-loader"), }, { test: /\.(png|jpe?g|gif|webp)$/, type: "asset", // 類似于 module.generator,你可以用 module.parser 在一個地方配置所有解析器的選項。 parser: { // 如果一個模塊源碼大小小于 maxSize,那么模塊會被作為一個 Base64 編碼的字符串注入到包中, 否則模塊文件會被生成到輸出的目標目錄中。 dataUrlCondition: { maxSize: 10 * 1024 // 小于10kb的圖片會被base64處理 } }, // 可以使用 module.generator 在一個地方配置所有生成器的選項 // generator: { // // 將圖片文件輸出到 static/imgs 目錄中 // // 將圖片文件命名 [hash:8][ext][query] // // [hash:8]: hash值取8位 // // [ext]: 使用之前的文件擴展名 // // [query]: 添加之前的query參數(shù) // filename: "static/imgs/[hash:8][ext][query]", // }, }, { test: /\.(ttf|woff2?|svg)$/, // 發(fā)送一個單獨的文件并導出 URL type: "asset/resource", // generator: { // filename: "static/media/[hash:8][ext][query]", // }, }, { test: /\.js$/, // exclude: /node_modules/, // 排除node_modules代碼不編譯 include: path.resolve(__dirname, "../src"), // 也可以用包含 use: [ { loader: "thread-loader", // 開啟多進程 options: { workers: threads, // 數(shù)量 }, }, { loader: "babel-loader", options: { cacheDirectory: true, // 開啟babel編譯緩存 cacheCompression: false, // 緩存文件不要壓縮 plugins: ["@babel/plugin-transform-runtime"], // 減少代碼體積 }, }, ], }, ] } ] }, plugins: [ new ESLintWebpackPlugin({ // 指定檢查文件的根目錄 context: path.resolve(__dirname, "../src"), exclude: "node_modules", // 默認值 cache: true, // 開啟緩存 // 緩存目錄 cacheLocation: path.resolve( __dirname, "../node_modules/.cache/.eslintcache" ), threads, // 開啟多進程 }), new HtmlWebpackPlugin({ // 以 public/index.html 為模板創(chuàng)建文件 // 新的html文件有兩個特點:1. 內容和源文件一致 2. 自動引入打包生成的js等資源 template: path.resolve(__dirname, "../public/index.html"), }), // 提取css成單獨文件 new MiniCssExtractPlugin({ // 定義輸出文件名和目錄 filename: "static/css/[name].css", chunkFilename: "static/css/[name].chunk.css", }), // new CssMinimizerPlugin() new PreloadWebpackPlugin({ rel: "preload", // preload兼容性更好 as: "script", // rel: 'prefetch' // prefetch兼容性更差 }), ], // 推薦將壓縮操作放在這里 optimization: { minimize: true, minimizer: [ // css壓縮也可以寫到optimization.minimizer里面,效果一樣的 new CssMinimizerPlugin(), // 當生產模式會默認開啟TerserPlugin,但是我們需要進行其他配置,就要重新寫了 new TerserPlugin({ parallel: threads // 開啟多進程 }), // 壓縮圖片 new ImageMinimizerPlugin({ minimizer: { implementation: ImageMinimizerPlugin.imageminGenerate, options: { plugins: [ ["gifsicle", { interlaced: true }], ["jpegtran", { progressive: true }], ["optipng", { optimizationLevel: 5 }], [ "svgo", { plugins: [ "preset-default", "prefixIds", { name: "sortAttrs", params: { xmlnsOrder: "alphabetical", }, }, ], }, ], ], }, }, }), ], splitChunks: { chunks: "all" } }, // 開發(fā)服務器 // devServer: { // host: "localhost", // 啟動服務器域名 // port: "3000", // 啟動服務器端口號 // open: true, // 是否自動打開瀏覽器 // }, mode: 'production' } Network Cache 介紹 將來開發(fā)時我們對靜態(tài)資源會使用緩存來優(yōu)化,這樣瀏覽器第二次請求資源就能讀取緩存了,速度很快。 但是這樣的話就會有一個問題, 因為前后輸出的文件名是一樣的,都叫 main.js,一旦將來發(fā)布新版本,因為文件名沒有變化導致瀏覽器會直接讀取緩存,不會加載新資源,項目也就沒法更新了。 所以我們從文件名入手,確保更新前后文件名不一樣,這樣就可以做緩存了。 它們都會生成一個唯一的 hash 值。 fullhash(webpack4 是 hash) 每次修改任何一個文件,所有文件名的 hash 至都將改變。所以一旦修改了任何一個文件,整個項目的文件緩存都將失效。 chunkhash 根據(jù)不同的入口文件(Entry)進行依賴文件解析、構建對應的 chunk,生成對應的哈希值。我們 js 和 css 是同一個引入,會共享一個 hash 值。 contenthash 根據(jù)文件內容生成 hash 值,只有文件內容變化了,hash 值才會變化。所有文件 hash 值是獨享且不同的。 存在問題與解決方案 問題: 當我們修改 math.js 文件再重新打包的時候,因為 contenthash 原因,math.js 文件 hash 值發(fā)生了變化(這是正常的)。 但是 main.js 文件的 hash 值也發(fā)生了變化,這會導致 main.js 的緩存失效。明明我們只修改 math.js, 為什么 main.js 也會變身變化呢? 原因: 更新前:math.xxx.js, main.js 引用的 math.xxx.js 更新后:math.yyy.js, main.js 引用的 math.yyy.js, 文件名發(fā)生了變化,間接導致 main.js 也發(fā)生了變化 解決: 將 hash 值單獨保管在一個 runtime 文件中。 我們最終輸出三個文件:main、math、runtime。當 math 文件發(fā)送變化,變化的是 math 和 runtime 文件,main 不變。 runtime 文件只保存文件的 hash 值和它們與文件關系,整個文件體積就比較小,所以變化重新請求的代價也小。 使用 // 提取runtime文件 runtimeChunk: { name: (entrypoint) => `runtime~${entrypoint.name}`, // runtime文件命名規(guī)則 }, 以生產模式為例子 // path 為 Node.js的核心模塊,專門用來處理文件路徑 const path = require('path') const os = require("os"); const ESLintWebpackPlugin = require("eslint-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); const TerserPlugin = require("terser-webpack-plugin"); const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin"); const PreloadWebpackPlugin = require("@vue/preload-webpack-plugin"); // cpu核數(shù) const threads = os.cpus().length; // 獲取處理樣式的Loaders const getStyleLoaders = (preProcessor) => { return [ MiniCssExtractPlugin.loader, "css-loader", { loader: "postcss-loader", options: { postcssOptions: { plugins: [ "postcss-preset-env", // 能解決大多數(shù)樣式兼容性問題 ], }, }, }, preProcessor, ].filter(Boolean); }; module.exports = { // 入口 entry: { // 需要一個相對路徑 index: './src/index.js' }, // 輸出 output: { // 需要一個絕對路徑 path: path.resolve(__dirname, '../dist'), clean: true, // chunk的name是啥呢? 比如: entry中xxx: "./src/xxx.js", name就是xxx。注意是前面的xxx,和文件名無關。 // 為什么需要這樣命名呢?如果還是之前寫法main.js,那么打包生成兩個js文件都會叫做main.js會發(fā)生覆蓋。(實際上會直接報錯的) // [contenthash:8]使用contenthash,取8位長度 filename: "static/js/[name].[contenthash:8].js", // 入口文件打包輸出資源命名方式 chunkFilename: "static/js/[name].[contenthash:8].chunk.js", // 動態(tài)導入輸出資源命名方式 assetModuleFilename: "static/media/[name].[hash][ext]", // 圖片、字體等資源命名方式(注意用hash) }, // Performance 這些選項可以控制 webpack 如何通知「資源(asset)和入口起點超過指定文件限制」。 performance: { hints: false }, devtool: "source-map", // 解析器 module: { rules: [ { oneOf: [ { test: /\.css$/, // loader的執(zhí)行順序是從右往左的,所以這里先寫style-loader,再寫css-loader // 如果只使用一個loader的話,可以使用loader屬性代替use,如下 // loader:"style-loader" use: getStyleLoaders(), }, { // 正則匹配所有已.less文件結尾的文件 test: /\.less$/, // loader的執(zhí)行順序是從右往左的,所以這里先寫style-loader,再寫css-loader // 如果只使用一個loader的話,可以使用loader屬性代替use,如下 // loader:"style-loader" use: getStyleLoaders("less-loader"), }, { test: /\.s[ac]ss$/, use: getStyleLoaders("sass-loader"), }, { test: /\.styl$/, use: getStyleLoaders("stylus-loader"), }, { test: /\.(png|jpe?g|gif|webp)$/, type: "asset", // 類似于 module.generator,你可以用 module.parser 在一個地方配置所有解析器的選項。 parser: { // 如果一個模塊源碼大小小于 maxSize,那么模塊會被作為一個 Base64 編碼的字符串注入到包中, 否則模塊文件會被生成到輸出的目標目錄中。 dataUrlCondition: { maxSize: 10 * 1024 // 小于10kb的圖片會被base64處理 } }, // 可以使用 module.generator 在一個地方配置所有生成器的選項 // generator: { // // 將圖片文件輸出到 static/imgs 目錄中 // // 將圖片文件命名 [hash:8][ext][query] // // [hash:8]: hash值取8位 // // [ext]: 使用之前的文件擴展名 // // [query]: 添加之前的query參數(shù) // filename: "static/imgs/[hash:8][ext][query]", // }, }, { test: /\.(ttf|woff2?|svg)$/, // 發(fā)送一個單獨的文件并導出 URL type: "asset/resource", // generator: { // filename: "static/media/[hash:8][ext][query]", // }, }, { test: /\.js$/, // exclude: /node_modules/, // 排除node_modules代碼不編譯 include: path.resolve(__dirname, "../src"), // 也可以用包含 use: [ { loader: "thread-loader", // 開啟多進程 options: { workers: threads, // 數(shù)量 }, }, { loader: "babel-loader", options: { cacheDirectory: true, // 開啟babel編譯緩存 cacheCompression: false, // 緩存文件不要壓縮 plugins: ["@babel/plugin-transform-runtime"], // 減少代碼體積 }, }, ], }, ] } ] }, plugins: [ new ESLintWebpackPlugin({ // 指定檢查文件的根目錄 context: path.resolve(__dirname, "../src"), exclude: "node_modules", // 默認值 cache: true, // 開啟緩存 // 緩存目錄 cacheLocation: path.resolve( __dirname, "../node_modules/.cache/.eslintcache" ), threads, // 開啟多進程 }), new HtmlWebpackPlugin({ // 以 public/index.html 為模板創(chuàng)建文件 // 新的html文件有兩個特點:1. 內容和源文件一致 2. 自動引入打包生成的js等資源 template: path.resolve(__dirname, "../public/index.html"), }), // 提取css成單獨文件 new MiniCssExtractPlugin({ // 定義輸出文件名和目錄 filename: "static/css/[name].[contenthash:8].css", chunkFilename: "static/css/[name].[contenthash:8].chunk.css", }), // new CssMinimizerPlugin() new PreloadWebpackPlugin({ rel: "preload", // preload兼容性更好 as: "script", // rel: 'prefetch' // prefetch兼容性更差 }), ], // 推薦將壓縮操作放在這里 optimization: { minimize: true, minimizer: [ // css壓縮也可以寫到optimization.minimizer里面,效果一樣的 new CssMinimizerPlugin(), // 當生產模式會默認開啟TerserPlugin,但是我們需要進行其他配置,就要重新寫了 new TerserPlugin({ parallel: threads // 開啟多進程 }), // 壓縮圖片 new ImageMinimizerPlugin({ minimizer: { implementation: ImageMinimizerPlugin.imageminGenerate, options: { plugins: [ ["gifsicle", { interlaced: true }], ["jpegtran", { progressive: true }], ["optipng", { optimizationLevel: 5 }], [ "svgo", { plugins: [ "preset-default", "prefixIds", { name: "sortAttrs", params: { xmlnsOrder: "alphabetical", }, }, ], }, ], ], }, }, }), ], splitChunks: { chunks: "all" }, // 提取runtime文件 runtimeChunk: { name: (entrypoint) => `runtime~${entrypoint.name}`, // runtime文件命名規(guī)則 }, }, // 開發(fā)服務器 // devServer: { // host: "localhost", // 啟動服務器域名 // port: "3000", // 啟動服務器端口號 // open: true, // 是否自動打開瀏覽器 // }, mode: 'production' } Core-js徹底解決js的兼容問題 介紹 過去我們使用 babel 對 js 代碼進行了兼容性處理,其中使用@babel/preset-env 智能預設來處理兼容性問題。 它能將 ES6 的一些語法進行編譯轉換,比如箭頭函數(shù)、點點點運算符等。但是如果是 async 函數(shù)、promise 對象、數(shù)組的一些方法(includes)等,它沒辦法處理。 所以此時我們 js 代碼仍然存在兼容性問題,一旦遇到低版本瀏覽器會直接報錯。所以我們想要將 js 兼容性問題徹底解決 core-js 是專門用來做 ES6 以及以上 API 的 polyfill。 polyfill翻譯過來叫做墊片/補丁。就是用社區(qū)上提供的一段代碼,讓我們在不兼容某些新特性的瀏覽器上,使用該新特性。 使用 下載 npm i core-js 三種使用方式 手動全部引入 import "core-js"; 這樣引入會將所有兼容性代碼全部引入,體積太大了。我們只想引入 promise 的 polyfill。 手動按需引入 import "core-js/es/promise"; 只引入打包 promise 的 polyfill,打包體積更小。但是將來如果還想使用其他語法,我需要手動引入庫很麻煩。 自動按需引入 修改babel.config.js的智能預設,幫助我們進行core.js的按需加載 // babel.config.js module.exports = { // 智能預設:能夠編譯ES6語法 presets: [ [ "@babel/preset-env", // 按需加載core-js的polyfill { useBuiltIns: "usage", corejs: { version: "3", proposals: true } }, ], ], }; 此時就會自動根據(jù)我們代碼中使用的語法,來按需加載相應的 polyfill 了。 PWA 介紹 開發(fā) Web App 項目,項目一旦處于網絡離線情況,就沒法訪問了。 我們希望給項目提供離線體驗。 漸進式網絡應用程序(progressive web application - PWA): 是一種可以提供類似于 native app(原生應用程序) 體驗的 Web App 的技術。 其中最重要的是,在 離線(offline) 時應用程序能夠繼續(xù)運行功能。 內部通過 Service Workers 技術實現(xiàn)的。 使用 下載 npm i workbox-webpack-plugin -D 配置 // path 為 Node.js的核心模塊,專門用來處理文件路徑 const path = require('path') const os = require("os"); const ESLintWebpackPlugin = require("eslint-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); const TerserPlugin = require("terser-webpack-plugin"); const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin"); const PreloadWebpackPlugin = require("@vue/preload-webpack-plugin"); const WorkboxPlugin = require("workbox-webpack-plugin"); // cpu核數(shù) const threads = os.cpus().length; // 獲取處理樣式的Loaders const getStyleLoaders = (preProcessor) => { return [ MiniCssExtractPlugin.loader, "css-loader", { loader: "postcss-loader", options: { postcssOptions: { plugins: [ "postcss-preset-env", // 能解決大多數(shù)樣式兼容性問題 ], }, }, }, preProcessor, ].filter(Boolean); }; module.exports = { // 入口 entry: { // 需要一個相對路徑 index: './src/index.js' }, // 輸出 output: { // 需要一個絕對路徑 path: path.resolve(__dirname, '../dist'), clean: true, // chunk的name是啥呢? 比如: entry中xxx: "./src/xxx.js", name就是xxx。注意是前面的xxx,和文件名無關。 // 為什么需要這樣命名呢?如果還是之前寫法main.js,那么打包生成兩個js文件都會叫做main.js會發(fā)生覆蓋。(實際上會直接報錯的) // [contenthash:8]使用contenthash,取8位長度 filename: "static/js/[name].[contenthash:8].js", // 入口文件打包輸出資源命名方式 chunkFilename: "static/js/[name].[contenthash:8].chunk.js", // 動態(tài)導入輸出資源命名方式 assetModuleFilename: "static/media/[name].[hash][ext]", // 圖片、字體等資源命名方式(注意用hash) }, // Performance 這些選項可以控制 webpack 如何通知「資源(asset)和入口起點超過指定文件限制」。 performance: { hints: false }, devtool: "source-map", // 解析器 module: { rules: [ { oneOf: [ { test: /\.css$/, // loader的執(zhí)行順序是從右往左的,所以這里先寫style-loader,再寫css-loader // 如果只使用一個loader的話,可以使用loader屬性代替use,如下 // loader:"style-loader" use: getStyleLoaders(), }, { // 正則匹配所有已.less文件結尾的文件 test: /\.less$/, // loader的執(zhí)行順序是從右往左的,所以這里先寫style-loader,再寫css-loader // 如果只使用一個loader的話,可以使用loader屬性代替use,如下 // loader:"style-loader" use: getStyleLoaders("less-loader"), }, { test: /\.s[ac]ss$/, use: getStyleLoaders("sass-loader"), }, { test: /\.styl$/, use: getStyleLoaders("stylus-loader"), }, { test: /\.(png|jpe?g|gif|webp)$/, type: "asset", // 類似于 module.generator,你可以用 module.parser 在一個地方配置所有解析器的選項。 parser: { // 如果一個模塊源碼大小小于 maxSize,那么模塊會被作為一個 Base64 編碼的字符串注入到包中, 否則模塊文件會被生成到輸出的目標目錄中。 dataUrlCondition: { maxSize: 10 * 1024 // 小于10kb的圖片會被base64處理 } }, // 可以使用 module.generator 在一個地方配置所有生成器的選項 // generator: { // // 將圖片文件輸出到 static/imgs 目錄中 // // 將圖片文件命名 [hash:8][ext][query] // // [hash:8]: hash值取8位 // // [ext]: 使用之前的文件擴展名 // // [query]: 添加之前的query參數(shù) // filename: "static/imgs/[hash:8][ext][query]", // }, }, { test: /\.(ttf|woff2?|svg)$/, // 發(fā)送一個單獨的文件并導出 URL type: "asset/resource", // generator: { // filename: "static/media/[hash:8][ext][query]", // }, }, { test: /\.js$/, // exclude: /node_modules/, // 排除node_modules代碼不編譯 include: path.resolve(__dirname, "../src"), // 也可以用包含 use: [ { loader: "thread-loader", // 開啟多進程 options: { workers: threads, // 數(shù)量 }, }, { loader: "babel-loader", options: { cacheDirectory: true, // 開啟babel編譯緩存 cacheCompression: false, // 緩存文件不要壓縮 plugins: ["@babel/plugin-transform-runtime"], // 減少代碼體積 }, }, ], }, ] } ] }, plugins: [ new ESLintWebpackPlugin({ // 指定檢查文件的根目錄 context: path.resolve(__dirname, "../src"), exclude: "node_modules", // 默認值 cache: true, // 開啟緩存 // 緩存目錄 cacheLocation: path.resolve( __dirname, "../node_modules/.cache/.eslintcache" ), threads, // 開啟多進程 }), new HtmlWebpackPlugin({ // 以 public/index.html 為模板創(chuàng)建文件 // 新的html文件有兩個特點:1. 內容和源文件一致 2. 自動引入打包生成的js等資源 template: path.resolve(__dirname, "../public/index.html"), }), // 提取css成單獨文件 new MiniCssExtractPlugin({ // 定義輸出文件名和目錄 filename: "static/css/[name].[contenthash:8].css", chunkFilename: "static/css/[name].[contenthash:8].chunk.css", }), // new CssMinimizerPlugin() new PreloadWebpackPlugin({ rel: "preload", // preload兼容性更好 as: "script", // rel: 'prefetch' // prefetch兼容性更差 }), new WorkboxPlugin.GenerateSW({ // 這些選項幫助快速啟用 ServiceWorkers // 不允許遺留任何“舊的” ServiceWorkers clientsClaim: true, skipWaiting: true, }), ], // 推薦將壓縮操作放在這里 optimization: { minimize: true, minimizer: [ // css壓縮也可以寫到optimization.minimizer里面,效果一樣的 new CssMinimizerPlugin(), // 當生產模式會默認開啟TerserPlugin,但是我們需要進行其他配置,就要重新寫了 new TerserPlugin({ parallel: threads // 開啟多進程 }), // 壓縮圖片 new ImageMinimizerPlugin({ minimizer: { implementation: ImageMinimizerPlugin.imageminGenerate, options: { plugins: [ ["gifsicle", { interlaced: true }], ["jpegtran", { progressive: true }], ["optipng", { optimizationLevel: 5 }], [ "svgo", { plugins: [ "preset-default", "prefixIds", { name: "sortAttrs", params: { xmlnsOrder: "alphabetical", }, }, ], }, ], ], }, }, }), ], splitChunks: { chunks: "all" }, // 提取runtime文件 runtimeChunk: { name: (entrypoint) => `runtime~${entrypoint.name}`, // runtime文件命名規(guī)則 }, }, // 開發(fā)服務器 // devServer: { // host: "localhost", // 啟動服務器域名 // port: "3000", // 啟動服務器端口號 // open: true, // 是否自動打開瀏覽器 // }, mode: 'production' } 問題與解決 此時如果直接通過 VSCode 訪問打包后頁面,在瀏覽器控制臺會發(fā)現(xiàn) SW registration failed。 因為我們打開的訪問路徑是:http://127.0.0.1:5500/dist/index.html。此時頁面會去請求 service-worker.js 文件,請求路徑是:http://127.0.0.1:5500/service-worker.js,這樣找不到會 404。 實際 service-worker.js 文件路徑是:http://127.0.0.1:5500/dist/service-worker.js。 解決 下載包 npm i serve -g serve 也是用來啟動開發(fā)服務器來部署代碼查看效果的。 運行指令 serve dist 此時通過 serve 啟動的服務器我們 service-worker 就能注冊成功了。 高級總結 我們從 4 個角度對 webpack 和代碼進行了優(yōu)化: 提升開發(fā)體驗 使用 Source Map 讓開發(fā)或上線時代碼報錯能有更加準確的錯誤提示。提升 webpack 提升打包構建速度使用 HotModuleReplacement 讓開發(fā)時只重新編譯打包更新變化了的代碼,不變的代碼使用緩存,從而使更新速度更快。使用 OneOf 讓資源文件一旦被某個 loader 處理了,就不會繼續(xù)遍歷了,打包速度更快。使用 Include/Exclude 排除或只檢測某些文件,處理的文件更少,速度更快。使用 Cache 對 eslint 和 babel 處理的結果進行緩存,讓第二次打包速度更快。使用 Thead 多進程處理 eslint 和 babel 任務,速度更快。(需要注意的是,進程啟動通信都有開銷的,要在比較多代碼處理時使用才有效果) 減少代碼體積 使用 Tree Shaking 剔除了沒有使用的多余代碼,讓代碼體積更小。使用 @babel/plugin-transform-runtime 插件對 babel 進行處理,讓輔助代碼從中引入,而不是每個文件都生成輔助代碼,從而體積更小。使用 Image Minimizer 對項目中圖片進行壓縮,體積更小,請求速度更快。(需要注意的是,如果項目中圖片都是在線鏈接,那么就不需要了。本地項目靜態(tài)圖片才需要進行壓縮。) 優(yōu)化代碼運行性能 使用 Code Split 對代碼進行分割成多個 js 文件,從而使單個文件體積更小,并行加載 js 速度更快。并通過 import 動態(tài)導入語法進行按需加載,從而達到需要使用時才加載該資源,不用時不加載資源。使用 Preload / Prefetch 對代碼進行提前加載,等未來需要使用時就能直接使用,從而用戶體驗更好。使用 Network Cache 能對輸出資源文件進行更好的命名,將來好做緩存,從而用戶體驗更好。使用 Core-js 對 js 進行兼容性處理,讓我們代碼能運行在低版本瀏覽器。使用 PWA 能讓代碼離線也能訪問,從而提升用戶體驗。 如果你學習到這了,那么恭喜你,你已經可以搞定面試官了!??! 該文章在 2025/4/30 15:36:46 編輯過 |
關鍵字查詢
相關文章
正在查詢... |