Webpack4学习笔记(五)——分离production和development环境 - 简书

这是一个关于Webpack4的文章系列,其中包含以下文章:

本系列文章源代码:

git clone https://github.com/davenkin/webpack-learning.git
    • *

欢迎访问本文github代码

上一篇文章中,我们讲到了CSS的处理,本文我们将讲解开发环境与产品环境的分离配置。

通常来讲,production环境与development的配置有不一样的地方,比如production环境要求更小的代码量以加快网页的加载速度,因此通常都会对源代码进行压缩,比如使用UglifyJsPlugin插件等;而对于development环境来说,我们希望有热加载(HMR)功能等。

Webpack4默认支持mode参数用于区分production环境和development环境。由于2个环境的配置有相同部分又有差异部分,因此通常的做法是提供3个webpack配置文件,一个是公有的配置文件,一个是针对production的配置文件,一个针对development,后两个配置文件通过webpack-merge引入公有的配置文件。

在默认情况下,Webpack4已经对两种mode分别有不同的配置了,比较明显的不同便是production环境引入了UglifyJsPlugin插件,而development没有。

具体来讲,webpack在默认情况下对production和development有以下不同配置:

模式

默认配置

production

通过DefinePlugin设置 process.env.NODE_ENV=production。启用FlagDependencyUsagePlugin、FlagIncludedChunksPlugin、ModuleConcatenationPlugin、NoEmitOnErrorsPlugin、OccurrenceOrderPlugin、SideEffectsFlagPlugin 和 UglifyJsPlugin插件。

development

通过DefinePlugin设置process.env.NODE_ENV=development。启用 NamedChunksPlugin和NamedModulesPlugin插件。

分离配置文件

多数时候,webpack默认的配置是无法满足我们的需求的,我们希望对于不同的环境进行不同的配置,此时一种常用的做法是维护一个作用于所有环境的公有配置文件,然后针对每个环境再创建单独的配置文件,环境配置文件通过webpack-merge引用公有配置文件中的内容。

创建3个配置文件:

  • webpack.base.conf.js (公有配置文件)
  • webpack.prod.conf.js(production环境配置文件)
  • webpack.dev.conf.js (development环境配置文件)

创建webpack.base.conf.js如下:

const path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');

function resolve(dir) {
    return path.join(__dirname, '..', dir)
}

module.exports = {
    entry: {
        'index': './src/index.js',
    },
    output: {
        filename: '[name].[contenthash].js',
        chunkFilename: '[name].[contenthash].js',
        path: path.resolve(__dirname, 'distribution')
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                loader: 'babel-loader',
                include: [resolve('src')],
                exclude: [resolve('node_modules')]
            },
            {
                test: /\.css$/,
                use: [
                    'style-loader',
                    {
                        loader: 'css-loader',
                        options: {
                            sourceMap: true,
                            modules: true,
                            localIdentName: '[name]---[local]---[hash:base64:5]'
                        }
                    }
                ]
            }
        ]
    },
    plugins: [
        new CleanWebpackPlugin(['distribution']),
        new HtmlWebpackPlugin({
            template: './src/index.html',
            filename: 'index.html'
        })
    ]

};

webpack.prod.conf.js与webpack.dev.conf.js使用webpack-merge引用webpack.base.conf.js,以webpack.dev.conf.js为例:

const merge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.base.conf');

const webpackConfig = merge(baseWebpackConfig, {
    mode:'development'
    //environment specific config goes here
});

module.exports = webpackConfig;

此时,我们通过配置mode参数声明当前配置所处的环境。另外,还需要修改一下package.json中的scripts:

...
"scripts": {
    "build": "webpack --config webpack.prod.conf.js",
    "start": "webpack-dev-server --open --config webpack.dev.conf.js"
  },
...

source map

source map有多种风格配置,不同风格各有利弊,对于不同的环境推荐配置如下:

  • development:inline-source-map
  • production:source-map

配置webpack.dev.conf.js:

...
const webpackConfig = merge(baseWebpackConfig, {
    mode:'development',
    devtool: 'inline-source-map'

    //environment specific config goes here
});
...

配置webpack.prod.config.js如下:

...
const webpackConfig = merge(baseWebpackConfig, {
    mode:'production',
    devtool:'source-map'
    //environment specific config goes here
});
...

本地调试

对于development环境来说,需要配置本地调试服务器,以及HMR。

配置webpack-dev-server

webpack-dev-server用于本地调试,配置webpack.dev.conf.js:

...
const webpackConfig = merge(baseWebpackConfig, {
    mode:'development',
    devtool: 'inline-source-map',
    devServer: {
        contentBase: './distribution',
        inline:true,//do not use iframe for the page, true is default
        open: true,//open browser after dev server starts, true is default
        port: 8080,//8080 is default
        proxy: {//proxy backend api
            '/api': 'http://localhost:3000'
        }
    }

    //environment specific config goes here
});
...

devServer下的很多配置项都可以通过命令行参数的形式替代,具体请参考webpack-dev-server官方文档

配置HMR

在webpack.dev.conf.js,在其中加入HotModuleReplacementPlugin插件:

...
plugins: [
        new webpack.HotModuleReplacementPlugin()
    ]
...

另外,需要在devServer中配置hot:true

devServer: {
        contentBase: './distribution',
        inline: true,//do not use iframe for the page, true is default
        open: true,//open browser after dev server starts
        port: 8080,//8080 is default
        proxy: {//proxy backend api
            '/api': 'http://localhost:3000'
        },
        hot: true
    },

此时报错:

Cannot use [chunkhash] or [contenthash] for chunk in '[name].[contenthash].js' (use [hash] instead)

解决方法:在webpack.dev.conf.js中重新配置output:

...
 output: {
        filename: '[name].js',
        chunkFilename: '[name].js',
    },
...

代码分割

production环境通常需要进行代码分割,而development环境则没有这样的需求。

分割javascript代码

对webpack的运行时代码进行抽离:

...
 optimization: {
        runtimeChunk: {
            "name": "manifest"
        },
...

另外,所有第三方库单独抽离:

...
splitChunks: {
            cacheGroups: {
                default: false,
                vendors: false,
                vendor: {
                    test: /[\\/]node_modules[\\/]/,
                    name: 'vendors',
                    chunks: 'all'
                }
            }
        }
...

为了支持缓存,加入HashedModuleIdsPlugin插件:

...
plugins: [
        new webpack.HashedModuleIdsPlugin()
    ]
...

分割css代码

当前,css代码被style-loader直接添加到js文件里面了,在production环境中,我们通常希望将css文件单独抽离出来,此时可以采用mini-css-extract-plugin插件。

不过,当前css的loader配置是放到webpack.base.conf.js中的,因此我们需要将其搬出到每个环境各自的配置文件中,此时webpack.base.conf.js文件如下:

const path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');

function resolve(dir) {
    return path.join(__dirname, '..', dir)
}


module.exports = {
    entry: {
        'index': './src/index.js',
    },
    output: {
        filename: '[name].[contenthash].js',
        chunkFilename: '[name].[contenthash].js',
        path: path.resolve(__dirname, 'distribution')
    },

    module: {
        rules: [
            {
                test: /\.js$/,
                loader: 'babel-loader',
                include: [resolve('src')],
                exclude: [resolve('node_modules')]
            }
        ]
    },
    plugins: [
        new CleanWebpackPlugin(['distribution']),
        new HtmlWebpackPlugin({
            template: './src/index.html',
            filename: 'index.html'
        })
    ]

};

对于production环境,我们将使用MiniCssExtractPlugin.loader替换style-loader

...
module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    {
                        loader: 'css-loader',
                        options: {
                            sourceMap: true,
                            modules: true,
                            localIdentName: '[name]---[local]---[hash:base64:5]'
                        }
                    }
                ]
            }
        ]
    }
...

另外需要配置MiniCssExtractPlugin插件:

...
 new MiniCssExtractPlugin({
            filename: "[name].[contenthash].css",
            chunkFilename: "[name].[contenthash].css"
        })
...

还需要将css文件分割到单独的css chunk中:

...
splitChunks: {
            cacheGroups: {
                default: false,
                vendors: false,
                vendor: {
                    test: /[\\/]node_modules[\\/]/,
                    name: 'vendors',
                    chunks: 'all'
                },
                styles: {
                    name: 'styles',
                    test: /\.css$/,
                    chunks: 'all',
                    enforce: true
                }
            }
        }
...

对于development环境,原封不动地将webpack.base.conf.js中的css module配置搬过来,此时的webpack.dev.conf.js文件为:

const merge = require('webpack-merge');
const webpack = require('webpack');
const baseWebpackConfig = require('./webpack.base.conf');

const webpackConfig = merge(baseWebpackConfig, {
    //environment specific config goes here
    mode: 'development',
    output: {
        filename: '[name].js',
        chunkFilename: '[name].js'
    },
    devtool: 'inline-source-map',
    devServer: {
        contentBase: './distribution',
        inline: true,//do not use iframe for the page, true is default
        open: true,//open browser after dev server starts
        port: 8080,//8080 is default
        proxy: {//proxy backend api
            '/api': 'http://localhost:3000'
        },
        hot: true
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    'style-loader',
                    {
                        loader: 'css-loader',
                        options: {
                            sourceMap: true,
                            modules: true,
                            localIdentName: '[name]---[local]---[hash:base64:5]'
                        }
                    }
                ]
            }
        ]
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin()
    ]

});

module.exports = webpackConfig;

下一篇文章中,我们将讲到使用Webpack4手动搭建VueJS2项目。


Original url: Access
Created at: 2019-05-05 16:59:06
Category: default
Tags: none

请先后发表评论
  • 最新评论
  • 总共0条评论