使用 Webpack 進行生產環境配置(附 Demo)

本文從代碼壓縮、代碼拆分、樣式分離等三個方面對生產環境的代碼進行了優化。只是最簡單的一些配置,若是真正運用到項目中,還須要根據項目添加更多配置。css


前言

本文講述的是如何對生產環境下的代碼進行壓縮,若是還不是太瞭解 Webpack 的朋友,能夠先看一下個人上一篇文章:從零開始搭建一個 Webpack 開發環境配置(附 Demo)html

本文項目代碼位置:源碼地址webpack


環境搭建

項目結構

首先編寫一個項目,初始化 npm,而後 在本地安裝 webpack,接着安裝 webpack-cli(此工具用於在命令行中運行 webpack):git

$ mkdir webpack-prod-demo

$ cd webpack-prod-demo

$ npm init -y

$ npm install webpack webpack-cli --save-dev
複製代碼

projectgithub

webpack-prod-demo
|- package.json
|- /public
  |- index.html
|- /src
  |- index.js
  |- index.css
複製代碼

pubic/index.htmlweb

<!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.jsnpm

import './index.css';

function component() {
    var element = document.createElement('div');

    element.innerHTML = 'Hello World';
  
    return element;
}
  
document.body.appendChild(component());
複製代碼

index.cssjson

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 文件,並進行基本配置bash

安裝插件

# 安裝 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 配置,仍是須要多多留意最新技術的出現,多蒐集有用的插件和配置。聚沙成塔,慢慢的就能配置出更好的項目腳手架(目前這也是個人目標)。

相關文章
相關標籤/搜索