React.lazy
定义一个动态加载的组件。有助于缩减bundle的体积,延迟加载未使用的组件。
2020-5-23 更新
React.lazy
自身缺陷无法解决遇到的【问题二】、【问题三】@loadable/component
尝试解决以上的问题,新增其他相关说明。说明:
示例中项目由create-react-app
创建生成。
const UserInfo = React.lazy("./userInfo");
动态加载页面路由,从后端获取到登陆用户所拥有的权限,动态设置路由信息。
模拟的行为测试。index.js
import React,{useState,useEffect,lazy,Suspense} from 'react';
import { BrowserRouter as Router ,Switch,Route, Link} from 'react-router-dom';
import { Menu } from 'antd';
function RouteDemo(){
let [menus,setMenus] = useState([]);
useEffect(() => {
setTimeout(()=>{
setMenus([
{
key:"/userInfo",
value:"./userInfo",
name:"用户管理"
},
{
key:"/deptInfo",
value:"./deptInfo",
name:"机构管理"
}
])
},3000);
}, [])
return (<Router>
<Menu mode="vertical">
{menus.map(item=><Menu.Item key={item.key}><Link to={item.key}>{item.name}</Link></Menu.Item>)}
</Menu>
<Suspense fallback={<p>加载中...</p>}>
<Switch>
{menus.map(route=><Route path={route.key} component={lazy(()=>import(route.value))} />)}
</Switch>
</Suspense>
</Router>);
}
以lazy(()=>import(route.value))
书写时报错,加载不到指定的资源。
调整测试是否能加载./userInfo
return (<Router>
// ...
{menus.map(route=><Route path={route.key} component={lazy(()=>import("./userInfo"))} />)}
// ...
</Router>)
return (<Router>
// ...
{menus.map(route=><Route path={route.key} component={lazy(()=>import(`${route.value}`))} />)}
// ...
</Router>)
测试加载成功,路由跳转OK。
加载上级目录组件。
function RouteDemo(){
let [menus,setMenus] = useState([]);
useEffect(() => {
setTimeout(()=>{
setMenus([
{
key:"/userInfo",
value:"./userInfo",
name:"用户管理"
},
{
key:"/deptInfo",
value:"./deptInfo",
name:"机构管理"
},
{
key:"/reduxDemo", // 新增上级目录的组件
value:"../reduxDemo",
name:"redux测试使用"
}
])
},3000);
}, [])
return (<Router>
// ...
<Suspense fallback={<p>加载中...</p>}>
<Switch>
{menus.map(route=><Route path={route.key} component={lazy(()=>import(`${route.value}`))} />)}
</Switch>
</Suspense>
</Router>);
}
当点击reduxDemo
菜单操作时报错。
再次测试直接加载:
return (<Router>
// ...
{menus.map(route=><Route path={route.key} component={lazy(()=>import("../reduxDemo"))} />)}
// ...
</Router>)
由于问题二未解决,遂想其他的解决方式,出现了问题三
在使用create-react-app
开发项目有时,需要自定义配置,使用react-app-rewired
如何使用react-app-rewired
,参考:
react-app-rewired
customize-cra
config-overrides.js
指定了所有的组件目录为@Component
, 这样可以统一所有不管从哪加载过来的组件都从根目录下写。
const {
override,
addWebpackAlias,
} = require("customize-cra");
const path = require("path");
module.exports = override(
addWebpackAlias({
["@Component"]:path.resolve(__dirname,'src/example')
})
);
修改异步加载的菜单数据信息,修改渲染。
function RouteDemo(){
let [menus,setMenus] = useState([]);
useEffect(() => {
setTimeout(()=>{
setMenus([
{
key:"/userInfo",
value:"/routerDom/userInfo",
name:"用户管理"
},
{
key:"/deptInfo",
value:"/routerDom/deptInfo",
name:"机构管理"
},
{
key:"/reduxDemo",
value:"/reduxDemo",
name:"redux测试使用"
}
])
},3000);
}, [])
return (<Router>
<Menu mode="vertical">
{menus.map(item=><Menu.Item key={item.key}><Link to={item.key}>{item.name}</Link></Menu.Item>)}
</Menu>
<Suspense fallback={<p>加载中...</p>}>
<Switch>
{menus.map(route=><Route path={route.key} component={lazy(()=>import(`@Component${route.value}`))} />)}
</Switch>
</Suspense>
</Router>);
}
按正常的逻辑,理应这样是可以的。可惜报错了
将变量改为字符串,在测试:
return (<Router>
// ...
{menus.map(route=><Route path={route.key} component={lazy(()=>import(`@Component${"/routerDom/userInfo"}`))} />)}
// ...
</Router>)
未解决
尝试了加载上级组件,结果是没有问题。
return (<Router>
// ...
{menus.map(route=><Route path={route.key} component={lazy(()=>import(`@Component${"/reduxDemo"}`))} />)}
// ...
</Router>)
现在的问题是搞懂React.lazy
到底是怎么处理的动态加载。处理其中的变量的。搞得心累
经过查证,React.lazy
是有缺陷的,完全的动态导入是不支持的。官方推荐使用@loadable/component
npm install @loadable/component
一下是针对React.lazy
问题一、问题二的测试。
import loadable,{lazy} from '@loadable/component';
function RouteDemo(){
// ...
return (<Router>
<Suspense fallback={<p>加载中...</p>}>
<Switch>
{menus.map(route=><Route path={route.key} component={loadable(()=>import(`${route.value}`))} />)}
</Switch>
</Suspense>
</Router>);
}
看来跟我预想的有点不一样。
以为@loadable/component
完美无缺的,可以解决我的问题。还是太年轻
import loadable,{lazy} from '@loadable/component';
// 其余部分省略
function RouteDemo(){
// ...
return (<Router>
<Suspense fallback={<p>加载中...</p>}>
<Switch>
{menus.map(route=><Route path={route.key} component={lazy(()=>import(route.value))} />)}
</Switch>
</Suspense>
</Router>);
}
情况二:../***
形式 存在的,当点击菜单时。function RouteDemo(){
// ...
return (<Router>
<Suspense fallback={<p>加载中...</p>}>
<Switch>
{menus.map(route=><Route path={route.key} component={lazy(()=>import(`${route.value}`))} />)}
</Switch>
</Suspense>
</Router>);
}
目前未使用其代替项目中的动态路由设置。一次做记,下次查看。
loadable
默认导出。
参数 | 说明 | |
---|---|---|
fn | 加载指定的组件 | ()=>import("…/**") |
options | 可以传入其他参数 | |
options.fallback | 在加载过程中显示,未加载完时。类似React.Suspense |
|
options.ssr | 服务端渲染设置,默认true |
|
options.cacheKey | 用于指定缓存动态属性键 |
lazy
用于配合使用React.Suspense
的方法。