为什么我们要做三份 Webpack 配置文件
时至今日,Webpack 已经成为前端工程必备的基础工具之一,不仅被广泛用于前端工程发布前的打包,还在开发中担当本地前端资源服务器(assets server)、模块热更新(hot module replacement)、API Proxy 等角色,结合 ESLint 等代码检查工具,还可以实现在对源代码的严格校验检查。
正如上文中提到的,前端从开发到部署前都离不开 Webpack 的参与,而 Webpack 的默认配置文件只有一个,即 webpack.config.js,那么问题来了,开发期和部署前应该使用同一份 Webpack 配置吗?答案肯定是否定的,既然 webpack.config.js 是一个 JS 文件,我们当然可以在文件里写 JavaScript 业务逻辑,通过读取环境变量 NODE_ENV 来判断当前是在开发(dev)时还是最终的生产环境(production),然而很多同学习惯把这两者的配置都混写在根目录下的 webpack.config.js,通过很多零散的 if…else 来“临时”决定某一个 plugin 或者某一个 loader 的配置项,随着 loaders 和 plugins 的不断增加,久而久之 webpack.config.js 变得原来越隆长,代码的可读性和可维护性也大大下降。
我想通过本文来介绍一种用 3 个 JS 文件来配置 Webpack 的方法,这里借鉴了很多开源项目的配置,同时也结合了我们自己在开发中碰到的种种问题解决方案。
本文中提及的配置基于 Webpack 2 或以上,建议使用 3.0 及以上版本
开发环境与生产环境的区别
- 开发环境 - NODE_ENV 为 development 
 启用模块热更新(hot module replacement)
 额外的 webpack-dev-server 配置项,API Proxy 配置项
 输出 Sourcemap
- 生产环境 - NODE_ENV 为 production 
 将 React、jQuery 等常用库设置为 external,直接采用 CDN 线上的版本
 样式源文件(如 css、less、scss 等)需要通过 ExtractTextPlugin 独立抽取成 css 文件
 启用 post-css
 启用 optimize-minimize(如 uglify 等)
 中大型的商业网站生产环境下,是绝对不能有 console.log() 的,所以要为 babel 配置 Remove console transform- 这里需要说明的是因为开发环境下启用了 hot module replacement,为了让样式源文件的修改也同样能被热替换,不能使用 ExtractTextPlugin,而转为随 JS Bundle 一起输出。 
你需要三份配置文件
- webpack.base.config.js
    在 base 文件里,你需要将开发环境和生产环境中通用的配置集中放在这里:
    const CleanWebpackPlugin = require('clean-webpack-plugin');
    const path = require('path');
    const webpack = require('webpack');
    // 配置常量
    // 源代码的根目录(本地物理文件路径)
    const SRC_PATH = path.resolve('./src');
    // 打包后的资源根目录(本地物理文件路径)
    const ASSETS_BUILD_PATH = path.resolve('./build');
    // 资源根目录(可以是 CDN 上的绝对路径,或相对路径)
    const ASSETS_PUBLIC_PATH = '/assets/';
    module.exports = {
    context: SRC_PATH, // 设置源代码的默认根路径
    resolve: {
        extensions: ['.js', '.jsx']  // 同时支持 js 和 jsx
    },
    entry: {
        // 注意 entry 中的路径都是相对于 SRC_PATH 的路径
        vendor: './vendor',
        a: ['./entry-a'],
        b: ['./entry-b'],
        c: ['./entry-c']
    },
    output: {
        path: ASSETS_BUILD_PATH,
        publicPath: ASSETS_PUBLIC_PATH,
        filename: './[name].js'
    },
    module: {
        rules: [
        {
            enforce: 'pre',  // ESLint 优先级高于其他 JS 相关的 loader
            test: /\.jsx?$/,
            exclude: /node_modules/,
            loader: 'eslint-loader'
        },
        {
            test: /\.jsx?$/,
            exclude: /node_modules/,
            // 建议把 babel 的运行时配置放在 .babelrc 里,从而与 eslint-loader 等共享配置
            loader: 'babel-loader'
        },
        {
            test: /\.(png|jpg|gif)$/,
            use:
            [
            {
                loader: 'url-loader',
                options:
                {
                limit: 8192,
                name: 'images/[name].[ext]'
                }
            }
            ]
        },
        {
            test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
            use:
            [
            {
                loader: 'url-loader',
                options:
                {
                limit: 8192,
                mimetype: 'application/font-woff',
                name: 'fonts/[name].[ext]'
                }
            }
            ]
        },
        {
            test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
            use:
            [
            {
                loader: 'file-loader',
                options:
                {
                limit: 8192,
                mimetype: 'application/font-woff',
                name: 'fonts/[name].[ext]'
                }
            }
            ]
        }
        ]
    },
    plugins: [
        // 每次打包前,先清空原来目录中的内容
        new CleanWebpackPlugin([ASSETS_BUILD_PATH], { verbose: false }),
        // 启用 CommonChunkPlugin
        new webpack.optimize.CommonsChunkPlugin({
        names: 'vendor',
        minChunks: Infinity
        })
    ]
    };
- webpack.dev.config.js
    这是用于开发环境的 Webpack 配置,继承自 base:
    const webpack = require('webpack');
    // 读取同一目录下的 base config
    const config = require('./webpack.base.config');
    // 添加 webpack-dev-server 相关的配置项
    config.devServer = {
    contentBase: './',
    hot: true,
    publicPath: '/assets/'
    };
    // 有关 Webpack 的 API 本地代理,另请参考 https://webpack.github.io/docs/webpack-dev-server.html#proxy
    config.module.rules.push(
    {
        test: /\.less$/,
        use: [
        'style-loader',
        'css-loader',
        'less-loader'
        ],
        exclude: /node_modules/
    }
    );
    // 真实场景中,React、jQuery 等优先走全站的 CDN,所以要放在 externals 中
    config.externals = {
    react: 'React',
    'react-dom': 'ReactDOM'
    };
    // 添加 Sourcemap 支持
    config.plugins.push(
    new webpack.SourceMapDevToolPlugin({
        filename: '[file].map',
        exclude: ['vendor.js'] // vendor 通常不需要 sourcemap
    })
    );
    // Hot module replacement
    Object.keys(config.entry).forEach((key) => {
    // 这里有一个私有的约定,如果 entry 是一个数组,则证明它需要被 hot module replace
    if (Array.isArray(config.entry[key])) {
        config.entry[key].unshift(
        'webpack-dev-server/client?http://0.0.0.0:8080',
        'webpack/hot/only-dev-server'
        );
    }
    });
    config.plugins.push(
    new webpack.HotModuleReplacementPlugin()
    );
    module.exports = config;
- webpack.config.js
    这是用于生产环境的 webpack 配置,同样继承自 base:
    const webpack = require('webpack');
    const ExtractTextPlugin = require('extract-text-webpack-plugin');
    // 读取同一目录下的 base config
    const config = require('./webpack.base.config');
    config.module.rules.push(
    {
        test: /\.less$/,
        use: ExtractTextPlugin.extract(
        {
            use: [
            'css-loader',
            'less-loader'
            ],
            fallback: 'style-loader'
        }
        ),
        exclude: /node_modules/
    }
    );
    config.plugins.push(
    // 官方文档推荐使用下面的插件确保 NODE_ENV
    new webpack.DefinePlugin({
        'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production')
    }),
    // 启动 minify
    new webpack.LoaderOptionsPlugin({ minimize: true }),
    // 抽取 CSS 文件
    new ExtractTextPlugin({
        filename: '[name].css',
        allChunks: true,
        ignoreOrder: true
    })
    );
    module.exports = config;
现在在你的工程文件夹里应该已经有三个 Webpack 配置文件,它们分别是:
webpack.base.config.js
webpack.dev.config.js
webpack.config.js
最后,你还需要在 package.json 里添加相应的配置:
    {
    ...
    "scripts": {
        "build": "webpack --optimize-minimize",
        "dev": "webpack-dev-server --config webpack.dev.config.js",
        "start": "npm run dev" // 或添加你自己的 start 逻辑
    },
    ...
    }
和很多项目一样,在开发环境下的时候,你需要使用 npm run dev 来启动,而在生产环境中,则用 npm run build 来发布。
为什么我们要做三份 Webpack 配置文件的更多相关文章
- webpack 配置文件相关解说
		博客地址:https://ainyi.com/10 webpack - 什么是webpack: WebPack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它 ... 
- webpack实践(二)- webpack配置文件
		webpack系列博客中代码均在github上:https://github.com/JEmbrace/webpack-practice <webpack实践(一)- 先入个门> < ... 
- webpack基础+webpack配置文件常用配置项介绍+webpack-dev-server
		一.webpack基础 1.在项目中生成package.json:在项目根目录中输入npm init,根据提示输入相应信息.(也可以不生成package.json文件,但是package.json是很 ... 
- 调试webpack配置文件
		webpack运行在nodejs上,调试webpack就相当于调试nodejs程序.下面介绍一种通用的办法. 1.配置package.json,加一个debug. { 'scripts': { 'de ... 
- react暴露webpack配置文件
		在react中安装create-react-app脚手架新建项目,但是新建的项目中没有配置文件. webpack的配置文件webpack.base.conf.js隐藏在了node_modules文件夹 ... 
- 3.WebPack配置文件
		一.为什么需要WebPack配置文件 引用自官方: 在 webpack 4 中,可以无须任何配置使用,然而大多数项目会需要很复杂的设置,这就是为什么 webpack 仍然要支持 配置文件.这比在终端( ... 
- 3.使用webpack配置文件webpack.confg.js配置打包文件的入口和出口
		在项目根目录下新建webpack.config.js文件 webpack.config.js文件配置如下: // Node的路径操作使用的是path模块 const path=require('pat ... 
- webpack 配置文件
		现如今,webpack非常的受欢迎,比较火的几款js框架都推荐使用webpack来构建项目,而webpack也确实非常强大,但是配置webpack缺常常带来很多问题,接下来就写一下我自己遇到的一些坑. ... 
- nginux做反向代理配置文件
		做反向代理的配置文件最好单独创建一个文件,然后在主配置文件中使用 include nginx-test.config; 这样的方式来导入. 配置代码如下: ## Basic reverse prox ... 
随机推荐
- UOJ#54 BZOJ3434 [WC2014]时空穿梭
			题目描述 小 X 驾驶着他的飞船准备穿梭过一个 \(n\) 维空间,这个空间里每个点的坐标可以用 \(n\) 个实数表示,即 \((x_1,x_2,\dots,x_n)\). 为了穿过这个空间,小 X ... 
- drupal7 获取当前使用的主题的名称
			直接引用全局变量就行: 参考: 代码测试: global $theme, $theme_key; echo $theme; echo '<br>'; echo $theme_key; 结果 ... 
- SVN的正确提交方式
			版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/Rundy_Deng/article/details/80338093 也会让我们百思不得其解,甚至耽 ... 
- c#  操作临时数据---XML操作
			class Config { static string path; /// <summary> /// 配置文件的路径 /// </summary> public stati ... 
- leetcode Ch5-Linked List
			一. 1. Remove Duplicates from Sorted List II class Solution { public: ListNode* deleteDuplicates(List ... 
- MySQL 8.0复制性能的提升(翻译)
			What’s New With MySQL Replication in MySQL 8.0 MySQL复制从问世到现在已经经历了多个年头,它的稳定性和可靠性也在稳步的提高.这是一个不停进化的过程,由 ... 
- Java学习---Java面试基础考核·
			Java中sleep和wait的区别 ① 这两个方法来自不同的类分别是,sleep来自Thread类,和wait来自Object类. sleep是Thread的静态类方法,谁调用的谁去睡觉,即使在a线 ... 
- Linux 下Shell的学习
			1.Shell学习 1.什么是Shell shell是一个命令解析器,在操作系统的最外层,负责和用户对话,将用户的输入解释给操作系统,并处理各种各样的操作系统的输出结果.2.什么是shell脚本 ... 
- redis下的持久化保存
			rdb.h rdb.c ---> 完成数据保存到临时文件,再利用rename保存到指定文件的过程: 如果需要写一个数据持久化保存的功能时,可以参考这部分代码: //rdb API int ... 
- Java基础知识强化之集合框架笔记77:ConcurrentHashMap之 ConcurrentHashMap的基本操作
			1. ConcurrentHashMap的初始化: 下面我们来结合源代码来具体分析一下ConcurrentHashMap的实现,先看下初始化方法: public ConcurrentHashMap(i ... 
