为什么我们要做三份 Webpack 配置文件


Posted in Javascript onSeptember 18, 2017

在知乎上我们常常会看到有同学发问:BAT 等大型网站的前端工程是如何组织管理的?这的确是一个可以发散的很广的 Q&A,我想如果要我回答这个问题,不如先从 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 一起输出。

你需要三份配置文件

1. 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
  })
 ]
};

2. webpack.dev.config.js

这是用于开发环境的 Webpack 配置,继承自 base:

const webpack = require('webpack');

// 读取同一目录下的 base config
const config = require('./webpack.base.config');

// 添加 webpack-dev-server 相关的配置项
config.devServer = {
 contentBase: './',
 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;

3. 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-dev-server,而采用 express + webpack/webpack-dev-middleware,配置方法与上面所述的完全相同。

关于专栏

如果你喜欢这篇文章,就请关注我的专栏《前端零栈》,在这里我们一起聊一聊前端技术和前端工程。

关于作者

Henry,10 岁开始学习计算机编程,高二暑假获得江苏省青少年信息奥林匹克一等奖。2000 年开始自学 JavaScript 及网页制作,2006 年起正式开始从事前端开发工作,从此一干就是 10 多年。加入阿里巴巴前,曾在 SAP 中国研究院担任智慧交通大数据产品经理。

Github:MagicCube (Henry Li)

 小结

前端从开发到部署前都离不开 Webpack 的参与,本文结合了我们自己在开发中碰到的种种问题解决方案,同时借鉴了很多开源项目的配置来介绍一种用 3 个 JS 文件来配置 Webpack 的方法。关于本文如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
js实现简单模态窗口,背景灰显
Nov 14 Javascript
dess中一个简单的多路委托的实现
Jul 20 Javascript
js图片预加载示例
Apr 30 Javascript
JS 实现导航菜单中的二级下拉菜单的几种方式
Oct 31 Javascript
vue中添加mp3音频文件的方法
Mar 02 Javascript
详解基于mpvue的小程序markdown适配解决方案
May 08 Javascript
Puppet的一些技巧
Sep 17 Javascript
详解VUE单页应用骨架屏方案
Jan 17 Javascript
JS数组进阶示例【数组的几种函数用法】
Jan 16 Javascript
详解vue中v-bind:style效果的自定义指令
Jan 21 Javascript
vue打包静态资源后显示空白及static文件路径报错的解决
Sep 02 Javascript
vue图片裁剪插件vue-cropper使用方法详解
Dec 16 Vue.js
分析javascript中9 个常见错误阻碍你进步
Sep 18 #Javascript
十个免费的web前端开发工具详细整理
Sep 18 #Javascript
Redux 和 Mobx的选择问题:让你不再困惑!
Sep 18 #Javascript
HTML5开发Kinect体感游戏的实例应用
Sep 18 #Javascript
聊聊那些使用前端Javascript实现的机器学习类库
Sep 18 #Javascript
Web技术实现移动监测的介绍
Sep 18 #Javascript
探索webpack模块及webpack3新特性
Sep 18 #Javascript
You might like
php中使用Curl、socket、file_get_contents三种方法POST提交数据
2011/08/12 PHP
超小PHP小马小结(方便查找后门的朋友)
2012/05/05 PHP
一个显示效果非常不错的PHP错误、异常处理类
2014/03/21 PHP
浅谈PHP接收POST数据方式
2015/06/05 PHP
PHP云打印类完整示例
2016/10/15 PHP
php arsort 数组降序排序详细介绍
2016/11/17 PHP
ThinkPHP框架中使用Memcached缓存数据的方法
2018/03/31 PHP
一个JavaScript的求爱小特效
2014/05/09 Javascript
Javascript的严格模式strict mode详细介绍
2014/06/06 Javascript
js实现仿Windows风格选项卡和按钮效果实例
2015/05/13 Javascript
jquery 将当前时间转换成yyyymmdd格式的实现方法
2016/06/01 Javascript
javascript简单实现深浅拷贝过程详解
2019/10/08 Javascript
JS中的const命令你真懂它吗
2020/03/08 Javascript
Vue实现计算器计算效果
2020/08/17 Javascript
vue实现简单加法计算器
2020/10/22 Javascript
详解template标签用法(含vue中的用法总结)
2021/01/12 Vue.js
[45:50]完美世界DOTA2联赛PWL S3 CPG vs Forest 第二场 12.16
2020/12/17 DOTA
wxPython中文教程入门实例
2014/06/09 Python
Python中使用md5sum检查目录中相同文件代码分享
2015/02/02 Python
python爬取51job中hr的邮箱
2016/05/14 Python
Python处理文本文件中控制字符的方法
2017/02/07 Python
Python简单计算数组元素平均值的方法示例
2017/12/26 Python
python3 打印输出字典中特定的某个key的方法示例
2019/07/06 Python
tensorflow 实现自定义梯度反向传播代码
2020/02/10 Python
利用PyQt中的QThread类实现多线程
2020/02/18 Python
Python tcp传输代码实例解析
2020/03/18 Python
购买澳大利亚最好的服装和内衣在线:BONDS
2016/10/14 全球购物
学院领导推荐信
2013/10/30 职场文书
求职信写作要突出重点
2014/01/01 职场文书
大二学习计划书范文
2014/04/27 职场文书
产品设计开发计划书
2014/05/07 职场文书
产品陈列协议书(标准版)
2014/09/17 职场文书
党的群众路线剖析材料
2014/10/09 职场文书
2015年远程教育工作总结
2015/05/20 职场文书
小学同学聚会感言
2015/07/30 职场文书
大学生心理健康教育心得体会
2016/01/12 职场文书