项目采用了阿里的umijs+dva+antd框架,系统通过“yarn start”运行后,可以正常启动运行,访问页面也没有问题。
但我注释掉一行代码后,然后系统就进入到死循环,直到页面崩溃。代码如下:
路由配置如下:
{
name: 'account',
path: '/account',
icon: 'user',
routes: [
{
name: 'settings',
path: '/account/settings',
component: './account/settings',
}
]
}
account/settings/index.jsx:
import MyContainer from "@/components/MyContainer";
import { useState } from "react";
import SettingForm from './setting_form';
const Index = () => {
const [loading, setLoading] = useState(false);
const [tip, setTip] = useState('正在加载数据...');
console.log("settings.Index");
console.log("settings.Index");
debugger
const setContainerLoading = (loading, tip) => {
setLoading(loading);
setTip(tip === undefined ? '' : tip);
}
return (
<MyContainer loading={ loading } tip={ tip }>
<SettingForm setLoading={ setContainerLoading }/>
</MyContainer>
);
}
export default Index;
MyContainer为一个页面容器
component/MyContainer/index.jsx:
import { PageContainer } from "@ant-design/pro-layout";
import styles from './index.less';
import { Spin } from 'antd';
const Index = ({loading, tip, children}) => {
console.log("loading: ", loading);
console.log("tip: ", tip);
return (
<PageContainer>
<Spin spinning={ loading } tip={ tip }>
<div className={ styles.pageContainer }>
{ children }
</div>
</Spin>
</PageContainer>
)
}
export default Index;
SettingForm是一个表单组件,我把其中的内容都删除了
account/settings/setting_form.jsx:
import { Form } from "antd";
import { useState } from "react";
import { cacheGet } from "@/utils/cache";
import { getUser } from "@/services/vipcti-adm/user";
import ProForm from "@ant-design/pro-form";
const SettingForm = () => {
const [form] = Form.useForm();
const formItemLayout = {
labelCol: {span: 4},
wrapperCol: {span: 12, offset: 1},
}
return (
<ProForm form={ form }
{ ...formItemLayout }
layout='horizontal'
>
</ProForm>
);
}
export default SettingForm;
setting_form.jsx中import的cache.js文件内容
utils/cache.js:
import storage from 'store';
import { listForTree } from '@/services/vipcti-adm/region';
import { fetchDictList, fetchDictIntList, fetchDictMap, fetchDictIntMap } from '@/services/vipcti-adm/dict';
export function cacheGet(key, dataType) {
...
}
在cache.js中又import了dict.js中的几个函数
import { fetchDictList, fetchDictIntList, fetchDictMap, fetchDictIntMap } from '@/services/vipcti-adm/dict';
dict.js中的内容如下:
services/vipcti-adm/dict.js:
import {request} from 'umi';
export function fetchDictList(type) {
return request('/dict/list', {
method: 'get',
params: { type: type }
});
}
export function fetchDictIntList(type) {
// return request('/dict/int-list', {
// method: 'get',
// params: { type: type }
// })
}
export function fetchDictMap(type) {
// return request('/dict/map', {
// method: 'get',
// params: { type: type }
// })
}
export function fetchDictIntMap(type) {
// return request('/dict/int-map', {
// method: 'get',
// params: { type: type }
// })
}
通过执行“yarn start”运行代码后,访问路由:/account/settings都正常,但是在我注释掉/account/settings/index.jsx中下面几行或一行代码并保存后:
//console.log("settings.Index");
console.log("settings.Index");
debugger
页面就进入死循环,打开浏览器的console,可以发现,打印"setting.Index"后,如果有debuger,就停留到debgger那一行,点击“继续运行”按钮后,一直重复以上动作。如果注释掉debugger这一行,则console中一直打印“setting.Index",直至页面崩溃。
为了查找问题,我对react源代码进行了debug,它进入了以下代码堆栈循环:
settings.Index
Component
renderWithHooks
mountIndeteminateComponent
beginWork
beginWork$1
stopProfilerTimerIfRunningAndRecordDelta
performUnitOfWork
workLoopSync
// workLoopSync是一个重复渲染组件及其子组件的过程,只要workInProcess !== null就会一直循环,而正是因为这种情况,
// 导致了代码一直在这个死循环中出不来。
function workLoopSync() {
while(workInProcess !== null) {
performUnitOfWork(workInProcess);
}
}
// 在mountIndeterminateComponent函数中,调用完renderWithHooks,会执行:
reconcileChildren(null, workInProgress, value, renderLanes);
workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderLanes);
function reconcileChildFibers(returnFiber, currentFirstChild, newChild, lanes);
return placeSingleChild(reconcileSingleElement(returnFiber, currentFirstChild, newChild, lanes));
var _created4 = createFiberFromElement(element, returnFiber.mode, lanes);
createFiberFromTypeAndProps(type, key, pendingProps, owner, mode, lanes)
resolvedType = resolveFunctionForHotReloading(resolvedType);
正常情况下,resolvedType应该获取到子组件的类型,这样才能递归渲染下去,但实际情况是获取到了本身的组件类型,导致一直循环。
resolveFunctionForHotReloading的代码如下:
function resolveFunctionForHotReloading(type) {
{
if (resolveFamily === null) {
// Hot reloading is disabled.
return type;
}
var family = resolveFamily(type);
if (family === undefined) {
return type;
} // Use the latest known implementation.
return family.current;
}
}
// 这里执行了:
var family = resolveFamily(type);
// resolveFamily代码如下:
function resolveFamily(type) {
// Only check updated types to keep lookups fast.
return updatedFamiliesByType.get(type);
} // If we didn't care about IE11, we could use new Map/Set(iterable).
// 这里的updatedFamiliesByType是一个Map
var updatedFamiliesByType = new PossiblyWeakMap();
debugger到这里,已经无法再继续了,那肯定是生成这个Map时,其保存的组件类型就有问题了。虽然知道在代码层面的问题,但却不知道造成死循环的根本原因, 也无法解决问题。
既然debug无法解决问题,则从另外一方面出发,通过删除掉具体的功能代码,只保留最简单的代码来尝试,看看是否还会出现死循环。上面列出的代码,即是删除功能代码后保留的最简单代码。但还是会出现死循环。通过不断尝试 注释/运行代码,发现问题代码为dict.js中的四个函数:
export function fetchDictList(type) {
return request('/dict/list', {
method: 'get',
params: { type: type }
});
}
export function fetchDictIntList(type) {
// return request('/dict/int-list', {
// method: 'get',
// params: { type: type }
// })
}
export function fetchDictMap(type) {
// return request('/dict/map', {
// method: 'get',
// params: { type: type }
// })
}
export function fetchDictIntMap(type) {
// return request('/dict/int-map', {
// method: 'get',
// params: { type: type }
// })
}
虽然程序中并没有调用这几个函数,只是import了一下,但也有问题,只要注释掉这几个函数,运行后,再注释掉一两行代码,就不会进入死循环了。或者保留函数,只注释掉函数中的‘return request” 代码,保留一个空函数,也不会有问题。
这个return request代码,正常来看也没啥特别,程序中也并没有调用,最诡异的是,一开始运行是没有问题的,只有在运行过程中,注释掉一两行无关紧要的代码并保存后,才会进入死循环。我百思不得其解,由于本人也是初学React,犯了什么基础性的错误也是有可能的,不知道哪位是否遇到类似问题,或者能帮忙指出问题所在,本人不胜感激!
最后发现是umijs框架的一个BUG,一个叫MSFU的配置,关掉,世界就清爽了。。。唉