日本xxxx18视频在线观看-日本xxxx1819-日本xxxwww在线观看-日本xxx-日本xx-日本www在线视频

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發(fā)文檔 其他文檔  
 
網站管理員

JAVASCRIPT前端框架WEBPACK5從入門到精通

admin
2024年4月3日 11:2 本文熱度 1262

前言

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 編輯過
關鍵字查詢
相關文章
正在查詢...
點晴ERP是一款針對中小制造業(yè)的專業(yè)生產管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國內大量中小企業(yè)的青睞。
點晴PMS碼頭管理系統(tǒng)主要針對港口碼頭集裝箱與散貨日常運作、調度、堆場、車隊、財務費用、相關報表等業(yè)務管理,結合碼頭的業(yè)務特點,圍繞調度、堆場作業(yè)而開發(fā)的。集技術的先進性、管理的有效性于一體,是物流碼頭及其他港口類企業(yè)的高效ERP管理信息系統(tǒng)。
點晴WMS倉儲管理系統(tǒng)提供了貨物產品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質期管理,貨位管理,庫位管理,生產管理,WMS管理系統(tǒng),標簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務都免費,不限功能、不限時間、不限用戶的免費OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved

主站蜘蛛池模板: 欧美日韩精品一区二区免费高清 | 韩日午夜在线资源一区二区 | 懂色中文一区二区在线播放 | 成人污污污www网站免费 | 国精产品一区二区三区有限 | 国产亚洲一区二区三 | 日韩aⅴ在线观看 | 我和亲女日b的性 | 国产欧美日韩在线高清 | 韩日午夜在线资源一区二区 | 国产普通话对白在线观看视频 | 国产精品免费网站 | 国产视讯手机在线播放 | 91九色国产社区在线观看 | 国产色片大全在线观看 | 男女多人混交群体交乱 | 国产91免费精品电影 | 99亚洲精品高清一二区 | 国产精品青青在线观看看 | 欧美乱码一二三区视频 | 国产精品极品美女自在线观看免 | 欧美在线视频播放 | 国产红亚洲视频日韩 | 国产极品乱码在线观看 | 欧美大片日韩精品 | 成人免费高清视频网址 | 成人午夜免费视频免费看 | 精品人成视频免费国产 | 国产在线精品一区二区不卡顿 | 欧美变态一区二区 | 成人永久免费网站在线观看 | 国产日韩欧美亚洲精品95 | 日产影视剧影视大全 | 日韩亚洲人成在线综合日本 | 国产亚洲欧美性爱 | 精品女同一区你懂在线国产 | 日韩在线观看自拍网址视频免费 | 欧美三级一区二区 | 日本免费人成黄页在线观看视频 | 国产精品日韩在线观看一区二区 | 日韩a级一区二区三区四区 国产剧情91露脸一区 |