Webpack+React的生产阶段配置

​ 在项目的开发和生产这两个阶段,Webpack的配置是有差别的,主要体现在以下三个方面:

  1. 开发阶段有,但生产阶段没有的配置:比如eslint做js语法校验、devServer来配置webpack神奇的热模块替换(HMR, hot module replacement)功能
  2. 生产阶段有,但开发阶段没有的配置:比如UglifyJsPlugin做js代码压缩
  3. 开发和生产阶段皆有的配置,但配置方法不同:比如output.publicPath的配置、source-map来调试js代码等

webpack相关配置

移除不必要的插件

​ 拷贝一份开发阶段的配置文件webpack.dev.config.jswebpack.production.config.js中,我们基于此来进行二次的改造。首先要删去生产阶段不需要的配置:devServer, HotModuleReplacementPlugin, NoErrorsPlugin等。

配置output.publicPath

​ 说到output.publicPath的配置,首先要讲到基于Epress框架的后端代码了:通过Epress唯一的中间件express.static我们可以配置引入静态资源的根路径,比如app.use(express.static(path.join(__dirname, 'assets')));。在基于Webpack的前端代码中,如果没有给output.publicPath做相关配置,则通过HtmlWebpackPlugin插件生成的html文件中,所有的资源引用都是相对的,比如:<script type="text/javascript" src="build/main.js"></script></body>,而相对路径是无法找到相关资源的

output.publicPath的作用是可以配置URL前缀,比如当我们使用了某cdn来引入静态资源,如www.xxxcdn.com/,则配置output.publicPath: 'www.xxxcdn.com/'可以在通过HtmlWebpackPlugin生成的html文件中,将所有的资源以绝对路径的形式进行引用<script type="text/javascript" src="www.xxxcdn.com/build/main.js"></script></body>。而针对以上的Express后端引入静态资源的方式,当配置output.publicPath/时候,通过HtmlWebpackPlugin生成的html文件中,所有的资源引用都是以<script type="text/javascript" src="/build/main.js"></script></body>形式引用的。

提取公共类库/打包公共代码

​ 在基于Webpack构建的多页面应用中,每个页面配有唯一的入口文件,即使多个入口使用了相同的类库(比如jQuery),也会各自打包进自己的js文件中,这样无疑增加了额外的代码量。传统的前端项目并没有这样的问题,因为那个时候没有模块打包这样的过程,多个页面会引入公共的静态资源。Webpack的CommonsChunkPlugin可以分析出各个入口文件中引用的公共代码片段,然后将其提取出来,作为公共类库供各个入口文件使用。

new webpack.optimize.CommonsChunkPlugin({
    name: 'commons', // 这公共代码的 chunk 名为 'commons'
    filename: '[name].bundle.js', // 生成后的文件名 commons.bundle.js
    minChunks: 2 // 设定要有4个页面均加载的js模块才会被纳入公共代码
})

CommonsChunkPlugin未解决的bug

​ 当引入CommonsChunkPlugin插件后,HtmlWebpackPlugin并不会把提取出来的公共代码片段插入html模板中。目前我的做法是手动将公共资源添加到各个页面最终由HtmlWebpackPlugin生成好的html文件中,并且公共资源文件比如在入口文件之前引入,否则会报错!如下:

<body>
    <div id="content"></div>
    <script type="text/javascript" src="/commons.bundle.js"></script>
    <script type="text/javascript" src="/build/main.js"></script>
</body>

js代码压缩

uglifyjs是一个js代码的压缩工具,webpack自带了该插件:UglifyJsPlugin,只需要在配置文件中引入即可:

new UglifyJSPlugin()

有关文件缓存

​ 缓存是一个好东西,当浏览器第一次请求到静态文件之后会对其进行缓存,等下次再请求该文件时,那么浏览器会直接读取缓存文件。但浏览器读取缓存是基于文件名的,会导致即使文件内容更新了,但是浏览器依旧缓存中读取该文件。因此,缓存的最好方法是保证文件名和文件内容是一一对应的。

​ webpack可以通过如下的配置方法来决定打包文件的唯一性:

output:{
    path: path.resolve(__dirname, '../ECSQueryServices/assets'), // build directly to ECSQueryServices
    publicPath: '/',
    filename: 'build/[name].[hash].bundle.js'
},

react相关配置

​ React在开发阶段会引入一些额外的模块来做代码警告等功能,这些是我们在生产阶段不再需要的,通过配置node环境为production可以让React以生产模式运行,配置方法如下:

new webpack.DefinePlugin({
  'process.env': {
    NODE_ENV: JSON.stringify('production')
  }
}),
new webpack.optimize.UglifyJsPlugin()

其他配置

​ 当完成了上述配置后,当我们执行"build": "webpack --progress --color --watch --config ./webpack.product.config.js"命令,会报如下的错误:

Uncaught Error: locals[0] does not appear to be a `module` object with Hot Module replacement API enabled. You should disable react-transform-hmr in production by using `env` section in Babel configuration. See the example in README: https://github.com/gaearon/react-transform-hmr

​ 看提示应该为react-transform-hmr插件的问题,我们在.babel.rc中配置仅在dev模式下使用react-transform-hmr模块:

  "env": {
    "development": {
      // only enable it when process.env.NODE_ENV is 'development' or undefined
      "plugins": [["react-transform", {
        "transforms": [{
          "transform": "react-transform-hmr",
          "imports": ["react"],
          "locals": ["module"]
        }]
      }]]
    }
  }

​ 并且在package.json中强制NODE_ENV为生产模式:"build": "set NODE_ENV=production && webpack --progress --color --watch --config ./webpack.product.config.js"

待完成

​ 其实仅通过webpack.dev.config.jswebpack.production.config.js来区分开发阶段和生产阶段的配置方案是不完美的,因为两个文件中有部分的相同配置项,如果有相应的改动,则两个文件的配置都需要进行一遍更新,这样维护起来不是很方便。Webpack官网的文章Production一文讲到了Advanced Approach,相同的配置项可以统一的放在同一份webpack.common.js中:

A more complex approach would be to have a base configuration file, containing the configuration common to both environments, and then merge that with environment specific configurations. This would yield the full configuration for each environment and prevent repetition for the common bits.

React-bootstrap貌似并没有类似于Antd的模块按需加载功能,需要自己配置样式文件来引入自己项目需要的组件:

Customize Bootstrap's components, Less variables, and jQuery plugins to get your very own version.

参考

React官网文章:Optimizing Performance

Webpack官网文章:Production

webpack多页应用架构系列(七):开发环境、生产环境傻傻分不清楚?

彻底解决 webpack 打包文件体积过大

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容