/ webpack

Caiyun 的实践之 —— webpack 打包优化

caiyun 是基于 react react-router redux开发的网站, 其中使用了 webpack 作为打包工具。如果你不了解 webpack 是什么,可以先去搜索了解一下,现在网上关于他的文档教程已经很多了。本文主要记录 caiyun 在对首屏渲染的优化上,对 webpack 做的相关配置优化。

问题概述

webpack 可以通过各类的 loader 将源文件做相关处理,比如通过babel-loader 可以解析 jsx 文件,将 es6 转化成 es5。可以通过 url-loader 可以将图片文件转化成 base64。当然 可以通过 limit 来指定条件。
比如

url-loader?prefix=img/&limit=1000

配置的意思是将小于 1000b 的图片转化成 base64 ,超过这个限制的图片还是以图片的形式加载。图片资源用 base64 替代,可以减少请求图片的 http 请求。

caiyun 的详情页面有大量的图片,我一开始是为了减少 http 的请求消耗,将这个 limit 的值设置的很大,几乎将所有成员的照片都用了
base64, 这样做的一个坏处就是 webpack 打包之后的文件很大。再由于这是时候没有做按需加载,也就是说,用户首次请求 caiyun 首页的时候,返回的打包文件是包括整站其他页面的。这就造成了首屏渲染的长时间阻塞。

如下是小伙伴的回馈。

网速好一点的 7s,10s。

网速差得简直可以去冲一杯咖啡了 :(

具体看一下,首屏的 js 文件居然有 800 多 kb。而这 800 kb 的文件首屏能用到的少之又少。浏览器阻塞渲染的时间上都造成大量的浪费。

加上站点是建在 github pages 上,这个体谅的加载,网络 io 的时间也是一段不少的时间。

按需加载

分析出上面的问题,最简单的思路就是,用户渲染的页面需要什么,才去加载什么。也就是接下来要提到的按需加载。在传统的项目中,图片的懒加载,滚动加载都可以看作是按需加载,即当前渲染的页面需要什么数据,采取加载什么数据。但在 webpack 打包的文件里,怎么把当前页面所需要的文件和当前页面不需要的文件拆分开来呢?也就是代码分离。

webpack 官方文档就有相应的说明

代码分割暂不支持 ES6 模块化。Commonjs 风格的模块化 webpack 提供了一个加载函数。

require.ensure(dependencies, callback)

如下的伪代码实现

if (window.location.pathname === '/home') {
  showLoadingState();
  require.ensure([], function() { // []里是 home 模块依赖的模块,同步加载。
    hideLoadingState();
    require('./home').show(); // 加载到 home 模块并展示。
  });
} else if (window.location.pathname === '/team') {
  showLoadingState();
  require.ensure([], function() {
    hideLoadingState();
    require('./team').show();
  });
}

相应的 webpack 的配置

output: {
    path: path.join(__dirname, 'dist'),
    chunkFilename: '[id].chunk.js',
    // ...
},

这样,相应的模块就会变成分离成 1.chunk.js2.chunk.js 然后在请求到时候加载。

caiyun 路由管理使用了 react-router ,如何配合 react-router 实现按需加载可以参考其文档

如下图,是 caiyun 生成的按需加载的模块

外部加载

webpack --display-modules --sort-modules-by size

webpack 添加如上的命令可以查看,在打包过程中,可以将模块的大小做一个排序,这样就很容易找出,哪些模块比较大。如下图:

事实上,由于项目使用了 react 等框架,然而这些框架没有必要和源代码一起打包在一起,可以使用 cdn 外部链接加载。一是速度快,二是可缓存。

// webpack.config.js
  externals: {
    'react': 'React',
    'react-dom': 'ReactDOM',
    'react-router': 'ReactRouter',
    'react-redux': 'ReactRedux'
  },

然后在 html 文件中,添加 cdn 引用

  <script src="//cdn.bootcss.com/react/15.1.0/react.min.js"></script>
  <script src="//cdn.bootcss.com/react/15.1.0/react-dom.min.js"></script>
  <script src="//cdn.bootcss.com/react-router/2.4.1/ReactRouter.min.js"></script>
  <script src="//cdn.bootcss.com/react-redux/4.4.5/react-redux.min.js"></script>

具体可以参考文档

压缩图片

在访问 caiyun 的 team 页面时有大量的图片需要加载,最简单的做法就是把图片的 size 减小(谢谢丽雅学姐)。

但即便如此,由于之前都将图片转成了 base64,所以按需加载的 team 模块也还是比较大,所以将 webpack 打包图片成 base64 的 limit 值缩小(具体多少看吧)。这样就能明显缩小 team 模块的大小。

因为图片加载本身做了滚动加载,所以同时去请求 src 的 http 不会很多,图片文件本身也成为了一个模块,做到了按需加载。

以上。

参考链接

webpack 按需打包加载

webpack打包bundle.js体积大小优化