React + Webpack 构建打包优化

时间: 2018-04-24阅读: 1563标签: React

react 相关的优化

  • 使用 babel-react-optimize 对 react 代码进行优化
  • 检查没有使用的库,去除 import 引用
  • 按需打包所用的类库,比如 lodash 、echarts 等

lodash 可以采用 babel-plugin-lodash 进行优化。


需要注意的是:在 babel-react-optimize 中使用了 babel-plugin-transform-react-remove-prop-types 这个插件。正常情况下,如果你在代码中没有引用到组件的 PropTypes,则完全没问题。如果你的组件用到了,那么使用该插件可能会导致问题。

具体见:https://github.com/oliviertassinari/babel-plugin-transform-react-remove-prop-types#is-it-safe


webpack 构建打包优化

webpack 构建打包存在的问题主要集中于下面两个方面:

  • Webpack 构建速度慢
  • Webpack 打包后的文件体积过大

Webpack 构建速度慢

可以使用 Webpack.DDLPlugin,HappyPack 来提高构建速度。具体参见小铭在 DMP DDLPlugin 的文档。原文如下:

Webpack.DLLPlugin

  • 添加一个 webpack.dll.config.js

主要是用到一个 DllPlugin 插件,把一些第三方的资源独立打包,同时放到一个 manifest.json 配置文件中,
这样在组件中更新后,就不会重新 build 这些第三方的资源,

  • 同时独立配置 dll/vendors.js 文件,提供给 webpack.dll.config.js
  • 修改 package.json

在 scripts 中添加: 

"dll": "webpack --config webpack.dll.config.js --progress --colors ",


执行 npm run dll 以后,会在 dll 目录下生产 两个文件 vendor-manifest.json ,vendor.dll.js,配置 webpack.dev.config.js 文件,加入一个 DllReferencePlugin 插件,并指定 vendor-manifest.json 文件

new webpack.DllReferencePlugin({
  context: join(__dirname, 'src'),
  manifest: require('./dll/vendor-manifest.json')
})


修改 html

<% if(htmlWebpackPlugin.options.NODE_ENV ==='development'){   %>
   <script src="dll/vendor.dll.js"></script>
<% } %>

注意,需要在 htmlWebpackPlugin 插件中配置 NODE_ENV 参数


Happypack

通过多线程,缓存等方式提升 rebuild 效率 https://github.com/amireh/happypack

  • 在 webpack.dev.config.js 中针对不同的资源创建多个 HappyPack, 比如 js 1 个,less 1 个,并设置好 id
new HappyPack({
  id: 'js',
  threadPool: happyThreadPool,
  cache: true,
  verbose: true,
  loaders: ['babel-loader?babelrc&cacheDirectory=true'],
}),
new HappyPack({
  id: 'less',
  threadPool: happyThreadPool,
  cache: true,
  verbose: true,
  loaders: ['css-loader', 'less-loader'],
})


  • 在 module.rules 中配置 use 为 happypack/loader, 设置 id
{
  test: /\.js$/,
  use: [
    'happypack/loader?id=js'
  ],
  exclude: /node_modules/
}, {
  test: /\.less$/,
  loader: extractLess.extract({
    use: ['happypack/loader?id=less'],
    fallback: 'style-loader'
  })
}


减少 Webpack 打包后的文件体积大小

首先需要对我们整个 bundle 进行分析,由哪些东西组成及各组成部分所占大小。
这里推荐 webpack-bundle-analyzer。安装后在 webpack.dev.config.js 中添加插件即可,就能在每次启动后自动在网站打开分析结果,如下图

plugins.push( new BundleAnalyzerPlugin());


react bundle

除此之外,还可以将打包过程输出成json文件

webpack --profile --json -> stats.json


然后到下面这两个网站进行分析

通过上面的图表分析可以清楚得看到,整个 bundle.js 的组成部分及对应的大小。
解决 bundle.js 体积过大的解决思路如下:

  • 生产环境启用压缩等插件,去除不必要插件
  • 拆分业务代码与第三方库及公共模块
  • webpack 开启 gzip 压缩
  • 按需加载


生产环境启用压缩等插件,去除不必要插件

确保在生产环境启动 webpack.DefinePlugin 和 webpack.optimize.UglifyJsPlugin。

const plugins = [
  new webpack.DefinePlugin({
    'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production')
  }),
    new webpack.optimize.UglifyJsPlugin({
    compress: {
      warnings: false,
      drop_console: false //eslint-disable-line
    }
    })
]


拆分业务代码与第三方库及公共模块

由于项目的业务代码变更频率很高,而第三方库的代码变化则相对没有那么频率。如果将业务代码和第三库打包到同一个 chunk 的话,在每次构建的时候,哪怕业务代码只改了一行,即使第三方库的代码没有发生变化,会导致整个 chunk 的 hash 跟上一次不同。这不是我们想要的结果。我们想要的是,如果第三方库的代码没有变化,那在构建的时候也要保证对应的 hash 没有发生变化,从而能利用浏览器缓存,更好的提高页面加载性能和缩短页面加载时间。
因此可以将第三库的代码单独拆分成 vendor chunk,与业务代码分离。这样就算业务代码再怎么发生变化,只要第三方库代码没有发生变化,对应的 hash 就不变。
首先 entry 配置两个 app 和 vendor 两个chunk

entry: {
  vendor: [path.join(__dirname, 'dll', 'vendors.js')],
  app: [path.join(__dirname, 'src/index')]
},
output: {
  path: path.resolve(__dirname, 'build'),
  filename: '[name].[chunkhash:8].js'
},


其中 vendros.js 是自己定义的哪些第三方库需要纳入 vendor 中,如下:

require('babel-polyfill');
require('classnames');
require('intl');
require('isomorphic-fetch');
require('react');
require('react-dom');
require('immutable');
require('redux');


然后通过 CommonsChunkPlugin 拆分第三库

plugins.push(
  // 拆分第三方库
  new webpack.optimize.CommonsChunkPlugin({ name: 'vendor' }),
  // 拆分 webpack 自身代码
  new webpack.optimize.CommonsChunkPlugin({
    name: 'runtime',
    minChunks: Infinity
  })
);


上面的配置有两个细节需要注意

  • 使用 chunkhash 而不用 hash
  • 单独拆分 webpack 自身代码

使用 chunkhash 而不用 hash

先来看看这二者有何区别:

  • hash 是 build-specific,任何一个文件的改动都会导致编译的结果不同,适用于开发阶段
  • chunkhash 是 chunk-specific,是根据每个 chunk 的内容计算出的 hash,适用于生产

因此为了保证第三方库不变的情况下,对应的 vendor.js 的 hash 也要保持不变,我们再 output.filename 中采用了 chunkhash


单独拆分 webpack 自身代码

Webpack 有个已知问题:

webpack 自身的 boilerplate 和 manifest 代码可能在每次编译时都会变化。

这导致我们只是在 入口文件 改了一行代码,但编译出的 vendor 和 entry chunk 都变了,因为它们自身都包含这部分代码。

这是不合理的,因为实际上我们的第三方库的代码没变,vendor 不应该在我们业务代码变化时发生变化。
因此我们需要将 webpack 这部分代码分离抽离

new webpack.optimize.CommonsChunkPlugin({
      name: "runtime",
      minChunks: Infinity
}),


其中的 name 只要不在 entry 即可,通常使用 "runtime" 或 "manifest"。
另外一个参数 minChunks 表示:在传入公共chunk(commons chunk) 之前所需要包含的最少数量的 chunks。数量必须大于等于2,或者少于等于 chunks的数量,传入 Infinity 会马上生成 公共chunk,但里面没有模块。

更多关于 CommonChunkPlugin 可以查看 官方文档


拆分公共资源

同 上面的拆分第三方库一样,拆分公共资源可以将公用的模块单独打出一个 chunk,你可以设置 minChunk 来选择是共用多少次模块才将它们抽离。配置如下:

new webpack.optimize.CommonsChunkPlugin({
  name: 'common',
  minChunks: 2,
}),


是否需要进行这一步优化可以自行根据项目的业务复用度来判断。

开启 gzip

使用 CompressionPlugin 插件开启 gzip 即可:

// 添加 gzip
new CompressionPlugin({
  asset: '[path].gz[query]',
  algorithm: 'gzip',
  test: /\.(js|html)$/,
  threshold: 10240,
  minRatio: 0.8
})


按需加载

本篇不做具体介绍


参考资料


本文作者:超人  
来源:HYPERS 前端团队博客  


站长推荐

1.云服务推荐: 国内主流云服务商,各类云产品的最新活动,优惠券领取。地址:阿里云腾讯云华为云

2.广告联盟: 整理了目前主流的广告联盟平台,如果你有流量,可以作为参考选择适合你的平台点击进入

链接: http://www.fly63.com/article/detial/698

关闭

渐进式React

可以说 React 为Web开发者带来了全新的开发模式,而在各类新功能下,如何达到性能最优仍是我们需要关心的。今天做一次精读尝试,原文地址在文末,话不多说,先呈上一份性能清单:

React高级性能优化

当大家考虑在项目中使用 React 的时候,第一个问题往往是他们的应用的速度和响应是否能和非 React 版一样,每当状态改变的时候就重新渲染组件的整个子树,让大家怀疑这会不会对性能造成负面影响

grpc-web与react的集成

使用create-react-app脚手架生成react相关部分,脚手架内部会通过node自动起一个客户端,然后和普通的ajax请求一样,和远端服务器进行通信,只不过这里采用支持rpc通信的grpc-web来发起请求,远端采用docker容器的node服务器,node服务器端使用envoy作为代理

react生命周期详解_深入理解React生命周期

React主要思想是通过构建可复用组件来构建用户界面,每个组件都有自己的生命周期,它规定了组件的状态和方法需要在哪个阶段改变和执行。所谓组件就是有限状态机,,表示有限个状态以及在这些状态之间的转移和动作行为的模型。

React 新 Context API

React 新 Context API它更符合工程化, 不再是实验性的,现在它是一流的API! 并且它还使用了 RENDER PROP!你在react官网上听说过 context API?那么你为何要使用context?Context的重生

用思维模型去理解 React

我了解到,掌握了某种语言、框架或工具的人与没有掌握的人之间的最大区别在于他们所使用的思维模型(Mental Model)。前者拥有清晰而先进的思维模型,而后者则没有。

react 源码之setState

与multirepo 相对。 monorepo是单代码仓库, 是把所有相关项目都集中在一个代码仓库中,每个module独立发布,每个module都有自己的依赖项(package.json),能够作为独立的npm package发布,只是源码放在一起维护。

react router中页面传值的三种方法

这篇文章主要介绍React Router定义路由之后如何传值,有关React和React Router 。react router中页面传值的三种方法:props.params、query、state

React 服务端渲染完美的解决方案

使用 React 构建客户端应用程序,默认情况下,可以在浏览器中输出 React 组件,进行生成 DOM 和操作 DOM。React 也可以在服务端通过 Node.js 转换成 HTML,直接在浏览器端“呈现”处理好的 HTML 字符串,这个过程可以被认为 “同构”

Gatsby.js_一款基于React.js静态站点生成工具

Gatsby能快速的使用 React 生态系统来生成静态网站,可以结合React Component、Markdown 和服务端渲染来完成静态网站生成让他更强大。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!