一、webpack 性能優化之 HRM
-
創建空文件夾,通過
npm init
命令初始化package.json
文件,通過npm install webpack webpack-cli -g
命令全局下載webpack
和webpack-cli
,通過npm install webpack webpack-cli -D
命令本地下載webpack
和webpack-cli
,通過npm i style-loader css-loader -D
命令下載style-loader
和css-loader
,通過npm i less less-loader -D
命令下載less
和less-loader
,通過npm i html-webpack-plugin -D
命令下載html-webpack-plugin
插件,通過npm i url-loader file-loader -D
命令下載url-loader
和file-loader
,通過npm i html-loader -D
命令下載html-loader
。 -
創建
src
文件夾,在裏面創建css
、js
、imgs
和media
文件夾,以及index.html
。css
文件夾中放index.less
和字體圖標資源樣式,js
文件夾中放index.js
和print.js
,imgs
文件夾中放圖片資源,media
文件夾放字體圖標的相關資源,代碼如下所示:
-
index.less
#box { width: 200px; height: 200px; background-image: url('../imgs/angular.jpg'); background-repeat: no-repeat; background-size: 100% 100%; }
-
index.js
// 引入 import print from './print.js'; import '../css/iconfont.css'; import '../css/index.less'; console.log('index.js 文件被加載') print() function add(x, y) { return x + y; } console.log(add(1, 2)); if (module.hot) { module.hot.accept('./print.js', function () { print(); }) }
-
print.js
// 引入 import print from './print.js'; import '../css/iconfont.css'; import '../css/index.less'; console.log('index.js 文件被加載') print() function add(x, y) { return x + y; } console.log(add(1, 2)); if (module.hot) { module.hot.accept('./print.js', function () { print(); }) }
-
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>開發環境</title> </head> <body> <h1>開發環境配置</h1> <span class="iconfont icon-icon-test"></span> <span class="iconfont icon-icon-test2"></span> <span class="iconfont icon-icon-test3"></span> <span class="iconfont icon-icon-test1"></span> <div id="box"></div> <img src="./imgs/vue.jpg" alt="vue"> <img src="./imgs/react.png" alt="react"> </body> </html>
- 創建
webpack.config.js
文件,通過require
引入path
和html-webpack-plugin
,entry
是入口文件,output
是出口文件,filename
是打包輸出後的文件名,path
是輸出路徑。module
是loader
的配置,rules
是詳細的loader
配置。第一個規則是處理less
資源,匹配以.less
結尾的文件,使用的loader
是style-loader
、css-loader
和less-loader
。第二個規則是處理css
資源, 匹配以.css
結尾的文件,使用的loader
是style-loader
和css-loader
。第三個規則是處理圖片資源,匹配以jpg
、png
和gif
結尾的文件圖片資源,使用的loader
是url-loader
,進行options
配置,limit
是限制圖片的大小小於8kb
,就會被base64
處理,給圖片做優化,name
給圖片做重命名,取hash
值的前10
位,取文件的原來擴展名,關閉es6
模塊化,輸出路徑爲imgs
。第四個規則是 處理html
中的img
資源,匹配以.html
結尾的文件,使用的loader
是html-loader
。由於處理圖片資源採用的是ES6 module
解析,而html
文件中的img
資源使用commonJS
解析,會造成衝突,需要關閉ES6 module
。第五個規則是處理其它資源,使用exclude
不包括上面的所有資源,html|css|js|less|jpg|png|gif
,使用file-loader
,通過options
配置name
的值爲取hash
值的前10
位,取文件的原來擴展名,通過outputPath
設置輸出路徑爲media
。plugins
裏面是一些插件配置,通過new HtmlWebpackPlugin()
,複製裏面的文件,並自動引入打包輸出的所有資源(JS/CSS
),設置mode
模式爲development
開發模式。在devServer
中,contentBase
是項目構建後路徑,compress
是啓動gzip
壓縮,port
是端口號,open
是自動打開瀏覽器,hot
是開啓HRM
功能,代碼如下所示:
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: ['./src/js/index.js', './src/index.html'],
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
esModule: false,
outputPath: 'imgs'
}
},
{
test: /\.html$/,
loader: 'html-loader'
},
{
exclude: /\.(html|js|css|less|jpg|png|gif)/,
loader: 'file-loader',
options: {
name: '[hash:10].[ext]',
outputPath: 'media'
}
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
mode: 'development',
devServer: {
contentBase: resolve(__dirname, 'build'),
compress: true,
port: 3000,
open: true,
hot: true
}
};
HMR
是hot module replacement
,熱模塊替換,也可以說是模塊熱替換。作用是一個模塊發生變化,只會重新打包這一個模塊,而不是打包所有模塊,極大提升構建速度,HRM
在樣式文件、JS
文件和HTML
文件的不同應用:
- 樣式文件:可以使用
HMR
功能,因爲style-loader
內部實現了 JS
文件:默認不能使用HMR
功能,需要修改js
代碼,添加支持HMR
功能的代碼。注意的是,HMR
功能對js
的處理,只能處理非入口js
文件的其他文件,添加代碼, 全局尋找module
這個變量,有沒有hot
這個屬性,一旦module.hot
爲true
,說明開啓了HMR
功能,方法會監聽print.js
文件的變化,一旦發生變化,其他模塊不會重新打包構建,會執行後面的回調函數,如下所示:
if (module.hot) {
module.hot.accept('./print.js', function () {
print();
})
}
HTML
文件:默認不能使用HMR
功能.同時會導致問題,html
文件不能熱更新了,不用做HMR
功能。如果需要做,修改entry
入口,將html
文件引入,代碼如下所示:
entry: ['./src/js/index.js', './src/index.html']
- 在命令行輸入
npx webpack-dev-server
命令,項目就會自動打包編譯。每次在更改完代碼後,自動編譯看到最新的顯示內容。在build
打包文件中,也可以看到打包後的css
、js
、imgs
和media
文件夾,以及index.html
。
二、webpack 性能優化之 source-map
- 在上面
HRM
熱模塊替換的項目中,修改webpack.config.js
文件,添加devtool
來指定不同的source-map
,代碼如下所示:
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: ['./src/js/index.js', './src/index.html'],
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
esModule: false,
outputPath: 'imgs'
}
},
{
test: /\.html$/,
loader: 'html-loader'
},
{
exclude: /\.(html|js|css|less|jpg|png|gif)/,
loader: 'file-loader',
options: {
name: '[hash:10].[ext]',
outputPath: 'media'
}
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
mode: 'development',
devServer: {
contentBase: resolve(__dirname, 'build'),
compress: true,
port: 3000,
open: true,
hot: true
},
devtool: 'eval-source-map'
};
source-map
是一種 提供源代碼到構建後代碼映射 技術,如果構建後代碼出錯了,通過映射可以追蹤源代碼錯誤,不同source-map
之間的比較,如下所示:
類型 | 類型描述 | 類型方式 |
---|---|---|
source-map | 錯誤代碼準確信息和源代碼的錯誤位置 | 外部 |
inline-source-map | 只生成一個內聯source-map,錯誤代碼準確信息和源代碼的錯誤位置 | 內聯 |
hidden-source-map | 錯誤代碼錯誤原因,但是沒有錯誤位置,不能追蹤源代碼錯誤,只能提示到構建後代碼的錯誤位置 | 外部 |
eval-source-map | 每一個文件都生成對應的source-map,都在eval,錯誤代碼準確信息 和源代碼的錯誤位置 | 內聯 |
nosources-source-map | 錯誤代碼準確信息, 但是沒有任何源代碼信息 | 外部 |
cheap-source-map | 錯誤代碼準確信息 和 源代碼的錯誤位置,只能精確的行 | 外部 |
cheap-module-source-map | 錯誤代碼準確信息 和 源代碼的錯誤位置,module會將loader的source map加入 | 外部 |
- 內聯和外部的區別:
- 外部生成了文件,內聯沒有
- 內聯構建速度更快
-
開發環境需要速度快,調試更友好。在速度快中,
eval>inline>cheap>...
,而eval-source-map
優於eval-cheap-souce-map
。在調試友好中,cheap-souce-map
優於cheap-module-souce-map
。所以,在開發環境中,最快調試,調試更加友好,可以選擇eval-source-map
或者是eval-cheap-module-souce-map
。 -
在生產環境中,需要考慮源代碼的隱藏和調試的友好。內聯會讓代碼體積變大,所以在生產環境不用內聯。
nosources-source-map
是全部隱藏,hidden-source-map
是隻隱藏源代碼,會提示構建後代碼錯誤信息。所以,在生成環境中,可以選擇source-map
或者是cheap-module-souce-map
。 -
在命令行輸入
npx webpack-dev-server
命令,項目就會自動打包編譯。每次在更改完代碼後,自動編譯看到最新的顯示內容。在build
打包文件中,也可以看到打包後的css
、js
、imgs
和media
文件夾,以及index.html
。
三、webpack 性能優化之 oneOf
-
創建空文件夾,通過
npm init
命令初始化package.json
文件,通過npm install webpack webpack-cli -g
命令全局下載webpack
和webpack-cli
,通過npm install webpack webpack-cli -D
命令本地下載webpack
和webpack-cli
,通過npm i html-webpack-plugin mini-css-extract-plugin optimize-css-assets-webpack-plugin -D
命令下載html-webpack-plugin
、mini-css-extract-plugin
和optimize-css-assets-webpack-plugin
,通過npm i eslint-loader eslint -D
命令下載eslint-loader
和eslint
,通過npm i eslint-config-airbnb-base eslint-plugin-import eslint -D
命令下載eslint-config-airbnb-base
、eslint-plugin-import
和eslint
,eslint-config-airbnb-base
依賴於eslint-plugin-import
和eslint
,通過npm i postcss-loeader postcss-preset-env -D
命令下載postcss-loeader
和postcss-preset-env
,通過npm i css-loader less-loader -D
命令下載css-loader
和less-loader
,通過npm i url-loader file-loader html-loader -D
命令下載url-loader
、file-loader
和html-loader
,通過npm i babel-loader @babel/core @babel/preset-env -D
命令下載babel-loader
、@babel/core
和@babel/preset-env
,通過npm i @babel/polyfill core-js -D
命令下載@babel/polyfill
和core-js
。 -
創建
src
文件夾,在裏面創建index.js
、index.html
、a.css
和b.css
文件,代碼如下所示:
- index.js
import '../css/a.css'; import '../css/b.css'; function add(x, y) { return x + y; } console.log(add(2, 5));
- index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>webpack</title> </head> <body> <h1>hello webpack</h1> <div id="box1"></div> <div id="box2"></div> </body> </html>
- a.css
#box1 { width: 100px; height: 100px; background-color: pink; }
- b.css
#box2 { width: 200px; height: 200px; background-color: deeppink; }
- 創建
webpack.config.js
文件,通過require
引入path
、html-webpack-plugin
、mini-css-extract-plugin
和optimize-css-assets-webpack-plugin
。通過process.env.NODE_ENV
定義nodejs
環境變量:決定使用browserslist
的哪個環境。定義一個複用loader
,在下面匹配.css
和.less
文件的時候,需要重複使用。通過MiniCssExtractPlugin.loader
提取css
單獨文件,使用css-loader
,使用postcss-loader
做css
的兼容性處理,同時使用postcss-preset-env
做css
的按需加載的兼容性處理,options
是postcss-loader
的配置項,ident
是固定寫法,plugins
是使用postcss-preset-env
這個插件。entry
是入口文件,output
是出口文件,filename
是輸出的文件名,path
是文件的輸出路徑。module
是loader
的配置,rules
是詳細的loader
配置。在rules
中使用oneOf
,以下的loader
只會匹配一個,不能有兩個配置處理同一種類型文件,如果有需要提取出來。第一個規則是匹配以.css
結尾的文件,通過use
使用commonCssLoader
這個抽出複用的loader
。第二個規則是匹配以.less
結尾的文件,通過use
使用commonCssLoader
這個抽出複用的loader
和less-loader
。第三個規則是匹配以.js
結尾的文件,通過exclude
不包括node_modules
,通過enforce
優先執行,通過loader
使用eslint-loader
,通過options
設置自動修復eslint
的錯誤。第四個規則是匹配以.js
結尾的文件,通過exclude
不包括node_modules
,通過loader
使用babel-loader
,通過options
進行配置,presets
爲預設,指示babel
做怎麼樣的兼容性處理,使用babel/preset-env
這個預設。通過useBuiltIns
按需加載,通過corejs
指定core-js
版本,通過targets
指定兼容性做到哪個版本瀏覽器。js
兼容性處理需要使用到babel-loader
、@babel/core
和@babel/preset-env
。如果是基本js
兼容性處理,需要使用@babel/preset-env
,但是隻能轉換基本語法,如promise
高級語法不能轉換。如果使用全部js
兼容性處理,需要使用@babel/polyfill
,但是隻要解決部分兼容性問題,但是將所有兼容性代碼全部引入,體積太大了,所有就需要做兼容性處理的就做做兼容性處理的就做,使用core-js
。第五個規則是匹配以.jpg
、png
和gif
格式的圖片資源,通過loader
使用url-loader
,url-loader
依賴於file-loader
。進行options
配置,設置limit
爲8 * 1024
,圖片大小小於8kb
,就會被base64
處理,給圖片做優化。這樣做的優點是減少請求數量(減輕服務器壓力),缺點是圖片體積會更大(文件請求速度更慢)。設置esModule
爲false
,由於url-loader
默認使用es6
模塊化解析,而html-loader
引入圖片是commonjs
,解析時會出問題爲[object Module]
,所以關閉url-loader
的es6
模塊化,使用commonjs
解析。設置name
爲[hash:10].[ext]
,給圖片進行重命名,因爲圖片值爲hash
,[hash:10]
取圖片的hash
的前10
位,[ext]
取文件原來擴展名。第三個規則是匹配以.html
結尾的文件,使用loader
爲html-loader
,處理html
文件的img
圖片(負責引入img
,從而能被url-loader
進行處理)。第六個規則是匹配以.html
結尾的文件,通過loader
使用html-loader
。第七個規則是匹配其它資源,通過loader
使用file-loader
,通過options
進行輸出路徑配置,輸出文件夾爲media
。plugins
裏面是一些插件配置,通過MiniCssExtractPlugin
插件提取css
以及對輸出的css
文件進行重命名,通過OptimizeCssAssetsWebpackPlugin
對css
文件進行壓縮,通過HtmlWebpackPlugin
複製裏面的文件,並自動引入打包輸出的所有資源(JS/CSS
),進行minify
配置,通過collapseWhitespace
移除空格,通過removeComments
移除註釋,壓縮 html 文件。設置mode
爲production
,在生產環境下webpack
會自動壓縮js
代碼,代碼如下所示:
const { resolve } = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
process.env.NODE_ENV = 'production'
const commonCssLoader = [
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [
require('postcss-preset-env')()
]
}
}
]
module.exports = {
entry: 'src/js/index.js',
output: {
filename: 'built.js',
path: resolve(__dirname, 'build')
},
module: {
rules:[
{
test: /\.js$/,
exclude: /node_modules/,
enforce: 'pre',
loader: 'eslint-loader',
options: {
fix: true
}
},
{
oneOf: [
{
test: /\.css$/,
use: [...commonCssLoader]
},
{
test: /\.less$/,
use: [...commonCssLoader, 'less-loader']
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: {
version: 3
},
targets: {
chrome: '60',
firefox: '60',
ie: '9',
safari: '10',
edge: '17'
}
}
]
}
},
{
test: /\.(jpg|png|gif)/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:].[ext]',
outputPath: 'imgs',
esModule: false
}
},
{
test: /\.html$/,
loader: 'html-loader'
},
{
test: /\.(js|css|less|html|jpg|png|gif)/,
loader: 'file-loader',
options: {
outputPath: 'media'
}
}
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/built.css'
}),
new OptimizeCssAssetsWebpackPlugin(),
new HtmlWebpackPlugin({
template: './src/index.html',
minify: {
collapseWhitespace: true,
removeComments: true
}
})
],
mode: 'production'
}
- 在
package.json
這個文件中,進行配置browserslist
,在css
兼容處理的時候,需要幫postcss
找到package.json
中browserslist
裏面的配置,通過配置加載指定的css
兼容性樣式,development
爲開發環境配置,last 1 chrome version
表示兼容最近的chrome
版本,last 1 firefox version
表示兼容最近的firefox
版本,last 1 safari version
表示兼容最近的safari
版本。production
爲生產環境配置,>0.2%
表示大於99.8%
的瀏覽器,not dead
表示不要已經死的瀏覽器,not op_mini all
表示不要使用op_mini all
瀏覽器。eslint
並不知道要檢查什麼,所有就有airbnb
風格指南,airbnb
需要使用eslint-config-airbnb-base
這個庫,這個庫依賴於eslint-plugin-import
和eslint
。所有需要在package.json
中eslintConfig
中設置檢查規則,設置extends
爲airbnb-base
,代碼如下所示:
"browserslist": {
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
"production": [
">0.2%",
"not dead",
"not op_mini all"
]
},
"eslintConfig": {
"extends": "airbnb-base"
}
- 在命令行輸入
webpack
命令,資源就會進行打包,以及相應的壓縮代碼。