带你真正用起来React lazy

React v16.6 版本引入了React.lazy,Suspense的功能,这个功能主要是利用了webpack对es6的import动态载入组件,能够自动实现Code Splittingcss

Code Splitting就是把代码分红不少个小块(chunk),须要某部分代码的时候再去加载,减少了页面首屏进来加载很大一个js文件的压力,另外拆分红小块还能更好的利用浏览器缓存,下次再用到的话直接从浏览器缓存中读取。node

简单使用

Beforereact

import OtherComponent from './OtherComponent';

function MyComponent() {
  return (
    <div>
      <OtherComponent />
    </div>
  );
}
复制代码

Afterwebpack

const OtherComponent = React.lazy(() => import('./OtherComponent'));

function MyComponent() {
  return (
    <div>
      <OtherComponent />
    </div>
  );
}
复制代码

React.lazy接收一个函数做为参数,该函数必须经过调用import来返回一个Promise来加载组件,返回的Promise对象的resolve方法接收组件默认导出的模块es6

若是只是这么使用运行会出现错误,根据提示须要引入Suspense组件 web

WechatIMG592.png

Suspense相似于一个错误捕获器,容许定义一个fallback指示符,fallback用来定义咱们在等待加载时显示的一些内容。能够将Suspense组件放在动态加载组件上方的任何位置,若是动态组件未加载,则从该组件开始向上寻找,直到找到Suspense组件。可使用单个Suspense组件包裹多个动态加载组件浏览器

完整示例缓存

const OtherComponent = React.lazy(() => import(/* webpackChunkName:"OtherComponent" */'./OtherComponent'));

function MyComponent() {
  return (
    <div>
      <Suspense fallback={<Loading/>}>
      <p>下面是一个动态加载的组件</p>
      <OtherComponent />
      </Suspense>
    </div>
  );
}

复制代码

真正项目中应用会遇到的问题总结

改造以前的文件大小

WechatIMG596.png

WechatIMG595.png

由于个人项目是个多页面应用,我接下来会以desktop.js这个入口来演示,其中vendor.dll.js是一个第三方库分离的文件。bash

能够看到,当前这个js文件大小Gzip压缩以后为603.2kb,没有其余任何异步加载的chunkbabel

使用react lazy异步加载改造

不要忘记修改webpack配置

output: {
    chunkFilename: "static/js/[name].[chunkhash:8].chunk.js"
  },
复制代码

若是使用了babel转译,则须要安装babel-plugin-syntax-dynamic-import插件来解析动态import语法,修改babel配置

{
     "plugins": ["syntax-dynamic-import"]
}
复制代码

看一下打包后的文件大小

WechatIMG597.png

能够看到,desktop.js文件已经缩小到了459.69kb,比以前缩小了150kb(由于我只作了部分组件的异步加载,因此文件大小变化不是太大)。下图的Subtile.chunk是我点击按钮以后进行加载的组件

WechatIMG599.png

提取异步chunk中的公共资源

WechatIMG600.png

为了对比清晰,我这张图只包含了MultiLayoutDialog.chunk(66.84kb)和MultiLayoutPollingDialog.chunk(47.55kb)两个模块

状况不太乐观,两个组件的node_modules中都分别包含antd/lib,rc-tree,rc-animate,这是由于每一个chunk都是一个独立的可运行的模块,所以会加载本身的依赖,可是显然这是不合理的,应该将这些公共的依赖抽离出来,这个时候你须要用到CommonsChunkPlugin,配置async,用于从异步chunk中抽离公共的资源为一个单独的文件,当首个异步chunk被加载时会同步加载该公共资源文件

从新配置webpack

new webpack.optimize.CommonsChunkPlugin({
      async: "common-in-lazy",
      minChunks: ({ resource }) =>
        resource && resource.indexOf("node_modules") >= 0 && resource.match(/\.js$/),
      children: true,
    })
    // 在全部的async chunk中查找被其中至少2个chunk所共享的node_modules资源,将它们挪到common-in-lazy文件中,没有则新建。
复制代码

再来看提取了公共资源以后的打包文件大小

WechatIMG606.png

MultiLayoutDialog.chunk(19.85kb)和MultiLayoutPollingDialog.chunk(26.21kb)两个chunk分别减小了46kb和20kb,(另外还有一些其余的chunk体积也减小了,在此没有截图),公共资源抽离为了common-in-lazy-desktop.chunk,该chunk中包含了以前重复加载的antd/lib,rc-tree,rc-animate等资源,改文件大小为50.09kb

拯救丢失的css

不要觉得到此异步加载就大功告成了,若是你敢build以后部署,你会收到qa小姐姐亲切问候的,由于有些异步加载的组件样式丢失啦,致使页面显示出现问题。正在喝茶的你吓得立马坐正一波谷歌以后,决定将异步chunk的css样式与入口的css合并到一个css文件,只作js的异步chunk加载,修改webpack配置

new ExtractTextPlugin({
      filename: cssFilename,
      allChunks: true // 默认为false,仅从初始chunk中提取
    }),
复制代码

至此,整个异步加载已经所有完成,继续喝茶,总结一下:

1. react的lazy方法须要配合Suspense组件来使用,能够定义异步chunk加载未完成以前的UI显示  

2.webpack配置的出口要添加chunkFilename: "static/js/[name].[chunkhash:8].chunk.js"  

3.若是使用了babel,为避免babel将异步import转译,则须要安装额外的插件来识别动态import  

4.打包的异步chunk会各自包含本身的依赖,这些依赖会存在重复加载,可使用commonsChunksPlugin将异步chunk中的公共资源提取为一个独立的异步chunk  

5.若是异步chunk的部分样式有丢失,将extractTextPlugin中chunks配置为all,则将全部chunk中的css样式抽离为一个文件,(可是这样作的话,css不会实现异步加载了,感受不太好,若是你们有好的建议欢迎提出)
复制代码

以上就是我要分享的内容,若是存在错误的理解或者更好的理解方法,欢迎提出指正。

相关文章
相关标签/搜索