React 异步组件之 Code Splite
在上一次开发一个大型的SPA网站的时候,因为加载了大量的第三方库,导致 webpack 打包出来的 bundle.js 异常的大,是时候祭出了 code splite 来减少 js 的大小了。所谓的 Code splite 其实就是所谓js的懒加载。因为我们的技术栈使用 react + redux + redux-thunk 配合 create-react-app 来搭建的,所以我们就说说 Create React App 如何来做代码分割。
首先需要申明的是代码分割不是一颗银弹,不要过度优化你的程序除非你的程序到了需要优化的程度,或者想我这样就是想瞎折腾学习学习。如果在开发React过程中发现打包出来的bundle.js越来越大,多半是和我们一样引入了很多组件导致程序在第一次加载的过慢。
思考以下如下的场景:在用户使用一个比较大的程序,当用户只是在登录界面,没有必要将整个程序都加载出来。Create React App起始已经内置了代码分割功能来动态加载 import。Import来动态加载组件通常配合React Router。我们通过设置React Router来根据Url路径来做到按需加载组件。
JS 的模块化规范
代码分割其实加载的一个模块,require/export出生在野生规范,被广泛使用在CommonJS、AMD、CMD等等, import/export则是名门正派由ECMAScript版本进来。
本质的差别:
- CommonJS 还是 ES6 Module 输出都可以看成是一个具备多个属性或者方法的对象
- default 是 ES6 Module 所独有的关键字,export default fs 输出默认的接口对象
- ES6 module 中导入模块的属性或者方法是强绑定,包括基础类型,而 CommonJS 则是普通的值传递或者引用传递。
Async Component
首先我们创建一个包装的 Async Component。
import React, {Component} from'react';
exportdefaultfunctionasyncComponent(importComponent) {
classAsyncComponentextendsComponent{
constructor(props) {
super(props);
this.setState(component : null)
}
async componentDidMount() {
const {default: component} = await importComponent();
this.setState({component: component})
}
render() {
const C = tis.state.component;
return C
? <C {...this.props}/>
: null
}
}
return AsyncComponent;
}
之前我们都是静态的加载组件
import Home from'./containers/Home';
现在我们使用 asyncComponent 来动态加载我们需要的组件
const AsyncHome = asyncComponent(() =>import ('./container/Home'))
比较两者的区别,我们会发现在动态加载起始我们只是包装了一个函数,将一个函数传入,当包装的函数加载完成后在生命周期的componentDidMount()来import我们真正需要的组件。
然后我们来看看将组件使用在 routes,之前我们是直接加载的静态组件
<Route path = '/' exact component = {Home}/>
// <Route path = '/' exact component = {AsyncHome}/>
总结
import React from"react";
import { Route, Switch } from"react-router-dom";
import asyncComponent from"./components/AsyncComponent";
import AppliedRoute from"./components/AppliedRoute";
import AuthenticatedRoute from"./components/AuthenticatedRoute";
import UnauthenticatedRoute from"./components/UnauthenticatedRoute";
const AsyncHome = asyncComponent(() =>import("./containers/Home"));
const AsyncLogin = asyncComponent(() =>import("./containers/Login"));
const AsyncNotes = asyncComponent(() =>import("./containers/Notes"));
const AsyncSignup = asyncComponent(() =>import("./containers/Signup"));
const AsyncNewNote = asyncComponent(() =>import("./containers/NewNote"));
const AsyncNotFound = asyncComponent(() =>import("./containers/NotFound"));
exportdefault ({ childProps }) =>
<Switch>
<AppliedRoute
path="/"
exact
component={AsyncHome}
props={childProps}
/>
<UnauthenticatedRoute
path="/login"
exact
component={AsyncLogin}
props={childProps}
/>
<UnauthenticatedRoute
path="/signup"
exact
component={AsyncSignup}
props={childProps}
/>
<AuthenticatedRoute
path="/notes/new"
exact
component={AsyncNewNote}
props={childProps}
/>
<AuthenticatedRoute
path="/notes/:id"
exact
component={AsyncNotes}
props={childProps}
/>
{/* Finally, catch all unmatched routes */}
<Route component={AsyncNotFound} />
</Switch>
通过运行 npm run build 我们会看到 代码切割的 js 分别是 build/static.js/1.chunk.js 之类的,说明我们已经已经完成了代码切割。
react-loadable
如果你讲上面的 asyncComponent 放入了线上环境很有可能会被主管打死,因为上面的代码仅供参考,因为是异步加载所以有很多异常的 corner case 没有考虑在其中。其实已经有大神给我们写好的 react-loadable,使用起来相当方便,再次我也不在赘述了。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

上一篇: CSS 隐藏滚动条
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论