React project actual combat rental app project (4) long list performance optimization & city selection module rendering list

foreword

1. Long list performance optimization

1.1 Overview

When displaying large lists and tabular data (city lists, address books, Weibo, etc.), it will cause performance problems such as page freezes and unsmooth scrolling, which will lead to faster power consumption of mobile devices and affect the battery life
of mobile devices.
The cause of the performance problem: Redrawing and rearranging of a large number of DOM nodes Optimization
scheme:

  • lazy rendering
  • viewport rendering

1.2 Lazy rendering

Overview: Lazy loading, a common long list optimization solution, is common on mobile terminals.
Principle: Only render a part at a time. When the rendered data is about to scroll, then render the following part.
Advantages: Render a part of the data each time, fast
Disadvantage: Data When the amount is large, there are still a large number of DOM nodes on the page, which takes up too much memory, reduces browser rendering performance, and causes page freezes.
Usage scenario: When the amount of data is not large

1.3 Visual area rendering (React-virtualized)

Principle: Only render the list items in the visible area of ​​the page, and do not render the data in the non-visible area at all (preload the first few items and the next few items), and dynamically update the list items when scrolling the list Usage scenario: Display a large amount of data at one
time Condition

Two, react-virtualized components

2.1 Overview

react-virtualized is a React component for efficiently rendering large lists and tabular data
We use it to render city list data

2.2 Basic use

Installation: yarn add react-virtualized
Import the style file in the project entry file index.js
Copy the sample code to our project, the sample code is as follows:

import {
    
     List } from 'react-virtualized';

// 列表数据
const list = [
  'Brian Vaughn'
];
// 渲染每一行的内容
function rowRenderer ({
    
    
  key,         // Unique key within array of rows
  index,       // 索引号
  isScrolling, // 当前项是否正在滚动中
  isVisible,   // 当前项在List中是可见的
  style        // 重点属性:一定要给每一个行数添加该样式
}) {
    
    
  return (
    <div
      key={
    
    key}
      style={
    
    style}
    >
      {
    
    list[index]}
    </div>
  )
}

// 渲染list列表
ReactDOM.render(
  <List
    // 组件的宽度
    width={
    
    300}
    // 组件的高度
    height={
    
    300}
    rowCount={
    
    list.length}
    // 每行的高度
    rowHeight={
    
    20}
    rowRenderer={
    
    rowRenderer}
  />,
  document.getElementById('example')
);

3. City selection module - rendering city list

3.1 Let the List component fill the screen

1. Import the AutoSizer component.
2. Obtain the width and height properties exposed by the AutoSizer component through the render-props mode.
3. Set the width and height properties of the List component.
4. Set the height of the root element of the city selection page to 100%, so that the List component occupies the entire page.
5. Adjust the style so that the page does not have a global scroll bar and avoid scrolling on the top navigation bar.

Add the following code in src/pages/Citylist/index.js:

        {
    
    /* 城市列表 */}
        <AutoSizer>
          {
    
    ({
     
      width, height }) => (
            <List
              width={
    
    width}
              height={
    
    height}
              rowCount={
    
    list.length}
              rowHeight={
    
    50}
              rowRenderer={
    
    rowRenderer}
            />
          )}
        </AutoSizer>
在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 Render the city list

Add the following code in 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 rows
    index, // 索引号
    isScrolling, // 当前项是否正在滚动中
    isVisible, // 当前项在 List 中是可见的
    style // 注意:重点属性,一定要给每一个行数据添加该样式!作用:指定每一行的位置
  }) => {
    
    
    // 获取每一行的字母索引
    const {
    
     cityIndex, cityList } = this.state
    const letter = cityIndex[index]

    // 获取指定字母索引下的城市列表数据
    return (
      <div key={
    
    key} style={
    
    style} className="city">
        <div className="title">{
    
    formatCityIndex(letter)}</div>
        {
    
    cityList[letter].map(item => (
          <div className="name" key={
    
    item.value}>
            {
    
    item.label}
          </div>
        ))}
      </div>
    )
  }

  // 创建动态计算每一行高度的方法
  getRowHeight = ({
     
      index }) => {
    
    
    // 索引标题高度 + 城市数量 * 城市名称的高度
    const {
    
     cityList, cityIndex } = this.state
    return TITLE_HEIGHT + cityList[cityIndex[index]].length * NAME_HEIGHT
  }

  render() {
    
    
    return (
      <div className="citylist">
        {
    
    /* 顶部导航栏 */}
        <NavBar
          className="navbar"
          mode="light"
          icon={
    
    <i className="iconfont icon-back" />}
          onLeftClick={
    
    () => this.props.history.go(-1)}
        >
          城市选择
        </NavBar>


        {
    
    /* 城市列表 */}
        <AutoSizer>
          {
    
    ({
     
      width, height }) => (
            <List
              width={
    
    width}
              height={
    
    height}
              //2 修改 List 组件的 rowCount 为 cityIndex 的长度。
              rowCount={
    
    this.state.cityIndex.length}
              rowHeight={
    
    this.getRowHeight}
              rowRenderer={
    
    this.rowRenderer}
            />
          )}
        </AutoSizer>
      </div>
    )
  }
}

3.3 Render the right index list

Add the following code in src/pages/Citylist/index.js:
add state activeIndex to state, and specify the currently highlighted index:

  state = {
    
    
    // 指定右侧字母索引列表高亮的索引号
    activeIndex: 0
  }
结构代码:

<ul className="city-index">{
    
    this.renderCityIndex()}</ul>
渲染右侧索引的函数:

renderCityIndex() {
    
    
    return this.state.cityIndex.map((item,index) => {
    
    
        return (
            <li className="city-index-item" key={
    
    item}>
                {
    
    /*判断一下,如果高亮状态的索引等于当前索引,那么就设置高亮样式*/}
                <span className={
    
    this.state.activeIndex == index? 'index-active' : ''}>{
    
    item == 'hot' ? '热' : item.toUpperCase()}</span>
            </li>
        )
    })
}

3.4 Scroll the city list to highlight the corresponding index on the right

1. Add the onRowsRendered configuration item to the list component to obtain the row information rendered by the current list, and there will be corresponding information in it. 2. Obtain the index number
corresponding to the start row through the parameter startIndex
3. Determine when the startIndex and activeIndex are different. Update status activeIndex to startIndex

Add the following code in src/pages/Citylist/index.js:

<List
    ...
    onRowsRendered={
    
    this.rowRendered}
/>

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

3.5 Click on the index on the right to set the index city to the top

1. Bind the click event to the index list
2. In the click event, get the index number of the current item through index
3. Call the scrollToRow method of the List component to scroll the List component to the specified row
3.1. In the constructor, call React.createRef () Create a ref object
3.2. Add the created ref object as the ref attribute of the List component.
3.3. Obtain the component instance through the current attribute of the ref, and then call the scrollToRow method of the component.
4. Set the scrollToAlignment configuration item value of the List component Start to ensure that the clicked row appears at the top of the page
5. For the problem that the clicked index cannot be positioned correctly, call the measureAllRows method of the List component to calculate the height in advance to solve the problem

Add the core code in 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 (
            <li className="city-index-item" key={
    
    item} onClick={
    
    () => {
    
    
                // 拿到List组件的实例
                this.listComponent.current.scrollToRow(index)
            }}>
              ...
            </li>
        )
    })
}
render() {
    
    
    return (
        <div className="citylist">
            ...
            {
    
    /* 城市列表 */}
            <AutoSizer>
                {
    
    
                    ({
     
      width, height }) => {
    
    
                        return <List
                            ref={
    
    this.listComponent}
                             scrollToAlignment="start"
                            ...
                        />
                    }
                }
            </AutoSizer>
            ...
        </div>
    )
}

3.6 Click the city list to realize the function of switching cities

1. Bind events to city list items
2. Determine whether the current city has housing data
3. If there is housing data, save the current city data in the local cache and return to the previous page
4. If there is no housing data, Then the user will be prompted: there is no housing data in the changed city, and no operation will be performed

Add the core code in src/pages/Citylist/index.js:

const HOST_CITY = ['北京', '上海', '广州', '深圳']
// 渲染每一行的内容
rowRenderer({
    
    
    key,         // Unique key within array of rows
    index,       // 索引号
    isScrolling, // 当前项是否正在滚动中
    isVisible,   // 当前项在List中是可见的
    style        // 重点属性:一定要给每一个行数添加该样式
}) {
    
    
    let letter = this.state.cityIndex[index]
    let citys = this.state.cityList[letter]
    return (
        <div
            key={
    
    key}
            style={
    
    style}
            className="city"
        >
            <div className="title">{
    
    this.formatCityIndex(letter)}</div>
            {
    
    citys.map(item => {
    
    
                return (
                    // 绑定点击事件,传递城市名称和value
                    <div className="name" key={
    
    item.value} onClick={
    
    () => this.changeCity(item.label, item.value)}>{
    
    item.label}</div>
                )
            })}</div>
    )
}
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);
    }
}

Summarize

So far, we have completed the code writing of 4 parts of the project, which are
1. Project preparation: deploy local interface, scaffold initialization project, antd-mobile, routing, etc.
2. Overall project layout: analyze two page layouts, use Nested routing implementation with TabBar page layout, etc.
3. Homepage module: rental group structure layout, data acquisition, H5 geolocation and Baidu map geolocation, etc. 4.
City selection module: data structure processing, long list performance optimization, react-virtualized, index list etc.

Guess you like

Origin blog.csdn.net/qq_40652101/article/details/127662774