# react ssr 下的路由分割 - 按需渲染
到这里,我们的应用骨架已相对完善,已经可以 用来进行实际项目开发,但是仍然不够,还有优化的空间。
现在的所有业务代码都打包到了一个文件内main.js。
若开发一个真实的项目,在开始阶段页面较少,支持的业务较少,js 代码体积并不会太大,但是随着时间的推移,这个 js 文件会变得越来越大,有可能超过1M。
过大的文件会严重影响页面的加载速度,直接影响用户体验。
# 如何优化
代码全部打包到一个文件内在访问时被全部加载,但用户当前访问的也就一个页面,所以我们只需要当前页面的业务代码就可以了,其他页面的代码是不需要加载的,当用户访问的时候再加载和执行岂不是更好?
所以我们本节开始来实现基于路由的按需渲染。
# 需渲染原理
早期接触过 webpack2 的同学应该都知道require.ensure方法,甚至使用过该方法来实现按需加载。
这个 Api 的作用就是用来实现代码分割,它会单独打包指定的文件,不和主文件打包在一起。
不过后来有了更加规范的方式来实现按需加载-动态导入。
const A = import('./pages/A');
并且在webpack2版本中早就支持了该特性,只需要配置@babel/plugin-syntax-dynamic-import插件便可使用。
也就是说从webpack2开始已经支持了require.ensure和动态导入两种方式来实现按需加载。
这里我们主要介绍下使用动态导入的方式来实现按需加载。
import()只是一个语法糖,当前模块没有加载时,内部会发起一个JSONP请求来加载目标代码模块, 返回值是一个Promise对象,可以在then方法内得到真正的模块。
// pages/a.js
export default class A{
//...
}
import('./pags/a').then({default:A}=>{
//...
})
代码拆分和异步加载逻辑webpack已帮我们完成。
那动态导入怎样和react结合来实现按需加载呢?
# 具体实现
实现按需加载并不复杂,官方也有很多 demo 可以参考。
在react router3下实现按需加载更简单,但是 react router4就完全不同了。
在v3中,路由提供了特定的属性来支持,下面简短的几行代码就达到了按需加载的效果。
const A = (location, cb) => {
require.ensure([], require => {
cb(null, require('../Component/A').default)
},'A')
}
//配置route
<Route path="/a" getComponent={A} />
# react router4 按需渲染
我们需要抽象一个AsyncBundler组件,用于按需加载。
我们为该组件添了一个state mod状态, 表示异步加载(import())完成后得到的组件,并且加载过程增加laoding显示。
该组件还接收一个load props,此属性为Promise类型,用于动态导入其他组件,当AsyncBundler挂载完成后,在componentDidMount事件内执行异步组件的加载,也就是props. load方法,在then方法内得到加载成功的异步组件,同时更新AsyncBundler组件的state.mod,完成渲染。
以下是该组件完整代码
/**
* 容器组件,组件按需加载器
*
* @class Bundle
* @extends {Component}
*/
export default class AsyncBundle extends React.Component {
constructor(props) {
super(props);
this.state = {
mod: null//自身状态
};
}
componentDidMount() {
if (!this.state.mod) {
this.load(this.props);
}
}
load(props) {
this.setState({
mod: null
});
//注意这里,使用Promise对象; mod.default导出默认
props.load().then((mod) => {
this.setState({
