前言
React开发过程中,大部分都是单页面应用,不做代码分片的话,所有的js文件都打成一个庞大的bundlejs文件,随着项目内容的不断增多,首屏空白时间就会变得越来越长。
因此可以对代码进行分片,打包时生成多个js文件,每次页面只请求所需要的js文件,用户体验大大提升。
法一:Loadable实现(不推荐)
在React16.6之前懒加载流行使用Loadable包。
- 安装依赖
npm i react-loadable -S
- 路由配置
import React from 'react'; import Loadable from 'react-loadable'; const Loading = () => <div>loading</div>; // 定义一个组件,在组件加载前展示,组件加载完成之后消失 const Index = Loadable({ loader: () => import('../views/Index/Index'), loading: Loading }); const PageOne = Loadable({ loader: () => import('../views/PageOne/PageOne'), loading: Loading }); const PageTwo = Loadable({ loader: () => import('../views/PageTwo/PageTwo'), loading: Loading }); export default [ { path: '/', component: Index, exact: true, title: '首页', }, { path: '/PageOne', component: PageOne, exact: true, title: '登录', }, { path: '/PageTwo', component: PageTwo, exact: true, title: '登录', }, ];
- App.js
import React, { Component } from 'react'; import { Route, Switch, Redirect, BrowserRouter as Router } from 'react-router-dom'; // 这里的是路由配置文件 import routes from './router/index'; export default class App extends Component { render() { return ( <Router> <Switch> { routes.length > 0 && routes.map((route) => { //遍历路由数组 const { path, component:C, // 这样写,是为了以下写法中,用C来代替标签如<Home />,因为不能<component /> exact, } = route; return ( <Route exact={exact} key={path} path={path} render={(props) => { // 实际上这里会处理很多的业务逻辑,如权限和登录(无token自动跳转到登录页),相当于vue的导航守卫 // console.log(props); return (<C />); }} /> ); }) } {/* 默认进入/时自动匹配到/Home */} <Redirect exact from="/" to={'/'} /> {/* 默认无效路径时自动匹配到首页 */} <Redirect to="/" /> </Switch> </Router> ); } }
如果react是高版本,会出现这样的警告,所以不推荐使用此方法
法二: 使用react的lazy,Suspense方法实现分片打包
- 路由配置
import { lazy } from 'react'; const PageOne = lazy(() => import('../views/PageOne/PageOne')); const PageTwo = lazy(() => import('../views/PageTwo/PageTwo')); const Index = lazy(() => import('../views/Index/Index')); export default [ { path: '/', component: Index, exact: true, title: '首页', }, { path: '/PageOne', component: PageOne, exact: true, title: '登录', }, { path: '/PageTwo', component: PageTwo, exact: true, title: '登录', }, ];
- App.js,引入并使用Suspense包裹要懒加载的组件(Route)
import React, { Suspense, Component } from 'react'; import { Route, Switch, Redirect, BrowserRouter as Router } from 'react-router-dom'; // 这里的是路由配置文件 import routes from './router/index'; export default class App extends Component { render() { return ( <Router> <Suspense fallback={<div>loading</div>}> <Switch> { routes.length > 0 && routes.map((route) => { //遍历路由数组 const { path, component:C, // 这样写,是为了以下写法中,用C来代替标签如<Home />,因为不能<component /> exact, } = route; return ( <Route exact={exact} key={path} path={path} render={(props) => { // 实际上这里会处理很多的业务逻辑,如权限和登录(无token自动跳转到登录页),相当于vue的导航守卫 // console.log(props); return (<C />); }} /> ); }) } {/* 默认进入/时自动匹配到/Home */} <Redirect exact from="/" to={'/'} /> {/* 默认无效路径时自动匹配到首页 */} <Redirect to="/" /> </Switch> </Suspense> </Router> ); } }
对redux进行分割
对页面进行分割后,可以进一步的切割页面所需要的数据,我们使用store的replaceReducer方法 在store.js中,添加一个维护当前reducer的对象,和一个更新reduer的方法。
let asyncReducers = {}; export const getNewReducer = (newModuleInfo) => { asyncReducers[newModuleInfo.name] = newModuleInfo.reducer; store.replaceReducer(combineReducer({ ...asyncReducers, })) }
在reducerC文件中,需要调用该方法
import { getNewReducer } from './store.js'; const state = { value: '' }; const reducer = (state = state, action) => { switch(action.type) { case 'setValue': return { ...state, value: action.value }; default: return state; } } export default getNewReducer({ name: 'reducerC', reducer });
文章参考
https://juejin.im/post/6844903953721737224
https://www.jianshu.com/p/164be62145a7
https://segmentfault.com/a/1190000020247862?utm_source=tag-newest