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 起始已经内置了代码分割功能来动态加载 importImport 来动态加载组件通常配合 React Router 我们通过设置 React Router 来根据 Url 路径来做到按需加载组件
JS 的模块化规范
代码分割其实加载的一个模块, require/export 出生在野生规范, 被广泛使用在 CommonJSAMDCMD 等等, import/export 则是名门正派由 ECMAScript 版本进来
本质的差别:
CommonJS 还是 ES6 Module 输出都可以看成是一个具备多个属性或者方法的对象
default 是 ES6 Module 所独有的关键字, export default fs 输出默认的接口对象
ES6 module 中导入模块的属性或者方法是强绑定, 包括基础类型, 而 CommonJS 则是普通的值传递或者引用传递
Async Component
首先我们创建一个包装的 Async Component
- import React, {Component} from 'react';
- export default function asyncComponent(importComponent) {
- class AsyncComponent extends Component {
- 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"));
- export default ({ 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, 使用起来相当方便, 再次我也不在赘述了
来源: https://juejin.im/post/5ab0cd96f265da239530d1c1