react lazy动态加载组件问题

React.lazy定义一个动态加载的组件。有助于缩减bundle的体积,延迟加载未使用的组件。

2020-5-23 更新

  1. React.lazy自身缺陷无法解决遇到的【问题二】、【问题三】
  2. @loadable/component 尝试解决以上的问题,新增其他相关说明。

React.lazy

说明:

示例中项目由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到底是怎么处理的动态加载。处理其中的变量的。搞得心累 💔💔💔

@loadable/component

经过查证,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完美无缺的,可以解决我的问题。还是太年轻😮😮😮

  1. 以下情况会导致页面卡死、奔溃
    情况一:
      按正常的变量形式传入组件加载地址.
    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>);
    }
    
其他API说明

目前未使用其代替项目中的动态路由设置。一次做记,下次查看。
loadable 默认导出。

参数说明
fn加载指定的组件()=>import("…/**")
options可以传入其他参数
options.fallback在加载过程中显示,未加载完时。类似React.Suspense
options.ssr服务端渲染设置,默认true
options.cacheKey用于指定缓存动态属性键

lazy 用于配合使用React.Suspense的方法。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

heroboyluck

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值