美文网首页
React项目实战四

React项目实战四

作者: 小王子__ | 来源:发表于2021-12-06 15:12 被阅读0次

    选择城市列表

    选择城市列表渲染后的界面:

    1,获取并处理城市列表数据
    • 接口返回的数据结构:
    [{"label": "北京", "value": "", "pinyin": "beijing", "short": "bj"}]
    
    • 渲染城市列表的数据格式应该为:
    {a: [{}, {}], b: [{}, ...]}
    
    • 渲染右侧索引的数据格式为:
    ['a', 'b']
    

    示例代码:

      // 获取城市列表
      async getCityList() {
        const res = await axios.get('http://localhost:8080/area/city', {
          params: { level: 1 }
        })
        const {cityList, cityIndex} = formatCityData(res.data.body)
        
        // 将当前定位城市数据添加到cityList中,当前定位城市的索引添加到cityIndex中
        const curCity = await getCurrentCity()
        cityList['#'] = curCity
        cityIndex.unshift('#')
        
        // 获取热门城市数据,并将数据添加到 cityList 中,将索引添加到 cityIndex 中
        const hotRes = await axios.get('http://localhost:8080/area/hot')
        cityList['hot'] = hotRes.data.body
        cityIndex.unshift('hot')
      }
      
      // 数据格式化的方法
      const formatCityData = list => {
        const cityList = {}
        
        // 1,遍历list数组
        list.map(item => {
          // 2,获取每一个城市的首字母
          const firstStr = item.short.substr(0, 1)
          // 3,判断 cityList 中是否有该分类
          if(cityList[firstStr]) {
            // 4,如果有,直接往该分类中 push 数据
            cityList[firstStr].push(item)
          } else {
            // 5,如果没有,就先创建一个数组,然后把当前城市信息添加到数组中
            cityList[firstStr] = [item]
          }
        })
    
        // 获取索引数组
        const cityIndex = Object.keys(cityList).sort()
        return {
          cityList,
          cityIndex
        }
      }
    
    2,当前定位城市数据问题
    • 多个模块需要获取定位城市,这时候我们需要把获取定位城市的方法封装成函数,哪个模块需要直接调用该方法即可
    • 封装思路
      • 1,在utils目录中,创建index.js,在该文件中封装
      • 2,创建并导出获取定位城市的函数,并将定位到的城市存储在本地(localStorage)
      • 3,判断localStorage中是否有定位城市,没有,就直接获取定位城市的方法来获取,并保存在本地,并返回该城市数据。如果有,直接返回本地存储中的城市数据。
    image
    3,长列表性能优化
    • 场景:展示大型列表和表格数据(比如:城市列表、通讯录、微博等),会导致页面卡顿、滚动不流畅等性能问题
    • 产生性能问题的原因:大量DOM节点的重绘和重排
    • 其他原因:老旧设备
    • 其他问题:移动设备耗电加快、影响移动设备电池寿命
    • 优化方案:1,懒渲染 2,可视区域渲染

    1,懒渲染

    • 常见的长列表优化方案,常用于移动端
    • 原理:每次只渲染一部分(比如10条数据),等渲染的数据即将滚动完再渲染下面数据
    • 优点:每次渲染一部分数据,速度快
    • 缺点:数据量大,页面中依然存在大量DOM节点,占用内存多、降低浏览器渲染性能,导致页面可顿
    • 使用场景:数据量不大的情况(比如1000条,具体还要看每条数据的复杂程度)

    2,可视区域渲染(react-virtualized)

    • 原理:只渲染页面可视区域的列表项,非可视区域的数据不渲染,在滚动列表的时候动态更新列表项
    • 使用场景:一次性展示大量数据的情况(比如:大表格、微博、聊天应用等)
    react-virtualized
    • 安装:yarn add react-virtualized
    • 文档
    • 在需要使用的组件里引入
    import {List, AutoSizer} from 'react-virtualized'
    
    城市列表示例代码
    image
    import {getCurrentCity} from '../../utils'
    import {List, AutoSizer} from 'react-virtualized';
    
    const TITLE_HEIGHT = 36
    const NAME_HEIGHT = 50
    
    // 数据格式化的方法
    const formatCityData = list => {
      const cityList = {}
    
      // 获取cityList
      list.map(item => {
        // 获取每一个城市的首字母
        const firstStr = item.short.substr(0, 1)
        // 判断 cityList 中是否有该分类
        if(cityList[firstStr]) {
          cityList[firstStr].push(item)
        } else {
          cityList[firstStr] = [item]
        }
      })
    
      // 获取索引数组
      const cityIndex = Object.keys(cityList).sort()
      return {
        cityList,
        cityIndex
      }
    }
    
    // 封装处理字母索引的方法
    const formatCityIndex = letter => {
      switch (letter) {
        case '#':
          return '当前定位'
        case 'hot':
          return '热门城市'
        default:
          return letter.toUpperCase()
          break
      }
    }
    class CityList extends React.Component {
      constructor(props) {
        super(props)
        this.state = {
          cityList: {},
          cityIndex: [],
          activeIndex: 0
        }
    
        // 创建ref对象
        this.cityListComponent = React.createRef()
      }
      
      async componentDidMount() {
        await this.getCityList()
        // 调用 measureAllRows,提前计算 List 中每一行的高度,实现 scrollToRow 的精确跳转
        // 注意:调用这个方法的时候,需要保证 List 组件中已经有数据了!如果 List 组件中的数据为空,就会导致调用方法报错!
        // 解决:只要保证这个方法是在 获取到数据之后 调用的即可。
        this.cityListComponent.current.measureAllRows()
      }
    
      
      // 获取城市列表
      async getCityList() {
        const res = await axios.get('http://localhost:8080/area/city', {
          params: { level: 1 }
        })
        const {cityList, cityIndex} = formatCityData(res.data.body)
        
        const hotRes = await axios.get('http://localhost:8080/area/hot')
        cityList['hot'] = hotRes.data.body
        cityIndex.unshift('hot')
    
        const curCity = await getCurrentCity()
        cityList['#'] = [curCity]
        cityIndex.unshift('#')
    
        this.setState({cityList, cityIndex})
      }
    
      rowRender = ({
        key, // Unique key within array of rows
        index, // Index of row within collection
        isScrolling, // The List is currently being scrolled
        isVisible, // This row is visible within the List (eg it is not an overscanned row)
        style, // Style object to be applied to row (to position it)
      }) => {
        // 获取每一行的字母索引
        const {cityIndex, cityList} = this.state
        const letter = cityIndex[index]
    
        return (
          <div key={key} style={style} className="city">
            <div className="title">{formatCityIndex(letter)}</div>
            {
              Array.from(cityList[letter]).map(item => <div className="name" key={item.value}>{item.label}</div>)
            }
          </div>
        );
      }
      
      // 封装渲染右侧索引列表的方法
      renderCityIndex() {
        // 获取到 cityIndex,并遍历其,实现渲染
        const {cityIndex, activeIndex} = this.state
        return cityIndex.map((item, index) => <li className="city-index-item" key={item} onClick={() => {
          // index 当前索引
          this.cityListComponent.current.scrollToRow(index)
    
        }}>
        <span className={activeIndex === index ? "index-active" : ""}>{item === 'hot' ? '热' : item.toUpperCase()}</span>
      </li>)
      }
    
      // 创建动态计算每一行高度的方法
      getRowHeight = ({index}) => {
        // 索引标题高度 + 城市数量 * 城市名称的高度
        // TITLE_HEIGHT + cityList[cityIndex[index]].length * NAME_HEIGHT
        const {cityList, cityIndex} = this.state
        return TITLE_HEIGHT + cityList[cityIndex[index]].length * NAME_HEIGHT
      }
    
    
      onRowsRendered = ({startIndex}) => {
        console.log(startIndex)
        if (this.state.activeIndex !== startIndex) {
          this.setState({
            activeIndex: startIndex
          })
        }
      }
      
        render() {
          return(
            <div className="cityList">
              <NavBar
                mode="light"
                icon={<i className="iconfont icon-back"/>}
                onLeftClick={() => this.props.history.go(-1)}
              >城市选择</NavBar>
    
              <AutoSizer>
                {({width, height}) => (
                  <List
                    ref={this.cityListComponent}
                    width={width}
                    height={height}
                    rowCount={this.state.cityIndex.length}
                    rowRenderer={this.rowRender}
                    rowHeight={this.getRowHeight}
                    scrollToAlignment="start"
                    onRowsRendered={this.onRowsRendered}
                  />
                )}
              </AutoSizer>
    
              {/* 右侧 */}
              <ul className="city-index">
                {this.renderCityIndex()}
              </ul>
            </div>
          )
        }
      }
    
    切换城市

    1, 给城市列表项绑定点击事件

    {
      Array.from(cityList[letter]).map(item => <div className="name" key={item.value} onClick={() => this.changeCity(item)}>{item.label}</div>)
    }
    

    2, 判断当前城市是否有房源数据(只有北、上、广、深有数据)

    3, 有房源,保存当前城市到本地缓存中,并返回上一页

    4,没有则提示用户该城市暂无房源信息

    // 有房源的城市
    const HOUSE_CITY = ['北京', '上海', '广州', '深圳']
    changeCity = ({label, value}) => {
      if (HOUSE_CITY.indexOf(label) > -1) {
        // 有
        localStorage.setItem('current_city', JSON.stringify({label, value}))
        this.props.history.go(-1)
      } else {
        Toast.info('该城市暂无房源数据', 1, null ,false)
      }
    }
    

    相关文章

      网友评论

          本文标题:React项目实战四

          本文链接:https://www.haomeiwen.com/subject/tsagxrtx.html