为什么我们要做三份 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 相关文章推荐
小议Function.apply()之二------利用Apply的参数数组化来提高 JavaScript程序性能
Nov 30 Javascript
javascript demo 基本技巧
Dec 18 Javascript
javascript之querySelector和querySelectorAll使用说明
Oct 09 Javascript
手机平板等移动端适配跳转URL的js代码
Jan 25 Javascript
JS 弹出层 定位至屏幕居中示例
May 21 Javascript
包含中国城市的javascript对象实例
Aug 03 Javascript
javascript中闭包(Closure)详解
Jan 06 Javascript
基于jQuery实现以手风琴方式展开和折叠导航菜单
Jan 28 Javascript
javascript轮播图算法
Oct 21 Javascript
JS图片懒加载的优点及实现原理
Jan 10 Javascript
JavaScript对象属性操作实例解析
Feb 04 Javascript
vue3.0中友好使用antdv示例详解
Jan 05 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堆栈与列队的学习
2013/06/21 PHP
PHP设置头信息及取得返回头信息的方法
2016/01/25 PHP
增强的 JavaScript 的 trim 函数的代码
2007/08/13 Javascript
效率高的Javscript字符串替换函数的benchmark
2008/08/02 Javascript
javascript 必知必会之closure
2009/09/21 Javascript
在JavaScript中获取请求的URL参数
2010/12/22 Javascript
jquery实现漂浮在网页右侧的qq在线客服插件示例
2013/05/13 Javascript
Bootstrap中的Dropdown下拉菜单更改为悬停(hover)触发
2016/08/31 Javascript
JQuery手速测试小游戏实现思路详解
2016/09/20 Javascript
基于bootstrap写的一点localStorage本地储存
2017/11/21 Javascript
使用Jenkins部署React项目的方法步骤
2019/03/11 Javascript
详解JavaScript作用域 闭包
2020/07/29 Javascript
前端性能优化建议
2020/09/17 Javascript
[49:20]VG vs TNC Supermajor小组赛B组败者组决赛 BO3 第二场 6.2
2018/06/03 DOTA
[53:36]Liquid vs VP Supermajor决赛 BO 第三场 6.10
2018/07/05 DOTA
python通过urllib2爬网页上种子下载示例
2014/02/24 Python
Python 基础教程之闭包的使用方法
2017/09/29 Python
对python中的logger模块全面讲解
2018/04/28 Python
python交易记录链的实现过程详解
2019/07/03 Python
PyCharm2019安装教程及其使用(图文教程)
2019/09/29 Python
手把手教你进行Python虚拟环境配置教程
2020/02/03 Python
python+Selenium自动化测试——输入,点击操作
2020/03/06 Python
使用卷积神经网络(CNN)做人脸识别的示例代码
2020/03/27 Python
解决python 执行sql语句时所传参数含有单引号的问题
2020/06/06 Python
新手常见Python错误及异常解决处理方案
2020/06/18 Python
Python 利用flask搭建一个共享服务器的步骤
2020/12/05 Python
详解CSS3阴影 box-shadow的使用和技巧总结
2016/12/03 HTML / CSS
PUMA官方商城:世界领先的运动品牌之一
2016/11/16 全球购物
高中生自我鉴定范文
2013/10/30 职场文书
如何打造一封优秀的留学推荐信
2014/01/25 职场文书
《邮票齿孔的故事》教学反思
2014/02/22 职场文书
机械工程及其自动化专业求职信
2014/08/08 职场文书
2014年大学生职业规划书:未来不是梦,只要勇敢冲!
2014/09/22 职场文书
2015年元旦晚会活动总结(学生会)
2014/11/28 职场文书
医院合作意向书范本
2015/05/08 职场文书
服务器间如何实现文件共享
2022/05/20 Servers