Vue递归组件+Vuex开发树形组件Tree--递归组件的简单实现
写在前面
首先,本篇文章所开发的组件并非一个已经开源的上线组件,所以如果你急于需要一个插件来只做你的项目,那么并不能带给你及时的帮助。这个组件的开发预计写两篇文章,一遍写组件,一篇写组件逻辑。这篇文章也是我自己开发的从无到有的过程,所以它可以为你提供一些tree组件开发的思路,代码写到一定程度,不能完全依赖插件了,有时间可以看看插件源码或者动手去开发,这样真的能加深对技术的掌握程度。
开发过程
1.数据仓库-vuex
2.组件的循环创建-递归组件
需求决定了我的技术选型,项目需求是一个中国各级*列表的选择,后台基于大数据去汇总各级*网站的文章决策,然后交给前端去展示,左侧显示各级*的tree,右侧显示具体文章。因为*数据的不确定性,所以显示*的tree的每一层级也是动态获取的,就比如北京市下级的海淀区*在一小时前可能不存在某某政策文章,那么北京市节点的子节点中就不会包含海淀区这一子节点,但是10分钟前网站发布了一篇文章,那么就需要在北京市中添加海淀区,为了良好的实时性效果,每一级的子节点都不固定。
因此 树状结构的data是变化的而不是在初始化的时候就固定好 的,数据驱动dom的思想,data有多少层级,dom就有多少层级,data不定,所以dom也无法提前定义好层级,决定采用vue的递归组件,组件递归自身实现无限制层级的渲染。需求中每一个节点的tree都具有增删改的功能,因为data是单例的,全局维护着一个data数据源头,增删改也就是操作data第n个子节点的某个子元素,因此采用了vuex作为一个data存放的仓库( 这不是一个组件库,只是一个项目的应用 )。
项目的关键点:
1.动态加载子节点,也就是说页面加载的时候,根节点的 nodes 子节点数组可能是个空数组,每次单击节点去获取子节点数组。
2.全局单例的树节点对象 data 对象,每次得到新的子节点,需要去维护这个对象,因为数据与dom动态绑定,所以每次数据对象内部发生了变化,dom也会重绘。
vue对虚拟dom的映射通过使用diff算法进行了优化,所以不用担心,重绘造成页面闪屏
树 data 数据结构如下:
let data = { id: "01", lable: "*机构", nodes: [ { id: "02", lable: "*机关", nodes: [{ //.. }] }, { id: "03", lable: "直辖市", nodes: [{ //.. //北京市... //天津市.. }] }, ] };
显示成这样:
每一个节点包含id,label,nodes三个属性,nodes往下延伸子节点,一共有多少级不确定根据后台获取得到。
递归组件
一个简单的递归组件的示例如下:
<template> <div class="tree-menu"> //组件内部不断用自身,只要子节点存在就递归调用 <tree-menu v-for='(item, index) of datanodes' :key='index'></tree-menu> </div> </template> <script> export default { name:'treemenu',//组件名称必须写 data() { return { datanodes: { //... } } } } </script>
treemenu.vue声明组件treemenu,并向外暴露,在组件内部调用自身,也就是一个递归的思想,绑定的datanodes有多少层级,那就会递归多少层,因为每一层都有v-for都会循环子节点。绑定具体数据的时候再具体分析。不断调用自身,当然这只是一个例子,实际情况还要改造一下:
新建treemenu.vue,作为显示节点的逻辑组件:
<template> <div class="tree-menu"> <div>{{label}}</div>//节点名称 <tree-menu //如果nodes.length>0就递归显示子节点 v-for="(node, index) of nodes" :key="index" :nodes="node.nodes" //子节点的子节点向下传递 :label="node.label" > </tree-menu> </div> </template> <script> export default { name: "treemenu", props:['label','nodes'],//数据通过pros向下传递,全局唯一数据源 data() { return {}; } }; </script>
创建tree.vue作为节点树的入口:
<template> <div class="tree-alone"> <tree-menu :label="tree.label" :nodes="tree.nodes"></tree-menu> </div> </template> <script> import treemenu from './treemenu.vue' export default { name: 'tree', data() { return { tree: { id: "01", lable: "总层级", nodes: [ { id: "02", label: "层级1", nodes: [{ label: '层级1-1' }] }, { id: "03", label: "层级2", nodes: [] }, ] }; } } }, components: { treemenu } } </script>
在tree.vue入口组件里引入子组件treemenu.vue,并且向子组件传递数据label和nodes,这里data先预定义一个简单的对象,后面会动态获取。
treemenu.vue作为树形菜单组件,负责递归和数据渲染,它会接受来自入口组件传递来的label和nodes数据,进行渲染, 并且继续递归传递nodes和label 。
在从父组件接收nodes和label后,先循环渲染n个tree-menu组件,然后每一个tree-menu又会递归自身,所以数据就这样一层层向下传递nodes=>nodes.node=>nodes.node.node,此时已经完成了数据与dom的绑定,可以修改一下tree.vue中的tree数据源,看一下组件是否动态改变了。现在渲染如下图:
数据正确渲染,现在需要加一些样式和点击事件(展开与收缩),就不做详细介绍了,代码如下: (向下传递depth参数来获取层级索引)
//tree.vue <template> <div class="tree-alone"> <tree-menu :label="tree.label" :nodes="tree.nodes" :depth="0"></tree-menu> </div> </template> //treemenu.vue <template> <div class="tree-menu"> <div :style="indent" @click="togglechildren">{{label}}</div> <div v-if="showchildren"> <tree-menu v-for="(item, index) of nodes" :key="index" :nodes="node.nodes" :label="node.label" :depth="depth + 1" ></tree-menu> </div> </div> </template> <script> export default { name: "treemenu", props: ["label", "nodes"], data() { return { showchildren: false }; }, methods: { togglechildren() { this.showchildren = !this.showchildren; } }, computed: { indent() { return { transform: `translate(${this.depth * 20}px)` }; } } }; </script>
修改后会展开收缩与缩进,一些箭头旋转等样式问题就不详细写了。
总结
以上所述是小编给大家介绍的vue递归组件+vuex开发树形组件tree--递归组件的简单实现,希望对大家有所帮助