react大型项目的按需加载和代码拆分。
REACT中的按需加载
按需加载,顾名思义,需要什么我们才加载什么资源。传统的单页应用(SPA)在首屏就会加载整个应用所有页面的结构,样式与逻辑。所以首屏的加载速度会大大降低,从而影响用户体验。有什么方法能让首屏加载变快呢?其中最最重要的就是spa的按需加载了,也可以叫相应页面的异步加载。
方法1:使用react-loadable模块辅助进行按需加载
本项目用的create-react-app来构建
这个方法是我最常用的。其中用到了一个react-router官方提供的第三方库,react-loadable。这个库主要是用来帮助我们完成按需加载的。
react-loadable
具体用法,参照官方给出的example。
安装之后,我们首先在需要按需加载的组件的同文件夹下创建一个loadable.js文件,如下
import React from "react";
import Loadable from 'react-loadable';
const LoadableComponent = Loadable({
loader: () => import('./'), //这里相当于再加同文件夹下的index.js
loading(){//这里是加载进行时,页面上要显示的ui
return <div>正在加载</div>
}
});
export default ()=> <LoadableComponent/>
然后,在入口文件中,我们写上关于路由的改变
//Detail变成一个异步组件了
import Detail from "./pages/detail/loadable.js";
//以及
<Route exact path="/detail/:id" component={Detail}></Route>
注意,由于Detail是loadable.js导出的模块,经过上面的包装,就会让loadable.js内的模块获取到props,但同时,props没法传入到真正的Detail,所以在Detail组件中,我们无法获取到react-router提供的history,进而没法进行一些关于路由的操作。
所以在Detail组件文件中,我们进行以下操作:
import { withRouter } from "react-router-dom";
//加载一个react-router-dom提供的withRouter对Detail进行包裹
export default withRouter(Detail);
方法一会把Detail的相关逻辑拆分出去,然后再首屏加载的时候对该部分进行忽略,等到我们进入detail页面,再进行加载。达到了代码拆分的目的。
方法2:import()方法:
import()函数是es6模块化标准之后才出现的新的特性。以前的import方法只能在文件中加载另一个文件导出的模块。现在,应用import()方法,我们可以在括号里面写相应加载函数,可以起到运行时加载的作用,就实现了了相应的按需加载功能。配合webpack相应的配置,同时实现代码拆分。
首先,我们在工具文件夹utils当中,写一个asyncload高阶组件,放在asyncload文件夹下的asyncComponent.js中.
让我们来写一个名为AsynComponent的高阶组件。它接收一个函数,函数返回平常的组件。经过包装后返回。
import React,{ PureComponent } from "react";
export default function AsyncComponent(importComponent){
class AsyncComponent extends PureComponent{
constructor(props) {
super(props);
this.state = {
component:null
};
}
async componentDidMount(){
const { default:component } = await importComponent();
this.setState({
component:component
});
}
render(){
const C = this.state.component;
return C ? <C {...this.props}/> : null
}
}
return AsyncComponent;
}
然后再入口文件app.js中:
// import()异步加载
import AsyncComponent from "./utils/asyncload/asyncComponent";
const BasicImport = AsyncComponent(() => import("./pages/imports"));
//import()方法加载了imports里面的index.js,并返回一个promise
<Route exact path="/impt" component={BasicImport}></Route>
这样也能把impt对应的impt代码分隔开来。构建的时候就能看到,多了相应的js文件。并且不会再首屏加载。