React项目实战之租房app项目(四)长列表性能优化城市选择模块渲染列表
admin
2024-04-03 07:22:19
0

前言

目录

  • 前言
  • 一、长列表性能优化
    • 1.1 概述
    • 1.2 懒渲染
    • 1.3 可视区渲染(React-virtualized)
  • 二、react-virtualized组件
    • 2.1 概述
    • 2.2 基本使用
  • 三、城市选择模块-渲染城市列表
    • 3.1 让List组件占满屏幕
    • 3.2 渲染城市列表
    • 3.3 渲染右侧索引列表
    • 3.4 滚动城市列表让对应右侧索引高亮显示
    • 3.5 点击右侧索引置顶该索引城市
    • 3.6 点击城市列表实现切换城市功能
  • 总结

一、长列表性能优化

1.1 概述

在展示大型列表和表格数据的时候(城市列表、通讯录、微博等),会导致页面卡顿,滚动不流畅等性能问题
这样就会导致移动设备耗电加快,影响移动设备的电池寿命
产生性能问题的原因:大量DOM节点的重绘和重排
优化方案:

  • 懒渲染
  • 可视区域渲染

1.2 懒渲染

概述:懒加载,常见的长列表优化方案,常见于移动端
原理:每次只渲染一部分,等渲染的数据即将滚动完时,再渲染下面部分
优点:每次渲染一部分数据,速度快
缺点:数据量大时,页面中依然存在大量DOM节点,占用内存过多,降低浏览器渲染性能,导致页面卡顿
使用场景:数据量不大的情况下

1.3 可视区渲染(React-virtualized)

原理: 只渲染页面可视区域的列表项,非可视区域的数据完全不渲染(预加载前面几项和后面几项) ,在滚动列表时动态更新列表项
使用场景: 一次性展示大量数据的情况

二、react-virtualized组件

2.1 概述

react-virtualized是React组件,用来高效渲染大型列表和表格数据
我们使用它来渲染城市列表数据

2.2 基本使用

安装: yarn add react-virtualized
在项目入口文件 index.js 中导入样式文件
拷贝示例代码到我们项目中,示例代码如下:

import { List } from 'react-virtualized';// 列表数据
const list = ['Brian Vaughn'
];
// 渲染每一行的内容
function rowRenderer ({key,         // Unique key within array of rowsindex,       // 索引号isScrolling, // 当前项是否正在滚动中isVisible,   // 当前项在List中是可见的style        // 重点属性:一定要给每一个行数添加该样式
}) {return (key}style={style}>{list[index]}
) }// 渲染list列表 ReactDOM.render(300}// 组件的高度height={300}rowCount={list.length}// 每行的高度rowHeight={20}rowRenderer={rowRenderer}/>,document.getElementById('example') );

三、城市选择模块-渲染城市列表

3.1 让List组件占满屏幕

1、导入 AutoSizer 组件。
2、通过 render-props 模式,获取到 AutoSizer 组件暴露的 width 和 height 属性。
3、设置 List 组件的 width 和 height 属性。
4、设置城市选择页面根元素高度 100% ,让 List 组件占满整个页面。
5、调整样式,让页面不要出现全局滚动条,避免顶部导航栏滚动。

在src/pages/Citylist/index.js中添加如下代码:

        {/* 城市列表 */}{({ width, height }) => (width}height={height}rowCount={list.length}rowHeight={50}rowRenderer={rowRenderer}/>)}
在src/pages/Citylist/index.scss中添加样式代码:.citylist {height: 100%;padding-top: 45px;.navbar {margin-top: -45px;color: #333;background-color: #f6f5f6;}// navbar 标题颜色.am-navbar-title {color: #333;}
}

3.2 渲染城市列表

在src/pages/Citylist/index.js中添加如下代码:

// 索引(A、B等)的高度
const TITLE_HEIGHT = 36
// 每个城市名称的高度
const NAME_HEIGHT = 50// 封装处理字母索引的方法
const formatCityIndex = letter => {switch (letter) {case '#':return '当前定位'case 'hot':return '热门城市'default:return letter.toUpperCase()}
}6 修改 List 组件的 rowHeight 为函数,动态计算每一行的高度(因为每一行高度都不相同)。
export default class CityList extends React.Component {//  1 将获取到的 cityList 和 cityIndex  添加为组件的状态数据。state = {cityList: {},cityIndex: []}//5 修改 rowRenderer 方法中渲染的每行结构和样式。// List组件渲染每一行的方法:rowRenderer = ({key, // Unique key within array of rowsindex, // 索引号isScrolling, // 当前项是否正在滚动中isVisible, // 当前项在 List 中是可见的style // 注意:重点属性,一定要给每一个行数据添加该样式!作用:指定每一行的位置}) => {// 获取每一行的字母索引const { cityIndex, cityList } = this.stateconst letter = cityIndex[index]// 获取指定字母索引下的城市列表数据return (
key} style={style} className="city">
{formatCityIndex(letter)}
{cityList[letter].map(item => (
item.value}>{item.label}
))}
)}// 创建动态计算每一行高度的方法getRowHeight = ({ index }) => {// 索引标题高度 + 城市数量 * 城市名称的高度const { cityList, cityIndex } = this.statereturn TITLE_HEIGHT + cityList[cityIndex[index]].length * NAME_HEIGHT}render() {return (
{/* 顶部导航栏 */}}onLeftClick={() => this.props.history.go(-1)}>城市选择{/* 城市列表 */}{({ width, height }) => (width}height={height}//2 修改 List 组件的 rowCount 为 cityIndex 的长度。rowCount={this.state.cityIndex.length}rowHeight={this.getRowHeight}rowRenderer={this.rowRenderer}/>)}
)} }

3.3 渲染右侧索引列表

在src/pages/Citylist/index.js中添加如下代码:
在state中添加状态activeIndex ,指定当前高亮的索引:

  state = {// 指定右侧字母索引列表高亮的索引号activeIndex: 0}
结构代码:
    {this.renderCityIndex()}
渲染右侧索引的函数:renderCityIndex() {return this.state.cityIndex.map((item,index) => {return (
  • item}>{/*判断一下,如果高亮状态的索引等于当前索引,那么就设置高亮样式*/}this.state.activeIndex == index? 'index-active' : ''}>{item == 'hot' ? '热' : item.toUpperCase()}
  • )}) }

    3.4 滚动城市列表让对应右侧索引高亮显示

    1、给list组件添加onRowsRendered配置项,用于获取当前列表渲染的行信息,在里面就会有相应信息
    2、通过参数 startIndex 获取到 起始行对应的索引号
    3、判断 startIndex 和 activeIndex 不同时候,更新状态 activeIndex为 startIndex

    在src/pages/Citylist/index.js中添加如下代码:

    this.rowRendered}
    />/**
    * 获取滚动时候,相应的数据
    * @param {*} param0
    */
    rowRendered = ({ startIndex }) => {if (this.state.activeIndex !== startIndex) {this.setState({activeIndex: startIndex})}
    }
    

    3.5 点击右侧索引置顶该索引城市

    1、给索引列表绑定点击事件
    2、在点击事件中,通过index获取到当前项索引号
    3、调用List组件的 scrollToRow方法,让List组件滚动到指定行
    3.1、在constructor中,调用React.createRef() 创建ref对象
    3.2、将创建好的ref对象,添加为List组件的ref属性
    3.3、通过ref的current属性,获取到组件实例,再调用组件的scrollToRow方法
    4、设置List组件的scrollToAlignment配置项值为start,保证点击行出现在页面顶部
    5、对于点击索引无法正确定位的问题,调用List组件的 measureAllRows 方法,提前计算高度来解决

    在src/pages/Citylist/index.js中添加核心代码:

    constructor() {...this.listComponent = React.createRef()
    }
    async componentDidMount() {await this.getCityList()// 计算List组件高度this.listComponent.current.measureAllRows()
    }
    renderCityIndex() {return this.state.cityIndex.map((item, index) => {return (
  • item} onClick={() => {// 拿到List组件的实例this.listComponent.current.scrollToRow(index)}}>...
  • )}) } render() {return (
    ...{/* 城市列表 */}{({ width, height }) => {return this.listComponent}scrollToAlignment="start".../>}}...
    ) }

    3.6 点击城市列表实现切换城市功能

    1、给城市列表项绑定事件
    2、判断当前城市是否有房源数据
    3、如果有房源数据,则保存当前城市数据到本地缓存中,并返回上一页
    4、如果没有房源数据,则提示用户:改城市暂无房源数据,不执行任何操作

    在src/pages/Citylist/index.js中添加核心代码:

    const HOST_CITY = ['北京', '上海', '广州', '深圳']
    // 渲染每一行的内容
    rowRenderer({key,         // Unique key within array of rowsindex,       // 索引号isScrolling, // 当前项是否正在滚动中isVisible,   // 当前项在List中是可见的style        // 重点属性:一定要给每一个行数添加该样式
    }) {let letter = this.state.cityIndex[index]let citys = this.state.cityList[letter]return (key}style={style}className="city">
    {this.formatCityIndex(letter)}
    {citys.map(item => {return (// 绑定点击事件,传递城市名称和value
    item.value} onClick={() => this.changeCity(item.label, item.value)}>{item.label}
    )})}
    ) } changeCity = (label, value) => {if (HOST_CITY.indexOf(label) > -1) {// 说明是有房源数据的城市localStorage.setItem('localCity', JSON.stringify({label,value}))this.props.history.go(-1)} else {// 没有房源城市,提示用户Toast.info('当前城市没有房源', 1);} }

    总结

    到目前为止,我们完成了项目的4个部分的代码编写,分别是
    1、项目准备:部署本地接口,脚手架初始化项目,antd-mobile,路由等
    2、项目整体布局:分析两种页面布局,使用嵌套路由实现带TabBar页面布局等
    3、首页模块:租房小组结构布局,数据获取,H5地理定位和百度地图地理定位等
    4、城市选择模块:数据结构处理,长列表性能优化,react-virtualized,索引列表等

    相关内容

    热门资讯

    河南夫妇冒雨移开折断大树,“不... 评论员 陈柯旭 折断的大树能挡住马路,但挡不住普通人身上的微光。 近日,在河南驻马店突降暴雨,一棵大...
    现货黄金跌破4300美元关口 8日,现货黄金盘中跳水,跌破4300美元/盎司,跌超0.6%。 消息面上,据新华社,当地时间7日晚,...
    英法德联合出手! 文丨陆弃 当英国首相斯塔默、法国总统马克龙和德国总理默茨准备与乌克兰总统泽连斯基在伦敦举行会晤时,外...
    两个月来伊以首次互袭,特朗普想... 当地时间6月7日,伊朗向以色列发射了四轮导弹袭击,以回应数小时前以色列对黎巴嫩首都贝鲁特进行的致命空...
    德韩竞标谁能赢? 崔轶亮:难分... 加拿大正在推进“巡逻潜艇项目”,计划采购最多12艘柴电潜艇,以替换预计于2030年代中期退役的4艘“...
    向以色列发射三波导弹后,伊朗威... 据参考消息6月8日报道,6月7日晚,伊朗向以色列发射三波导弹,以回应以色列不断升级对黎巴嫩的军事行动...
    原创 恐... 6月8日,国际足坛再次出现球员在比赛中昏迷的情况。在丹麦同乌克兰的热身赛中,34岁的埃里克森忽然晕倒...
    原创 歼... 这两天,咱们国产隐身机歼-35的外贸版(也就是歼-35AE)算是彻底曝光了,连带着那架编号0001的...
    以色列遭多波导弹袭击!特朗普:... 据美国有线电视新闻网(CNN)报道,当地时间7日,伊朗伊斯兰革命卫队(IRGC)发布声明称,当日用弹...
    原创 W... WB集团的Warmate“滞留弹药,配备气动弹射器,起飞前使用。(WB集团) 波兰军备局与WB集团签...