问题的起因是左侧模版列表在开发时没有测试最多五百条数据的极限,当数据列表达到五百条的时候,就单纯的改变checkbox组件状态(选中或者不选中之间切换),就很慢,看了一下性能分析,一个click事件在4倍降低CPU的情况下长达4s的长任务,我不理解 :)
这里我在控制台中,发现列表并没有重新渲染所有节点,而且也没有接口调用。仅仅五百个节点就这样,虚拟dom性能有点...
顺便补充一下:react版本是18.2.0,组件库是"antd": "^4.21.0"。难道Fiber树对于虚拟dom树没有提升,还是我使用方式不对?
组件书写结构大概是这样:
const GroupItem = (props: GroupItemProps) => { .....}const TpTemplate = () => { return <>{ <CheckboxGroup className="group-check" value={checkedList} onChange={onChange} style={{ width: '100%' }} > {TpTemplateList?.map(group => ( <div className="group-container" key={group?.id}> <Checkbox value={group.id} className="group-check" /> <GroupItem {...props} > {group.name} </GroupItem> </div> ))} {!(TpTemplateList?.length > 0) && <Empty />} </CheckboxGroup> }</>}
完整代码有点多,其中有使用'ahooks','styled-components'
const { TextArea } = Inputconst CheckboxGroup = Checkbox.Groupinterface GroupItemProps { activeId: string | undefined children: string id: string typeName: string onDelete: (id: string) => void | Promise<any> onClick: () => void | Promise<any> checked: boolean describe: string searchName: string editForm: FormInstance<any> getTpTemplateList: (name: string) => Promise<Result<TemplateListResult[]>> setVisibleCompile: React.Dispatch< React.SetStateAction<{ visible: boolean editId: string name: string }> > setDeleteModal: React.Dispatch<boolean> setCheckedList: React.Dispatch<React.SetStateAction<any[]>>}const GroupItem = (props: GroupItemProps) => { const { children, id, onClick, activeId, checked, describe, getTpTemplateList, setVisibleCompile, setDeleteModal, setCheckedList, editForm, searchName, } = props const isActive = id === activeId const handleSwitchChange = async (e: boolean) => { try { await updateTpTemplateStatus({ template_id: id, is_enable: e ? 1 : 2 }) await getTpTemplateList(searchName) } catch (error: any) { if (error.code !== 200) { Message.error(error.msg || '操作失败') } } } const handleEditClick = (e: React.MouseEvent) => { e.stopPropagation() setVisibleCompile({ visible: true, editId: id, name: children }) editForm.setFieldsValue({ name: children, describe: describe, }) } const handleDeleteClick = () => { setDeleteModal(true) setCheckedList([id]) } return ( <GroupItemContainer isActive={isActive} onClick={onClick}> <Typography.Text ellipsis={{ tooltip: { getPopupContainer: () => document.body } }} className="text" key={id} > {children} </Typography.Text> <Switch checkedChildren="开" unCheckedChildren="关" size="small" style={{ marginRight: 10 }} checked={checked} // loading={updateStatusLoading} onChange={handleSwitchChange} /> <Dropdown overlay={ <Menu items={[ { key: '1', label: ( <div className="operate" onClick={handleEditClick}> <IconFont type={'icon-huabanfuben'} className="delete-icon" /> <div>编辑</div> </div> ), }, { key: '2', label: ( <div className="operate" onClick={handleDeleteClick}> <IconFont type={'icon-shanchu-m'} className="delete-icon" /> <div>删除</div> </div> ), }, ]} /> } > <IconFont type="icon-gengduocaozuo" /> </Dropdown> </GroupItemContainer> )}const MemoizedGroupItem = React.memo(GroupItem)const TpTemplate = () => { const [searchName, setSearchName] = useState<string>('') /**获取att&ck列表 */ // const [getAttCkList, AttCkList] = useGetEndPointAttackList() /**获取模板列表 */ const [getTpTemplateList, TpTemplateList, tpTemplateLoading] = useGetTpTemplateList() const [selectGroup, setSelectGroup] = useState<TemplateListResult>(null) /**编辑模版 */ const [visibleCompile, setVisibleCompile] = useState<{ visible: boolean editId: string name: string }>({ visible: false, editId: '', name: '', }) /**添加模版 */ const [visibleModal, setVisibleModal] = useState<boolean>(false) const [deleteModal, setDeleteModal] = useState<boolean>(false) const [checkedList, setCheckedList] = useState([]) const [createForm] = Form.useForm() const [editForm] = Form.useForm() const { run: setSearchValue } = useDebounceFn( (name: string) => { setSearchName(name) }, { wait: 500, }, ) useEffect(() => { getTpTemplateList(searchName).catch(() => {}) }, [searchName]) const hasSelected = checkedList.length > 0 useEffect(() => { getTpTemplateList('') .then(res => { if (res?.data?.length > 0) { setSelectGroup(res?.data?.[0]) } else { setSelectGroup(null) } }) .catch(() => {}) }, []) const onFinish = async values => { const { name, describe } = values try { if (visibleCompile.visible) { setVisibleCompile({ visible: false, editId: '', name: '' }) await updateTpTemplate({ template_id: visibleCompile.editId, name, describe }).then(() => setSelectGroup(pre => ({ ...pre, id: visibleCompile.editId, name, describe, })), ) await getTpTemplateList(searchName) editForm.resetFields() } else { setVisibleModal(false) await createTpTemplate({ name, describe, }) await getTpTemplateList(searchName) createForm.resetFields() } } catch (error: any) { if (error.code !== 200) { Message.error(error.msg || '操作失败') } } } const onChange = list => { setCheckedList(list) } return ( <UserContainer> <LeftCard title={<SignTitle show={false}>模板列表</SignTitle>}> <SearchGroup> <SearchInputTwo inputProps={{ placeholder: '请输入模板名称', onChange: e => { setSearchValue(e.target.value) }, }} /> </SearchGroup> <BatchOperate hasSelected={hasSelected}> <Delete active={hasSelected} onClick={() => { if (hasSelected) setDeleteModal(true) }} > <SvgIcon name={hasSelected ? 'batch_remove' : 'batch_remove_ash'} className="icon-delete" /> 批量删除 </Delete> </BatchOperate> <DeleteModal text="此操作将永久删除,是否继续?" visible={deleteModal} // confirmLoading={batchDeleteLoading} onOk={async () => { try { setDeleteModal(false) await deleteTpTemplate(checkedList) await getTpTemplateList(searchName) setCheckedList([]) setSelectGroup(TpTemplateList[0]) } catch (error: any) { Message.error(error?.msg || '删除模板列表失败') } }} onCancel={() => { setDeleteModal(false) setCheckedList([]) }} /> <GroupListContainer> <Skeleton loading={tpTemplateLoading}> <CheckboxGroup className="group-check" value={checkedList} onChange={onChange} style={{ width: '100%' }} > {TpTemplateList?.map(group => ( <div className="group-container" key={group?.id}> <Checkbox value={group.id} className="group-check" /> <MemoizedGroupItem // typeId={group?.TypeId} activeId={selectGroup?.id} id={group.id} checked={group?.is_enable == 1} typeName={group?.name} describe={group?.describe} onDelete={() => setDeleteModal(true)} onClick={() => { setSelectGroup(group) }} editForm={editForm} getTpTemplateList={getTpTemplateList} setVisibleCompile={setVisibleCompile} setDeleteModal={setDeleteModal} searchName={searchName} setCheckedList={setCheckedList} > {group.name} </MemoizedGroupItem> </div> ))} {!(TpTemplateList?.length > 0) && <Empty />} </CheckboxGroup> </Skeleton> </GroupListContainer> <LeftCardFooter> <Button type={'primary'} style={{ width: '100%' }} onClick={() => { setVisibleModal(true) }} > 添加模板 </Button> </LeftCardFooter> <Modal style={{ width: '450px' }} title={<SignTitle show>添加模板</SignTitle>} centered // confirmLoading={createLoading} getContainer={() => document.body} visible={visibleModal} // destroyOnClose onCancel={() => { createForm.resetFields() setVisibleModal(false) }} onOk={() => createForm.submit()} // getContainer={false} > <Form form={createForm} onFinish={onFinish} layout="vertical"> <div className="add-payload"> <Form.Item name={'name'} label="模板名称:" rules={[ { required: true, message: '请输入模板名称' }, { max: 15, message: '模板名称最多为15个字符' }, () => ({ validator(_, value) { if ((TpTemplateList || [])?.some(group => group.name === value)) { return Promise.reject(new Error('模板名称已存在')) } return Promise.resolve() }, validateTrigger: 'onSubmit', }), ]} style={{ display: 'flex', flexDirection: 'column' }} > <Input placeholder="请输入(15字)" type="string" onChange={e => {}} /> </Form.Item> <Form.Item name={'describe'} label="模板描述" rules={[{ required: true, message: '请输入模板描述' }]} > <TextArea showCount maxLength={4096} style={{ height: 120, resize: 'none' }} // onChange={onChange} /> </Form.Item> </div> </Form> </Modal> <Modal title={<SignTitle>编辑模板</SignTitle>} centered // confirmLoading={updateLoading} getContainer={() => document.body} visible={visibleCompile.visible} onOk={() => editForm.submit()} onCancel={() => { editForm.resetFields() setVisibleCompile({ visible: false, editId: '', name: '' }) }} // getContainer={false} > <Form form={editForm} onFinish={onFinish} layout="vertical"> <Form.Item name={'name'} label="模板名称:" rules={[ { required: true, message: '模板名称不能为空' }, { max: 15, message: '模板名称最多为15个字' }, () => ({ validator(_, value) { if ( visibleCompile.name !== value && (TpTemplateList || [])?.some(group => group.name === value) ) { return Promise.reject(new Error('已存在')) } return Promise.resolve() }, validateTrigger: 'onSubmit', }), ]} // initialValue={selectGroup?.name} style={{ display: 'flex', flexDirection: 'column' }} > <Input placeholder="请输入(15字)" type="string" onChange={e => {}} /> </Form.Item> <Form.Item name={'describe'} label="模板描述" rules={[{ required: true, message: '模板描述不能为空' }]} // initialValue={selectGroup?.describe} > <TextArea showCount maxLength={4096} style={{ height: 120, resize: 'none' }} /> </Form.Item> </Form> </Modal> </LeftCard> <RightContainer> {TpTemplateList?.length > 0 ? ( <SubTemplate key={selectGroup?.id} id={selectGroup?.id} // attCkList={AttCkList} describe={selectGroup?.describe} /> ) : ( <div style={{ marginTop: 200 }}> <Empty image={EmptyImg} /> </div> )} </RightContainer> </UserContainer> )}export default TpTemplate
有大佬解惑吗?万分感谢!
GroupItem用React.memo:
const GroupItem = React.memo((props: GroupItemProps) => { // ...});
第三方库:
import { FixedSizeList as List } from 'react-window';const TpTemplate = () => { // ... return ( <List height={500} // 视窗的高度 itemCount={TpTemplateList?.length || 0} itemSize={50} // 每个列表项的高度 > {({ index, style }) => { const group = TpTemplateList[index]; return ( <div style={style} key={group?.id}> <Checkbox value={group.id} className="group-check" /> <GroupItem {...props}>{group.name}</GroupItem> </div> ); }} </List> );};
本文向大家介绍react怎么提高列表渲染的性能?相关面试题,主要包含被问及react怎么提高列表渲染的性能?时的应答技巧和注意事项,需要的朋友参考一下 使用webpack 做代码分割。 使用hooks。
问题内容: 我以TodoList为例来反映我的问题,但是显然我的实际代码更加复杂。 我有一些这样的伪代码。 我所有的数据都是不可变的,并且使用了并且一切正常。修改待办事项数据后,仅 重新渲染父项和已编辑的待办事项。 问题是,随着用户的滚动,我的列表有时会变得很大。并且,当更新单个待办事项时,渲染 父项,调用shouldComponentUpdate所有待办事项以及渲染单个待办事项所花费的时间越来越
根据数据库里面获取到的数据信息渲染表格,起初根据数据结构生成了四列,但是由于后续数据库里面会有数据结构上的变化,会增加字段,那我要如何在表格中去追加这部分新数据,从而在前端渲染出一个新的表格————就是说原来四列变六列,并把对应数据也一同渲染上去。
Mpx中的列表渲染与原生小程序中完全一致,详情可以查看这里 值得注意的是wx:key与Vue中的key属性的区别,不能使用数据绑定,只能传递普通字符串将数组item中的对应属性作为key,或者传入保留关键字*this将item本身作为key 下面是简单示例: <template> <!-- 使用数组中元素的 id属性/保留关键字*this 作为key值 --> <view wx:for=
在上文中,我们从多个角度讨论了如何优化页面加载性能。但一个用户体验良好的页面,不仅要快速加载,还需要有一系列流畅的交互。从而,这一节我们把目光投向页面渲染性能。 渲染流程 浏览器在渲染页面前,首先会将 HTML 文本内容解析为 DOM,将 CSS 解析为 CSSOM。DOM 和 CSSOM 都是树状数据结构,两者相互独立,但又有相似之处。DOM 树描述了 HTML 标签的属性,以及标签之间的嵌套关
在使用 nuxt 时,nuxt 可以使用 usefetch 进行请求,底层的实现是 ofetch 这个库,这个库支持在服务器端和客户端进行请求,nuxt 做了优化,如果服务器端有请求过的数据会序列化传输到客户端,这样客户端在水合时就不用再发起请求。而在使用 nextjs 时,使用的是 fetch 进行请求,nextjs 对 fetch 进行了扩展,增加了缓存的功能,但是我发现这个扩展的 fetch