本文從程式碼壓縮、程式碼拆分、樣式分離等三個方面對生產環境的程式碼進行了優化。只是最簡單的一些配置,如果真正運用到專案中,還需要根據專案新增更多配置。
前言
本文講述的是如何對生產環境下的程式碼進行壓縮,如果還不是太瞭解 Webpack 的朋友,可以先看一下我的上一篇文章:從零開始搭建一個 Webpack 開發環境配置(附 Demo)
本文專案程式碼位置:原始碼地址
環境搭建
專案結構
首先編寫一個專案,初始化 npm,然後 在本地安裝 webpack,接著安裝 webpack-cli(此工具用於在命令列中執行 webpack):
$ mkdir webpack-prod-demo
$ cd webpack-prod-demo
$ npm init -y
$ npm install webpack webpack-cli --save-dev
複製程式碼
project
webpack-prod-demo
|- package.json
|- /public
|- index.html
|- /src
|- index.js
|- index.css
複製程式碼
pubic/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Webpack 生產環境配置</title>
</head>
<body>
</body>
</html>
複製程式碼
index.js
import './index.css';
function component() {
var element = document.createElement('div');
element.innerHTML = 'Hello World';
return element;
}
document.body.appendChild(component());
複製程式碼
index.css
div {
color: blue;
text-align: center;
}
複製程式碼
package.json
{
"name": "webpack-prod-demo",
"version": "1.0.0",
"description": "",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --config webpack.config.js"
},
"keywords": [],
"author": "",
"license": "ISC"
}
複製程式碼
配置 webpack.config.js 檔案
在根目錄下新建 webpack.config.js 檔案,並進行基本配置
安裝外掛
# 安裝 babel-loader
$ npm install babel-core babel-loader@7 --save-dev
# 安裝 babel presets
$ npm install babel-preset-env babel-preset-stage-0 --save-dev
# 安裝 babel plugins
$ npm install babel-plugin-transform-class-properties babel-plugin-transform-runtime babel-runtime --save-dev
# 安裝其餘所需 loader
$ npm install css-loader style-loader file-loader csv-loader xml-loader html-loader markdown-loader --save-dev
# 安裝 webpack 外掛
$ npm install clean-webpack-plugin html-webpack-plugin friendly-errors-webpack-plugin --save-dev
複製程式碼
配置 webpack.config.js:
webpack.config.js
const path = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
module.exports = {
mode: 'development',
devtool: 'hidden-source-map',
entry: './src/index.js',
output: {
filename: '[name]-[hash:8].js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new HTMLWebpackPlugin({
// 用於生成的HTML文件的標題
title: 'Webpack 生產環境配置',
// webpack 生成模板的路徑
template: './public/index.html'
}),
// 用法:new CleanWebpackPlugin(paths [, {options}])
new CleanWebpackPlugin(['dist']),
// 在命令列進行友好提示
new FriendlyErrorsWebpackPlugin()
],
module: {
rules: [
{
test: /\.js/,
include: path.resolve(__dirname, 'src'),
loader: 'babel-loader?cacheDirectory'
},
// 解析 css
{
test: /\.css$/,
include: path.resolve(__dirname, 'src'),
use: [
'style-loader',
// 還可以給 loader 新增一些配置
{
loader: 'css-loader',
options: {
// 開啟 sourceMop
sourceMap: true
}
}
]
},
// 解析圖片資源
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
},
// 解析 字型
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
},
// 解析資料資源
{
test: /\.(csv|tsv)$/,
use: [
'csv-loader'
]
},
// 解析資料資源
{
test: /\.xml$/,
use: [
'xml-loader'
]
},
// 解析 MakeDown 檔案
{
test: /\.md$/,
use: [
"html-loader",
"markdown-loader"
]
}
]
}
}
複製程式碼
在專案根目錄下建立 .babelrc 檔案
.babelrc
{
"presets": ["env", "stage-0"],
"plugins": [
"transform-runtime",
"transform-class-properties"
]
}
複製程式碼
基本配置完成後執行專案
在命令列執行指令:
$ npm run build
複製程式碼
此時在瀏覽器開啟 dist 資料夾下的 html 檔案,頁面上正常顯示 藍色居中的 Hello World
檢視此時 dist 檔案大小:
設定 mode 為 production
webpack.config.js
...
module.exports = {
mode: 'production'
...
}
複製程式碼
設定為生產環境後執行專案
在命令列執行指令:
$ npm run build
複製程式碼
此時在瀏覽器開啟 dist 資料夾下的 html 檔案,頁面上還是能夠正常顯示 藍色居中的 Hello World
檢視此時 dist 檔案大小:
本文的重點:優化打包
安裝需要用到的外掛:
# 安裝壓縮 js、 css 程式碼的外掛
$ npm install uglifyjs-webpack-plugin optimize-css-assets-webpack-plugin --save-dev
# 安裝提取 css 的外掛
$ npm install mini-css-extract-plugin --save-dev
複製程式碼
uglifyjs-webpack-plugin 和 optimize-css-assets-webpack-plugin 的使用
webpack.config.js
...;
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
module.exports = {
...,
optimization: {
// 打包壓縮js/css檔案
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
compress: {
// 在UglifyJs刪除沒有用到的程式碼時不輸出警告
warnings: false,
// 刪除所有的 `console` 語句,可以相容ie瀏覽器
drop_console: true,
// 內嵌定義了但是隻用到一次的變數
collapse_vars: true,
// 提取出出現多次但是沒有定義成變數去引用的靜態值
reduce_vars: true,
},
output: {
// 最緊湊的輸出
beautify: false,
// 刪除所有的註釋
comments: false,
}
}
}),
new OptimizeCSSAssetsPlugin({
cssProcessorOptions: {
safe: true
}
})
]
}
}
複製程式碼
uglifyjs-webpack-plugin 更多配置請參考:外掛文件。
optimize-css-assets-webpack-plugin 更多配置請參考:外掛文件。
mini-css-extract-plugin 的使用
注意: MiniCssExtractPlugin.loader 和 style-loader 一起使用可能出現問題。所以下面我將 style-loader 去掉了。
webpack.config.js
...;
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
...
plugins: [
...,
// 打包後提取出css檔案
new MiniCssExtractPlugin({
filename: '[name].[contenthash:8].css',
chunkFilename: '[name].[contenthash:8].chunk.css'
})
],
module: [
rules: [
...,
// 解析 css
{
test: /\.css$/,
include: path.resolve(__dirname, 'src'),
use: [
{
loader: MiniCssExtractPlugin.loader
},
// 還可以給 loader 新增一些配置
{
loader: 'css-loader',
options: {
// 開啟 sourceMop
sourceMap: true
}
}
]
},
...
]
]
}
複製程式碼
mini-css-extract-plugin 更多配置請參考:外掛文件。
chunk 拆分
webpack.config.js
...;
module.exports = {
...,
optimization: {
// 打包壓縮js/css檔案
minimizer: [
...
],
splitChunks: {
chunks: 'all'
}
}
}
複製程式碼
splitChunks 更多配置請參考:官方文件。
區分環境
在開發網頁的時候,一般都會有多套執行環境,例如:
- 在開發過程中方便開發除錯的環境
- 釋出到線上給使用者使用的執行環境
為了儘可能的複用程式碼,在構建的過程中需要根據目的碼要執行的環境而輸出不同的程式碼,我們需要一套機制在原始碼中去區分環境。可以通過 Webpack 內建的 DefinePlugin 外掛進行環境的區分。
區分環境的原因: 很多第三方庫中也做了環境區分的優化
- 開發環境:包含型別檢查、HTML 元素檢查等等針對開發者的警告日誌程式碼
- 線上環境:去掉了所有針對開發者的程式碼,只保留讓 React 能正常執行的部分,以優化大小和效能
注意: NODE_ENV 和 'production' 兩個值是社群的約定,通常使用這條判斷語句在區分開發環境和線上環境。
配置:
webpack.config.js
...
module.exports = {
...,
plugins: [
...,
// 區分環境
new webpack.DefinePlugin({
// 定義 NODE_ENV 環境變數為 production
'process.env': {
NODE_ENV: JSON.stringify('production')
}
})
],
...
}
複製程式碼
進行配置優化後執行專案
在命令列執行指令:
$ npm run build
複製程式碼
此時在瀏覽器開啟 dist 資料夾下的 html 檔案,頁面上仍能正常顯示 藍色居中的 Hello World
檢視此時 dist 檔案大小:
webpack.config.js 檔案最終程式碼
其餘檔案基本未進行修改。在此將 webpack.config.js 程式碼貼出來
webpack.config.js
const path = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
mode: 'production',
devtool: 'hidden-source-map',
entry: './src/index.js',
output: {
filename: '[name]-[hash:8].js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new HTMLWebpackPlugin({
// 用於生成的HTML文件的標題
title: 'Webpack 開發環境配置',
// webpack 生成模板的路徑
template: './public/index.html'
}),
// 用法:new CleanWebpackPlugin(paths [, {options}])
new CleanWebpackPlugin(['dist']),
// 在命令列進行友好提示
new FriendlyErrorsWebpackPlugin(),
// 打包後提取出css檔案
new MiniCssExtractPlugin({
filename: '[name].[contenthash:8].css',
chunkFilename: '[name].[contenthash:8].chunk.css'
}),
// 區分環境
new webpack.DefinePlugin({
// 定義 NODE_ENV 環境變數為 production
'process.env': {
NODE_ENV: JSON.stringify('production')
}
})
],
module: {
rules: [
{
test: /\.js/,
include: path.resolve(__dirname, 'src'),
loader: 'babel-loader?cacheDirectory'
},
// 解析 css
{
test: /\.css$/,
include: path.resolve(__dirname, 'src'),
use: [
{
loader: MiniCssExtractPlugin.loader
},
// 還可以給 loader 新增一些配置
{
loader: 'css-loader',
options: {
// 開啟 sourceMop
sourceMap: true
}
}
]
},
// 解析圖片資源
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
},
// 解析 字型
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
},
// 解析資料資源
{
test: /\.(csv|tsv)$/,
use: [
'csv-loader'
]
},
// 解析資料資源
{
test: /\.xml$/,
use: [
'xml-loader'
]
},
// 解析 MakeDown 檔案
{
test: /\.md$/,
use: [
"html-loader",
"markdown-loader"
]
}
]
},
optimization: {
// 打包壓縮js/css檔案
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
compress: {
// 在UglifyJs刪除沒有用到的程式碼時不輸出警告
warnings: false,
// 刪除所有的 `console` 語句,可以相容ie瀏覽器
drop_console: true,
// 內嵌定義了但是隻用到一次的變數
collapse_vars: true,
// 提取出出現多次但是沒有定義成變數去引用的靜態值
reduce_vars: true,
},
output: {
// 最緊湊的輸出
beautify: false,
// 刪除所有的註釋
comments: false,
}
}
}),
new OptimizeCSSAssetsPlugin({
cssProcessorOptions: {
safe: true
}
})
],
splitChunks: {
chunks: 'all'
}
}
}
複製程式碼
對比
通過三次打包的對比,可以看到:
- 第一次普通配置打包後,包大小為 48.1 K
- 第二次設定為生產環境後打包,包大小為 39.0 K
- 第三次進行優化配置後打包,包大小為 1.46 K
目前這個專案一個 js 檔案,程式碼量很少,但是還是可以看到優化的效果的。如果專案更大的話,優化的效果也會更明顯。當然,需要的配置可能更多了。
專案原始碼
總結
本文只是對於生產環境下程式碼打包的簡單優化,在專案實戰的過程中,可能會需要更多的配置。其實無論看多少教程,其實裡面的配置都不一定能夠滿足自身的要求。在開發的過程中,還是需要自己根據需求進行配置。
其實 Webpack 的學習並不難,根據官網的說明和指南,其實基本都會使用。難的是各種 loader、plugin 自身的配置。這些都需要去到 loader 和 plugin 的網站上深入研究才可以。所以想要更好的進行 webpack 配置,還是需要多多留意最新技術的出現,多蒐集有用的外掛和配置。積少成多,慢慢的就能配置出更好的專案腳手架(目前這也是我的目標)。