场景
现在很多场景都是使用react+webpack 实现的单页面应用(SPA),SPA不仅良好的实现了前后端分离,而且在路由切换时相应更快。但是,如果单页面打包成单一的JS文件会很大,加载时间长,所以,怎样才能保证首屏加载快,同时切换到其他页面的响应时间又较短呢?答案是按需加载,访问哪个路由就加载哪块代码,不需要加载其他路由页面的代码。
怎么做
webpack 配置
webpack配置文件的output字段设置为:

name 会被替换成你为chunk指定的名字,默认是webpack自动生成的chunk id;这里取chunkhash中的6位,避免使用缓存。
异步加载路由
按需加载,需要让路由动态加载组件,详见Webpack的代码分割功能。
Webpack中import、require、按需加载的执行过程可以参考这里
动态import, 需要webpack version > 2.4。如果项目使用的webpack版本较低,就不能使用动态import技术,可以用 require.ensure 等。 都能实现按需加载,具体使用哪种方式,根据场景来定。
使用官网推荐的动态import()
react-router版本>=4
方法一:借助bundle-loader来实现按需加载,或自己写一个lazyloader
路由:
//bundle 模块用来异步加载组件
import Bundle from './bundle.js;
import ListContainer from 'bundle-loader?lazy&name=app-[name]!./app/list.js'
const List = () => (
<Bundle load={ListContainer}>
{(List) => <List /> }
</Bundle>
)
// ......
<BrowserRouter>
<Router path='/' >
<Router path='list' component={List} />
</Router>
</BrowserRouter>
bundle.js实现:
import React, { Component } from 'react';
export default class Bundle extends React.Component {
state = {
mod: null
}
componentWillMount() {
this.load(this.props)
}
componentWillReceiveProps(nextProps) {
if (nextProps.load !== this.props.load) {
this.load(nextProps)
}
}
load(props) {
this.setState({ mod: null })
props.load((mod) => {
this.setState({
// handle both es imports and commonjs
mod: mod.default ? mod.default : mod
})
})
}
render() {
if (!this.state.mod) return false;
return this.props.children(this.state.mod)
}
}
方法二: 动态import, code split


使用webpack的require.ensure()
老方案,react-router版本<4,使用getComponent
由于要按需加载,需要让路由动态加载模块,将原来的component用getComponents方法来替代。相比以前的 component 属性,这个方法是异步的,只有当路由匹配时,才会调用这个方法。
如果需要返回多个子组件,则使用getComponents方法,将多个组件作为一个对象的属性通过callback返回出去即可,这个在官方示例也有。
require.ensure方法是webpack提供的方法,也是按需加载的 核心方法 。第一个参数是依赖,第二个是回调函数,第三个就是上面提到的chunkfile的Name。
// require.ensure的使用
// chunkName参数 可以不写
require.ensure(dependencies, callback, chunkName)
各个页面的子路由写到childRoutes里,onEnter属性是react router提供的 api。如:
export default routes = {
path: '/',
getComponent(nextState, callback) {
require.ensure([], (require) => {
callback(null, require('./container/index').default)
}, 'index')
},
childRoutes: [{
path: '/your_Route',
getComponent(nextState, callback) {
require.ensure([], (require) => {
callback(null, require('./container/notfoundPage').default)
})
},
// onEnter 可以不写
onEnter: ()=> {}
}, {
path: '*',
getComponent(nextState, callback) {
require.ensure([], (require) => {
callback(null, require('./container/notfoundPage').default)
})
},
}]
}
网友评论