当网站规模越来越大,通过webpack 打包后的 react 项目也会越来越大,这会导致首页渲染时间变长,影响用户体验,webpack 提供了一种按需加载的方式,需要结合 react-router 使用,他会将代码拆分成多个小包,需要哪个部分就加载响应的包。
React-router-v4 - Webpack 实现按需加载(code-splitting)
首先需要对 config 文件修改一下,如下
output: {
path: path.join(__dirname, 'dev'), // 输出路径
filename: '/js/bundle.js', // 输出文件名
publicPath: path.join(__dirname, 'dev'), // 必填项
chunkFilename : '/js/routes/[name].chunk.js?[chunkhash:10]', // 按需加载输出的文件名
},
app/js/routes.js
)react 按需加载,关键是让路由动态加载组件,react-router 提供了一个属性
getComponent
,它与component
属性一样,但是是异步的,当路由匹配时才会调用这个方法,常用于代码分割;
相关内容查看 API文档webpack 为我们提供了一个按需加载函数
require.ensure(dependencies, callback, chunkName)
,
更多了解,查看官方文档
/*
app/js/routes.js
路由配置文件
*/
import React from 'react';
import { Route, IndexRoute} from 'react-router';
// 引入容器组件
import App from './containers/App';
export default (
"/" component={App}>
(location, cb) => {
require.ensure([], require => {
cb(null, require('./containers/Login.js').default)
},'login');
}} />
"home"
getComponent={(location, cb) => {
require.ensure([], require => {
cb(null, require('./containers/Home.js').default)
},'home');
}} />
"login" getComponent={(location, cb) => {
require.ensure([], require => {
cb(null, require('./containers/Login.js').default)
},'login');
}} />
);
注意:
cb(null, require('./containers/Login.js').default)
因为我用 es6 的export default
导出的组件,所以 require 之后要加上default
,如果使用module.export
导出组件 则不需要加 default
以上写法在 路由层级或者数量较多的时候会比较臃肿,所以还需要对路由进行拆分
路由拆分,需要对目录结构做一下改变,在js 文件夹下添加 routes 路由配置文件夹
|---js
| |---actions
| |
| |---components
| |
| |---containers
| |
| |---reducers
| |
| |---store
| |
| |---routes
| | |---login.js // 主路由的下一级路由
| | |---login // login 路由的下一级路由目录
| |---constants.js // 静态常量
| |---index.js // 项目入口文件
| |---routes.js // 路由配置主文件
|
routes.js 路由配置的主文件
const routes = {
path : '/', // 将匹配的路由,对应标签中的 path 属性
indexRoute: { // 设置默认显示页面,对应标签中的 IndexRoute 组件
getComponent(nextState, cb) {
require.ensure([], (require) => {
cb(null, require('./containers/Login').default);
}, 'Login');
}
},
// childRoutes:[] // 子 route 的一个数组,与在 JSX route 配置中的 children 一样。
getChildRoutes(partialNextState, cb) { // 与 childRoutes 一样,但是是异步的,并且可以接收 partialNextState(location 信息)
require.ensure([], (require) => {
cb(null, [
require('./routes/login'),
require('./routes/home'),
require('./routes/error')
])
})
},
getComponent(nextState, cb) { // 定义对应的组件,对应标签中的 component 属性,但是是异步的,路由匹配时才会调用这个方法
require.ensure([], (require) => { // require.ensure : webpack 提供的按需加载方法
cb(null, require('./containers/App').default);
}, 'App');
}
}
export default routes;
js/routes/home.js 路由分支
module.exports = {
path: 'home',
getChildRoutes(partialNextState, cb) {
require.ensure([], (require) => {
cb(null, [
require('./home/page'),
require('./home/student'),
require('./home/admin')
])
})
},
getComponent(nextState, cb) {
require.ensure([], (require) => {
cb(null, require('../containers/Home').default)
}, 'Home')
}
}
基本思路 : 每当指定路由要发生改变,就使用一个中间服务,介于上一级路由和将要到达路由之间启动,来判断我们是否有进入这个路由的权限,React 中的
提供了一个
onEnter
方法,表示正要进入这个路由,在这里判断进入权限,可以修改要进入的页面
"home"
getComponent={(location, cb) => {
require.ensure([], require => {
cb(null, require('./containers/Home.js').default)
},'home');
}} onEnter={request}/>
function request(nextState, replace, next){
let auth = nextState.location.query.auth;
if(auth === 'home'){
next();
} else if (auth === 'other') {
replace('/other');
next();
} else {
replace('/');
next();
}
}
注:nextState : 表示跳转后的location 信息;replace 用于 更改下一个进入的页面地址,但是不会跳转;next : 用于跳转页面,没有其他操作则显示当前路由对应页面
如果使用路由拆分,如下
module.exports = {
path: 'home',
getChildRoutes(partialNextState, cb) {
require.ensure([], (require) => {
cb(null, [
require('./home/page'),
require('./home/student'),
require('./home/admin')
])
})
},
getComponent(nextState, cb) {
require.ensure([], (require) => {
cb(null, require('../containers/Home').default)
}, 'Home')
},
onEnter: (nextState, replace, next) => {
console.log(nextState.location.state);
let isAuth = nextState.location.state.isAuth;
if (isAuth === 'student' || isAuth === 'teacher' || isAuth === 'admin') {
next();
} else {
replace('/error');
next();
}
}
}