使用事件代理实现vue的手风琴组件

1.为什么要使用事件代理?

在项目中要做一个手风琴组件,需求是页面created事件中请求数据,以显示在列表中,加载数据时显示“正在加载”,没有数据了就显示“没有更多数据了”,当点击列表项时再去请求相应的详情,如果给每一个li绑定相应的事件处理程序,在JavaScript中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能,因为需要不断的与dom节点进行交互,访问dom的次数越多,引起浏览器重绘与重排的次数也就越多,就会延长整个页面的交互就绪时间,这就是为什么性能优化的主要思想之一就是减少DOM操作的原因;如果要用事件委托,就会将所有的操作放到js程序里面,与dom的操作就只需要交互一次,这样就能大大的减少与dom的交互次数,提高性能;每一个函数都是一个对象,是对象就会占用内存,所以有很多个li就会导致内存占用率大,性能也会越来越差,所以我们在此使用事件代理来执行事件。

2.什么是事件代理 ?

事件代理是根据事件冒泡原理来实现的,事件冒泡即从事件的最深节点开始 ,然后逐步向上传播事件,比如给一个对象绑定了oncllick事件,如果此对象上没有绑定onclick事件就会向此对象的父元素传播,从里到外,直至它被处理(父级对象所有同类事件也会被激活),所以如果我们给父元素绑定事件,那么给里边的子元素即嵌套子元素做onclick事件后,都会冒泡到最外层的父元素上,都会被触发 ,这就是事件委托,委托它们父级代为执行事件。

在此组件中,我们要将li的点击事件绑定给ul,实现加载对应详情项。


3.代码实现


// html:
<ul class="daily daliprob" @click="toggle($event)" >

  // 列表

    <li class="probdate" v-for="(item,index) in listData" >
      <p class="listInner">
        <span>{{item.ctime}}</span>
        <span class="dib earnNumber">{{item.total_interest}}元</span>
        <span class="eranIcon" ></span>
      </p>
// 列表详情项
       <div class="earnDetail" style="display:none" v-bind:index="index">...</div>

</li>

</ul>


// js:

// 引入弹框组件

import * as iakit from 'iakit'

// 引入请求接口
import { queryInterestListInfo, interestDetailInfo } from 'services'

// 引入判断是否是页面底部函数
import isReachBottom from 'helper/isReachBottom'


export default create({
  data() {
    return {

//此处数据为在页面上显示的数据
      listData: [],
      detailInfo: [],
      current: {
        basic: {},
        newcomer: [],
        new_come_rate: '',
        activity: [],
        commission: []
      },
      rate: 0,
      page: 1,
      // 是否有更多数据
      hasMore: true,
      // 是否显示加载更多数据信息
      isShowLoading: true
    }
  },

  created() {
    this.queryInterestList()
    // 为窗体添加滚动事件 RreachBottom,判断是否滑动到了页面底部
    window.addEventListener('scroll', this.RreachBottom)
  },


  methods: {

// 找到li子节点
    getNode(node) {
      if (node.nodeName === 'LI') {
        return node
      } else if (node.nodeName === 'UL') {
        return false
      } else {
        return this.getNode(node.parentNode)
      }
    },


    toggle(e) {
      var target = e.target || e.srcElement
      var $li = document.querySelectorAll('li')
      // thisLi 当前点击li的e.target
      var thisLi = this.getNode(target)
      // 获取所有列表项,生成数组
      var $details = document.querySelectorAll('.earnDetail')
      // 获取所有旋转图标,生成数组
      var $icons = document.querySelectorAll('.eranIcon')
      for (let i = 0, length = $li.length; i < length; i++) {
        if (thisLi === $li[i]) {
          if ($details[i].style.display === '') {
            // div展开时,将它隐藏
            $details[i].style.display = 'none'
            $icons[i].className = 'eranIcon'
          } else {
            // 隐藏时,将它展开
            // 如果当前详情项数组里无对应此项数据
            if (!this.detailInfo[i]) {
              let ctime = thisLi.children[0].children[0].innerHTML
              interestDetailInfo(
                {
                  'data': ctime
                }
              ).then((resp) => {
                // 将请求到的详情项放入详情数组中
                this.detailInfo[i] = resp
                // 将请求详情项赋值给当前详情项,以供显示
                this.current = resp
                if (!this.activity) {
                  this.rate = 0
                } else {
                  this.rate = this.activity.rate
                }
              }).catch((err) => {
                iakit.alert('', err.message, [
                  {
                    text: '朕知道了'
                  }
                ])
              })
            } else {
              // 如果详情项数组中有对应此项数据,就将数组中的数据赋值给当前详情项,以供显示
              this.current = this.detailInfo[i]
            }
            $details[i].style.display = ''
            $icons[i].className = 'eranIcon rotate'
          }
        } else {
          $details[i].style.display = 'none'
          $icons[i].className = 'eranIcon'
        }
      }
    },


    queryInterestList() {
      // 判断数据是否正在加载中,以防重复加载
      if (this.fetching) {
        return
      }
      this.fetching = true
      this.isShowLoading = true
      this.loadingTip = '正在加载中...'
      queryInterestListInfo({
        page: this.page,
        len: 15
      }).then((resp) => {
        const data = resp.list
        // 成功后不显示提示信息,数据加载完毕
        this.isShowLoading = false
        this.fetching = false
        if (data && data.length > 0) { // 存在数据,用concat连接每次请求的数组项
          this.listData = this.listData.concat(data)
        } else { // 没有更多数据了
          this.hasMore = false
          this.isShowLoading = true
          this.loadingTip = '没有更多数据了'
        }
        // 请求页标志加1,即当再次请求时请求下一页
        this.page += 1
      }).catch((err) => {
        this.fetching = false
        iakit.alert('', err.message, [
          {
            text: '朕知道了'
          }
        ])
      })
    },


    RreachBottom() {
      // 如果滚动到页面底部,并且当前选项卡为投资项
      if (isReachBottom()) {
        // 判断接口还有无数据 ,如果有,就再次请求接口
        if (this.hasMore) {
          this.queryInterestList()
        } else {
          // 如果没有数据,就解绑此事件
          window.onscroll = null
        }
      }
    }
  }
})




数据加载中


列表详情项




没有更多数据了


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值