欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue3非遞歸渲染Tree組件的初步實現(xiàn)代碼

 更新時間:2024年05月30日 08:26:04   作者:Java小卷  
這篇文章主要介紹了Vue3非遞歸渲染Tree組件的初步實現(xiàn),文中通過代碼示例講解的非常詳細,對大家的學(xué)習(xí)或工作有一定幫助,需要的朋友可以參考下

本節(jié)開始,咱們一起來開發(fā)一個tree組件,通過逐步迭代,使其從光禿禿、毫無生機變?yōu)樯鷻C盎然、裝飾漂亮。

樹結(jié)構(gòu)的定義

首先對于樹結(jié)構(gòu),我們自然想到下面的展現(xiàn)形式:

[
  {
    label: '前端',
    id: 'frontend'
  },
  {
    label: 'java',
    id: 'java',
    expanded: true,
    children: [
      { label: '11', id: '11' },
      { label: '22', id: '22' },
      { label: '33', id: '33' },
      {
        label: '44',
        id: '44',
        expanded: true,
        children: [
          {
            label: 'aaa',
            id: 'aaa',
            expanded: true,
            children: [
              { label: 'a11', id: 'a11' },
              { label: 'a22', id: 'a22' }
            ]
          },
          {
            label: 'bbb',
            id: 'bbb',
            expanded: false,
            children: [
              { label: 'b11', id: 'b11' },
              { label: 'b22', id: 'b22' }
            ]
          }
        ]
      },
      { label: '55', id: '55' },
      { label: '66', id: '66' },
      { label: '77', id: '77' },
      {
        label: 'Java基礎(chǔ)',
        id: 'javaBasic'
      },
      {
        label: 'Java Web',
        id: 'javaWeb',
        children: [
          {
            label: 'ssm',
            id: 'ssm'
          }
        ]
      }
    ]
  },
  {
    label: '數(shù)據(jù)庫',
    id: 'db',
    expanded: false,
    children: [
      {
        label: '關(guān)系型數(shù)據(jù)庫',
        id: 'relationShipDB'
      },
      {
        label: '非關(guān)系型數(shù)據(jù)庫',
        id: 'nonRelationShipDB'
      }
    ]
  }
]

這里,小卷隨便造了些數(shù)據(jù),這是一個帶有children子節(jié)點數(shù)組的嵌套結(jié)構(gòu),此外還具有的屬性:label(標簽)、id(節(jié)點標識id)和expanded(是否展開)。很自然,我們可以為這種嵌套結(jié)構(gòu)的節(jié)點抽取出類型定義,創(chuàng)建一個維護樹節(jié)點類型定義的types.ts文件: src/components/tree/types.ts

// 定義基本的樹節(jié)點類型
export interface ITreeNode {
  label: string // 節(jié)點標簽名
  id?: string // 節(jié)點id,可為空
  children?: ITreeNode[] // 子節(jié)點列表,可為空
  expanded?: boolean //是否展開,空則表示默認折疊
}

這里咱們創(chuàng)建并導(dǎo)出一個ITreeNode類型,其中label字段是非空的,children是自身類型的一個數(shù)組。 有了前面的tree數(shù)據(jù)結(jié)構(gòu)和節(jié)點類型的定義,是不是咱們就可以編寫組件模板來渲染這棵樹了呢?答案是肯定的!我們可以定義一個TreeNode的組件,對其子節(jié)點列表的渲染采用遍歷的方式繼續(xù)遞歸渲染子組件TreeNode,這種思路是開發(fā)具有嵌套的層級結(jié)構(gòu)組件大伙兒所能想到的常規(guī)思路。

咱們這里要介紹的是對嵌套數(shù)據(jù)結(jié)構(gòu)進行扁平化處理后,采用列表的形式來渲染,只不過樹的層級結(jié)構(gòu)是由節(jié)點元素按照層級進行一定的paddingLeft來實現(xiàn)的。隨著后續(xù)的學(xué)習(xí),小伙伴會發(fā)現(xiàn),盡管tree組件的渲染不是遞歸的,但是對鋪平節(jié)點列表之前的拍平處理以及后續(xù)子節(jié)點的計算處理卻依然采用的遞歸的思想呢。

注意

拍平結(jié)構(gòu)避免了樹組件內(nèi)部的遞歸渲染,但帶來的麻煩是,需要開發(fā)者對于子節(jié)點范圍劃定做更多的編程處理。后續(xù)開發(fā)中,小卷會提供思路,如何來簡化這種處理方式。

樹結(jié)構(gòu)拍平處理

現(xiàn)在咱們寫一個工具函數(shù)對之前的嵌套結(jié)構(gòu)進行拍平處理。小伙伴們跟著小卷的思路,咱們先來實現(xiàn)一個最簡單的數(shù)組結(jié)果處理的需求:把一個數(shù)組中的元素復(fù)制到一個新數(shù)組中。創(chuàng)建一個utils.ts的工具函數(shù)編寫文件:

src/components/tree/utils.ts

function copyArr(arr: number[]) {
  return arr.reduce((result, cur) => {
    return result.concat(cur)
  }, [] as number[])
}
const newArr = copyArr([1, 2, 3])
console.log(newArr)

這里我們巧用了Arrayreduce方法,在遍歷每個元素時進行轉(zhuǎn)存的處理,把結(jié)果存入箭頭回調(diào)函數(shù)的第一個參數(shù)result中,而在reduce的第2個參數(shù)中對這個存放轉(zhuǎn)存元素的數(shù)組進行初始化。 現(xiàn)在我們測試這個copyArr函數(shù),可以全局安裝一個ts-node工具來執(zhí)行.ts文件:

npm i -g ts-node@10.9.2

同時,在工程tsconfig.json中加入ts-node配置,啟用對es module的編譯支持:

{
    ...
    "ts-node": {
        "esm": true
    },
    ...
}

測試下,ok!

核心處理函數(shù)

發(fā)散思維

對于嵌套的樹結(jié)構(gòu),我們只需要寫一個函數(shù),接收一個代表當(dāng)前層級的數(shù)組,也就是通過數(shù)組的reduce方法,將當(dāng)前層級的每個節(jié)點放到一個新的數(shù)組中,而對于父節(jié)點的情況,我們遞歸調(diào)用該函數(shù),對其子節(jié)點列表做相同的處理,將得到的拍平的數(shù)組,插入到當(dāng)前父節(jié)點之后即可。

ok!現(xiàn)在我們可以輕易寫出拍平核心處理函數(shù):

src/components/tree/utils.ts

import { ITreeNode } from './types'

export function generateFlatTree(tree: ITreeNode[]): ITreeNode[] {
  return tree.reduce((prev, cur) => {
    if (cur.children) {
      // 遞歸,得到子節(jié)點拍平的數(shù)組
      const children = generateFlatTree(cur.children)
      return prev.concat(cur, children)
    } else {
      return prev.concat(cur)
    }
  }, [] as ITreeNode[])
}

測試一下,ok!

扁平化數(shù)據(jù)結(jié)構(gòu)

現(xiàn)在我們系統(tǒng)拍平后的結(jié)構(gòu)能夠展示父節(jié)點id、所處的層級,并且把嵌套的children屬性移除掉,用一個是否葉子節(jié)點的標記屬性代替,也就是說,咱們要定義一個新的IFlattreeNode結(jié)構(gòu)來替換掉原始的ITreeNode,我們將在原來節(jié)點類型基礎(chǔ)上進行擴展: src/components/tree/types.ts

...
// 擴展層級關(guān)系,用于拍平結(jié)構(gòu)的樹節(jié)點
export interface IFlatTreeNode extends ITreeNode {
  parentId?: string // 父節(jié)點id,若是一級節(jié)點則為空
  level: number // 節(jié)點層級,數(shù)值從1開始
  isLeaf: boolean //是否為葉子節(jié)點
  originalNode: ITreeNode // 指向?qū)?yīng)的原始節(jié)點
}

這里,我們對ITreeNode擴展了4個字段,其中parentId可為空,originalNode的引用有助于對其子節(jié)點做遞歸計算處理。

generateFlatTree函數(shù)進一步完善后導(dǎo)出,方法參數(shù)擴展levelpid參數(shù),使得遞歸時能綁定節(jié)點上下級關(guān)系,對拍平的節(jié)點這里我們從原節(jié)點拷貝出一個對象作為IFlatTreeNode類型,后續(xù)會通過邏輯處理為其擴展屬性賦值,并最終把children屬性移除掉,完善后的核心代碼:

import { IFlatTreeNode, ITreeNode } from './types'

/**
 *
 * @param tree 當(dāng)前層級的節(jié)點列表
 * @param level 表示當(dāng)前節(jié)點所處的層級
 * @param pid 父節(jié)點id
 */
export function generateFlatTree(tree: ITreeNode[], level = 0, pid = ''): IFlatTreeNode[] {
  level++ // 層級加1
  // 巧用數(shù)組的reduce方法對嵌套的樹形結(jié)構(gòu)進行拍平處理,prev為當(dāng)前層級要處理的拍平結(jié)構(gòu)結(jié)果,cur為當(dāng)前遍歷的節(jié)點
  return tree.reduce((prev, cur) => {
    // 拷貝當(dāng)前節(jié)點
    const o = { ...cur } as IFlatTreeNode
    // 綁定關(guān)系
    o.originalNode = cur
    o.level = level
    // 為內(nèi)層節(jié)點設(shè)置父id
    if (level > 1 && pid) {
      o.parentId = pid
    }
    // 判斷當(dāng)前節(jié)點是否存在children,如果存在則遞歸處理
    if (o.children) {
      // 以當(dāng)前節(jié)點作為父節(jié)點,對子節(jié)點列表做遞歸處理,得到內(nèi)部拍平的內(nèi)容
      const children = generateFlatTree(o.children, level, o.id!)
      // 移除嵌套結(jié)構(gòu)
      delete o.children
      // 在已經(jīng)拍平的結(jié)構(gòu)基礎(chǔ)上再拼接當(dāng)前節(jié)點和內(nèi)部拍平節(jié)點
      o.isLeaf = false
      return prev.concat(o, children)
    } else {
      // 葉子節(jié)點,處理會更簡單
      o.isLeaf = true
      return prev.concat(o)
    }
  }, [] as IFlatTreeNode[])
}

通過ts-node進行測試,ok!

Tree組件開發(fā)

有了前面樹結(jié)構(gòu)轉(zhuǎn)換的鋪墊,樹組件的開發(fā)會變得非常簡單!在types.ts中定義組件的data屬性:

import { ExtractPropTypes, PropType } from 'vue'
// 樹數(shù)據(jù)的屬性定義
export const props = {
  data: {
    // 類型為一個元素為ITreeNode類型的數(shù)組
    type: Object as PropType<Array<IFlatTreeNode>>,
    required: true
  }
} as const // 設(shè)置為常量,外部無法修改
// 提取tree組件的屬性定義類型
export type Props = ExtractPropTypes<typeof props>
...

注意,這里節(jié)點的定義類型為IFlatTreeNode,這樣我們可以在外部先完成拍平操作后再給tree組件傳入data屬性即可。

src/components/tree/index.tsx

import { defineComponent } from 'vue'
import { props, Props } from './types'

export default defineComponent({
  name: 'JuanTree',
  props,
  setup(props: Props) {
    // 解構(gòu)出傳入的tree數(shù)據(jù)
    const { data } = props
    return () => {
      return (
        <div class='juan-tree'>
          {/* 相當(dāng)于v-for */}
          {data.map((treeNode) => (
            <div
              key={treeNode.id}
              class='juan-tree-node'
              style={{
                /* 樹的層級縮進 */
                paddingLeft: `${24 * (treeNode.level - 1)}px`
              }}
            >
              {treeNode.label}
            </div>
          ))}
        </div>
      )
    }
  }
})

值得注意的是,這里tsx的元素遍歷的寫法,需要為元素綁定key,以實現(xiàn)節(jié)點變化后的局部dom渲染。這里我們對節(jié)點元素<div>采用了動態(tài)綁定style屬性的方式依據(jù)level來決定其左邊留白的距離。

App組件中應(yīng)用JuanTree組件:

import { defineComponent } from 'vue'
import JuanTree from './components/tree'
import { generateFlatTree } from './components/tree/utils'
import { ITreeNode } from './components/tree/types'

export default defineComponent({
  setup() {
    // 這里數(shù)據(jù)省略
    const treeData = [...] as ITreeNode[]
    return () => {
      // 樹的扁平化處理
      const flatTree = generateFlatTree(treeData)
      return (
        <div class='m-4'>
          <JuanTree class='bg-gray-200' data={flatTree}></JuanTree>
        </div>
      )
    }
  }
})

看下頁面效果,粗略的展示出一棵樹:

實現(xiàn)樹節(jié)點的展開與折疊

思路點撥

實際要渲染的樹結(jié)構(gòu),咱們應(yīng)該排除掉所有expanded不為true的節(jié)點的后代節(jié)點。也就是應(yīng)該有一個方法來計算一個IFlatTreeNode下所有后代節(jié)點的長度,在從上到下對傳入的data進行遍歷時,跳過這些長度的節(jié)點即可得到最終要渲染的樹的列表結(jié)構(gòu)。

這里要計算的后代節(jié)點長度,聰明的小伙伴想到在generateFlatTree工具函數(shù)中,其實基于遞歸調(diào)用得到的結(jié)果,咱們是直接可以利用的。我們不妨打印到控制臺看看:

if (o.children) {
  const children = generateFlatTree(o.children, level, o.id!)
  console.log(o.id + '子節(jié)點長度:' + children.length)
  ...
}

這樣還要啥自行車,咱直接獲取就行了嘛。自然我們可以在IFlatTreeNode類型中擴展一個length屬性:

export interface IFlatTreeNode extends ITreeNode {
  ...
  length: number // 所有子孫節(jié)點的長度
}

generateFlatTree函數(shù)中判斷是父節(jié)點的邏輯中設(shè)置下該屬性:

if (o.children) {
  const children = generateFlatTree(...)
  // 記錄當(dāng)前節(jié)點子代的長度
  o.length = children.length
  ...
}

接著,咱們需要借助于計算屬性對響應(yīng)式的tree數(shù)據(jù)進行計算,得到真正要展示的數(shù)據(jù),自然我們想到在tree組件的setup方法中進行這樣的處理:

// 讓其變?yōu)轫憫?yīng)式數(shù)據(jù)以加入計算屬性的計算
const flatData = ref(data)
// 獲取那些展開的節(jié)點列表
const expandedTree = computed(() => {
  const result = []
  // 循環(huán)列表,跳過那些非expanded
  for (let i = 0; i < flatData.value.length; i++) {
    const item = flatData.value[i]
    // 當(dāng)當(dāng)前節(jié)點處于折疊狀態(tài),它的子節(jié)點應(yīng)該被排除
    if (!item.isLeaf && item.expanded !== true) {
      // 跳過內(nèi)部所有的節(jié)點
      i += item.length
    }
    result.push(item)
  }
  // 得到折疊后的新節(jié)點列表
  return result
})

此時,模板使用的是計算屬性返回的數(shù)據(jù),遍歷的是expandedTree.value

<div ...>
  {expandedTree.value.map((treeNode) => (
    ...
  ))}
</div>

看下效果,ok!正是我們需要的折疊后的效果!

對是否展開的節(jié)點做下標記,在渲染的節(jié)點模板中我們暫且以button元素作為節(jié)點折疊、展開的修飾部件:

<div ...>
  {treeNode.isLeaf ? (
    /* 葉子節(jié)點臨時展示,留出間距,確保同級的父節(jié)點和葉子節(jié)點對齊 */
    <span class='mr-1 inline-block w-[20px]'></span>
  ) : (
    <button class='mr-1 inline-block h-[18px] w-[20px]'>
      {/* 父節(jié)點的展開/折疊操作臨時用+、-代替 */}
      {treeNode.expanded ? <span>-</span> : <span>+</span>}
    </button>
  )}
  {treeNode.label}
</div>

再瞅一眼,good!

最后再來錦上添花,實現(xiàn)展開與折疊效果,給button綁定點擊事件:<button onClick={() => toggleNode(treeNode)} ...>,在TSX的setup方法中聲明下事件處理函數(shù):

const toggleNode = (node: ITreeNode) => {
  // 對展開狀態(tài)取反
  node.expanded = !node.expanded
}

這里對實際為IFlatTreeNode類型的節(jié)點更新expanded屬性,因為expandedTree計算屬性中參與計算的flatData是響應(yīng)式的,而計算屬性返回數(shù)據(jù)列表中的節(jié)點對象來自于傳入的data,節(jié)點對象屬性發(fā)生變化自然會觸發(fā)計算屬性重新計算啦看下效果,杠杠滴

存在的問題

聰明的小伙伴會提出這樣的問題:“小卷,現(xiàn)在咱們的樹節(jié)點是固定的,如果可以動態(tài)增刪節(jié)點,那么IFlatTreeNode節(jié)點的length屬性豈不是不會變化了嘛?!”“非常好的問題!”

咱們后面要進一步使用計算屬性來修復(fù)這個問題,給善于思考的小伙伴一個大大的??

好了!學(xué)到這里,咱們一顆基本的樹組件就“畫”好了,后續(xù)咱們會繼續(xù)逐步迭代來豐富展示效果和交互體驗!大家加油!

以上就是Vue3非遞歸渲染Tree組件的初步實現(xiàn)代碼的詳細內(nèi)容,更多關(guān)于Vue3 Tree組件實現(xiàn)的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • vue中常用的rules驗證方式總結(jié)

    vue中常用的rules驗證方式總結(jié)

    這篇文章主要為大家詳細介紹了vue中常用的幾種表單rules驗證方式,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-10-10
  • vue3利用customRef實現(xiàn)防抖

    vue3利用customRef實現(xiàn)防抖

    防抖就是對于頻繁觸發(fā)的事件添加一個延時同時設(shè)定一個最小觸發(fā)間隔,防抖大家都學(xué)過,但是如何在?Vue3?里中將防抖做到極致呢,下面小編就來和大家詳細講講
    2023-10-10
  • 詳解vue表單驗證組件 v-verify-plugin

    詳解vue表單驗證組件 v-verify-plugin

    本篇文章主要介紹了詳解vue表單驗證組件 v-verify-plugin,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-04-04
  • 淺談Vue.js之初始化el以及數(shù)據(jù)的綁定說明

    淺談Vue.js之初始化el以及數(shù)據(jù)的綁定說明

    今天小編就為大家分享一篇淺談Vue.js之初始化el以及數(shù)據(jù)的綁定說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-11-11
  • Vue實現(xiàn)根據(jù)hash高亮選項卡

    Vue實現(xiàn)根據(jù)hash高亮選項卡

    這篇文章主要為大家詳細介紹了Vue實現(xiàn)根據(jù)hash高亮選項卡,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-05-05
  • vue router 通過路由來實現(xiàn)切換頭部標題功能

    vue router 通過路由來實現(xiàn)切換頭部標題功能

    在做單頁面應(yīng)用程序時,一般頁面布局頭尾兩塊都是固定在布局頁面,中間為是路由入口。這篇文章主要介紹了vue-router 通過路由來實現(xiàn)切換頭部標題 ,需要的朋友可以參考下
    2019-04-04
  • 一款移動優(yōu)先的Solid.js路由solid router stack使用詳解

    一款移動優(yōu)先的Solid.js路由solid router stack使用詳解

    這篇文章主要為大家介紹了一款移動優(yōu)先的Solid.js路由solid router stack使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • Vue-input框checkbox強制刷新問題

    Vue-input框checkbox強制刷新問題

    這篇文章主要介紹了Vue-input框checkbox強制刷新問題,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-04-04
  • Vue組件的Prop命名約定詳解

    Vue組件的Prop命名約定詳解

    這篇文章主要為大家介紹了Vue組件的Prop命名約定詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-08-08
  • Vue集成three.js并加載glb、gltf、FBX、json模型的場景分析

    Vue集成three.js并加載glb、gltf、FBX、json模型的場景分析

    這篇文章主要介紹了Vue集成three.js,并加載glb、gltf、FBX、json模型,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-09-09

最新評論