最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Tree组件引发的一些思考

    正文概述 掘金(dengBox)   2021-05-11   489

    概述

    即为无限层级,无限节点。大致应用场景为:文件夹目录,组织架构等带有明显层级关系的业务场景。本文说明了一些自己在实现树组件的一些思考和优化(jsx)。

    // 树形数据
    const treeData = [
      {
        name: '一级 1',
        id: 1,
        children: [
          {
            name: '二级 1-1',
            id: 2,
            children: [
              {
                name: '三级 1-1-1',
                id: 3,
                children: [
                  {
                    name: '四级 1-1-1-1',
                    id: 4,
                    children: [],
                    checked: true,
                    disabled: true
                  },
                  {
                    name: '四级 1-1-1-2',
                    id: 5,
                    children: [],
                    checked: false
                  }
                ]
              }
            ]
          },
          {
            name: '二级 1-2',
            id: 8,
            children: [
              {
                name: '三级 1-2-1',
                id: 9,
                children: [
                  {
                    name: '四级 1-2-1-1',
                    id: 10,
                    children: []
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        name: '一级 2',
        id: 11,
        children: [
          {
            name: '二级 2-1',
            id: 12,
            children: []
          },
          {
            name: '二级 2-2',
            id: 15,
            children: []
          }
        ]
      }
    ]
    

    设计

    每一个节点都可以认为是拥有同样的功能,即 每一个节点都是在渲染相同的组件。从这一点触发,树组件拆分为两个组件,一个是Tree入口组件,一个是Node叶子组件。

    1. Tree组件负责处理公共数据、方法,以及初始化时组件需要内置的一些数据。并向外暴露标准API
    2. Node组件负责渲染真实的节点,并且递归渲染自身。

    优化

    1. 在入口组件初始化时,遍历整个树形数据,为每一个子节点绑定其父节点引用。目的是为了在更新节点选中状态时,获取当前引用,反向遍历更新节点状态
    // Tree.vue 初始化数据
    initData (arr) {
      console.time('init-Data')
      if (this.showCheckbox) {
        arr.forEach(node => {
          const stack = [node]
          while (stack.length !== 0) {
            const item = stack.pop()
            // 选中节点
            if (item.checked) this.commonData.checkedNode.push(item)
            // 包含子节点
            if (this.hasChild(item)) {
              item[this.childKey].forEach(child => {
                child._parent = item._parent ? [...item._parent, item] : [item]
                stack.push(child)
              })
            }
            // 半选按钮
            if (stack.length === 0) this.resetState(item)
          }
        })
      }
      console.timeEnd('init-Data')
      // console.log(arr)
      this.nodeData = arr
    }
    resetState (node) {
      /**
       * 思考:treeData,父节点的半选状态需要根据子节点的状态去展现,
       * 有什么思路可以最快确定所有节点自身的半选状态。
       *   1. treeData无限级嵌套
       *   2. treeData依赖变量x的展现,所以一次递归几乎不可能完成。
       *   3. 如何避免循环更新节点状态导致的性能浪费。
       * 思路:
       *   1. 递归当前Object,找出层级最高的一个节点,并且每个节点保存其父节点引用
       *   2. 根据父节点引用进行反向查找,从而确定每个父级点的状态
       *   3. 一级节点父节点即自身
       */
      // const length = node._parent?.length || 0
      const length = node._parent ? node._parent.length : 0
      for (let i = length - 1; i >= 0; i--) {
        let halfelEction = false
        let cur = node._parent[i]
        let checked = cur.checked
        cur[this.childKey].forEach(child => {
          if (child.checked || child._halfelEction) halfelEction = true
          if (!child.checked) checked = false
        })
        this.$set(cur, 'checked', checked)
        this.$set(cur, '_halfelEction', halfelEction)
        !checked
          ? this.commonData.checkedNode = this.commonData.checkedNode.filter(cNode => cNode[this.nodeKey] !== cur[this.nodeKey])
          : this.commonData.checkedNode.push(cur)
      }
    }
    
    1. 子组件状态变更时,根据变更的状态以及节点自身的属性(是否是父级节点)触发不同的事件,做到父节点向下捕获,子节点向上冒泡
    // Node.js
    checked (val) {
      /**
       * 根据自身的状态不同,向上 && 向下发送事件不同,根据事件类别,减少计算量(考虑子节点含有禁用)
       * 0. 自身是半选 ---- 被动
       *    a: 子级更新,才会导致父级变更为半选的状态
       * 1. 自身是选中 ---- 主动
       *    a: 根据平级所有节点选中状态,判断是否向上发送被动选中事件
       *    b: 向下发送被动选中事件
       * 2. 自身是选中 ---- 被动
       *    a: 重置半选状态
       * 3. 自身是取消 ---- 主动
       *    a: 向上发送半选事件
       * 4. 自身是取消 ---- 被动
       *    a: 重置半选状态
       */
      // !val
      //   ? this.commonData.checkedNode = this.commonData.checkedNode.filter(cNode => cNode[this.nodeKey] !== this.node[this.nodeKey])
      //   : this.commonData.checkedNode.push(this.node)
      // this.node._halfelEction && this.$set(this.node, '_halfelEction', false)
      /**
       * 是否是末级节点
       * 1. 是:
       *     a:直接向上冒泡
       *     b:逐层更新父级节点状态
       * 2. 否:
       *     a:传递捕获找到最末级节点根据此次状态,被动更新末级所有节点状态,
       *     b:逐层更新父级节点状态
       */
      if (this.hasChild(this.node)) {
        this.parentChecked(val)
      } else {
        this.emitEvent('on-child-checked')
      }
      this.emitEvent('on-checked', this.node, this.index)
    }
    

    拓展

    1. 使用v-if去渲染子组件,懒加载
    2. 懒加载时组件的状态怎么同步,何时同步,怎么优化同步?

    链接

    optimization-ui


    起源地下载网 » Tree组件引发的一些思考

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元