react lazy动态加载组件问题

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

React.lazy使用问题

      • React.lazy
        • 简单的使用:
        • 遇到的问题:
          • 问题一
          • 问题二
          • 问题三
      • @loadable/component
        • 尝试解决问题二、问题三
        • 踩雷警告
        • 其他API说明

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))书写时报错,加载不到指定的资源。
react lazy动态加载组件问题_第1张图片
调整测试是否能加载./userInfo

return (<Router>
		// ...
        {menus.map(route=><Route path={route.key} component={lazy(()=>import("./userInfo"))} />)}
        // ...
   </Router>)

测试可以加载成功。
react lazy动态加载组件问题_第2张图片
已解决此问题

以模板字符串的写法书写在这里插入图片描述

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菜单操作时报错。
react lazy动态加载组件问题_第3张图片
再次测试直接加载:

return (<Router>
		// ...
	   {menus.map(route=><Route path={route.key} component={lazy(()=>import("../reduxDemo"))} />)}
	   // ...
</Router>)

测试加载组件成功,可操作。
react lazy动态加载组件问题_第4张图片
未解决

问题三

由于问题二未解决,遂想其他的解决方式,出现了问题三

在使用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>);
}

按正常的逻辑,理应这样是可以的。可惜报错了
react lazy动态加载组件问题_第5张图片
将变量改为字符串,在测试:

return (<Router>
		// ...
        {menus.map(route=><Route path={route.key} component={lazy(()=>import(`@Component${"/routerDom/userInfo"}`))} />)}
        // ...
   </Router>)

测试加载成功,只想说如此诡异。
react lazy动态加载组件问题_第6张图片

未解决

尝试了加载上级组件,结果是没有问题。

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动态加载组件问题_第7张图片

尝试解决问题二、问题三

一下是针对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>);
}

截图一:
react lazy动态加载组件问题_第8张图片
截图二:
react lazy动态加载组件问题_第9张图片

看来跟我预想的有点不一样。

踩雷警告

以为@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的方法。

你可能感兴趣的:(问题小集,react)