前端学习----使用vue递归组件完成树形结构
- 1.初步认知
- 1.1MVVM和MVC
- 1.2VUE的一些知识点
- 递归组件
- 展开效果
1.初步认知
近期整理以前杂乱的代码,发现有些还有点乱,就开始梳理并尝试用Vue来代替以前纯js和css制作导航栏之类树形结构,顺便梳理一下Vue的知识
1.1MVVM和MVC
首先讲一下相关的两个模式,
一是MVVM相信很多旁友都有听过,简言之就是M(Model 模型层)可以理解为一堆数据,V(view视图层)也就是我们直观即毫无关键数据的界面,VM(ViewModel 逻辑层)即解析数据用的
二是MVC,其中的M,V跟上述一样,C(control 控制层)即MV交互的中介
M即数据,例如我们从数据库或者文件中读取的数据,这一层不会对数据进行处理
V即视图,通俗的讲可以说就是我们用HTML和CSS构造的界面
C即管理,他负责让MV交互数据,例如用js代码来获取某个标签的值,又或者为某个标签赋值,这里即可理解为C层,但要注意的是并不会对数据处理
所以,从以上看,MVVM模式也同样包含C层,但是,由于太懒散即只负责跟MV说说话,交互数据,所有其他解析工作,对数据处理,诸如我们要对数据筛选或者转换,检查等其他操作都由VM来负责,这也是MVVM和MVC的区别,VM实现一些可重复性使用的业务逻辑,
MVVM基本含有了MVC功能分离的特点,同时具有数据绑定的功能,即在VM进行验证和解析等操作初始化或者修改数据后,最小化C层(操纵MV)的过程甚至隐藏,简言之就是V绑定了M,当VM对M进行操作时,V也会随之变动,C谦虚的完成了这个传递过程然后继续当吃瓜群众,我想这应该是MVVM里没有提到C的原因之一
1.2VUE的一些知识点
为啥子要废话上面那么多呢?因为,Vue的核心知识就是MVVM,我想也正因为有这一些特殊性才设置vue-cli脚手架来构建项目
先来看看一些属性
new Vue({
el: //挂载目标,选择的DOM元素
props://父组件传递过来的数据,要注意子组件不能对这个数据更改
data: //数据
component: //全局组件,要进行全局注册,能在页面任何位置使用
components: //局部组件,只能在挂载位置使用
methods: //方法,可改变数据
computed: //属性,可以提取数据进行计算然后保存但不能改变数据
created: //初始化
mounted://后续操作,即整个页面完成后的操作
router://使用的路由
watch://监听器
})
再来看看.Vue文件(一般用来构造组件,当然代码也可以放在HTML文档里)
<template>
</template>
<script>
</script>
<style >
</style>
再来联立MVVM模式来进行理解,M数据即上面的data里,V视图就是下面的template和style里实现的界面,然后VM就是script里created或者method等对data数据进行的解析验证计算等操作,C层细节并没有明示出来,也就是隐式的进行了数据绑定,一旦我们对data进行修改,联动下来的子元素里的data数据也会进行改变,也就是C哥在背后默默的付出,而我们不用知道
递归组件
在会用构建完项目后自定义组件,新建文件名为test.vue文件,先放数据
var dtest = [{
name: '1',
next: [{
name: '1-2'
},
{
name: '1-3'
},
{
name: '1-4'
},
{
name: '1-5'
},
{
name: '1-6'
}
]
}, {
name: '2'
}, {
name: '3',
next: [{
name: '3-1'
},
{
name: '3-2'
}
]
}
]
以及定义的组件
<template>
<div>
<ul>
<li v-for = "(val,index) in menu" :key = " index">
{{val.name}}
<test :menu='val.next'></test>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'test',
props: ['menu', 'type']
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
在这里通过回调test组件来重复构建,当然可以重复嵌套实现,上下两个方法在只有两层时实现的效果是一样的
<template>
<div>
<ul>
<li v-for = "(val,index) in menu" :key = " index">
{{val.name}}
<div>
<ul>
<li v-for = "(val,index) in val.next" :key = " index">
{{val.name}}
<test :menu='val.next'></test>
</li>
</ul>
</div>
</li>
</ul>
</div>
</template>
当然要注意在HTML文档内调用并传数据,js里代码为
import test from './components/test'
Vue.component('test', test)//注册
new Vue({
el: '#app',
data: {
dtest: dtest
},
component: { test}
})
HTML里也要注意对应,并传送数据
<div id="app">
<test :menu='dtest'></test>//将data中的dtest传送给test中的menu
</div>
当然如果,数据层数不确定,例如文件目录那样的结构,那么前者自然更好
最后的效果图
展开效果
现在在进一步来添加点击展开的效果,分支数量不同,单纯的一个Boolean监听状态显然不够,所以定义一个数组,来监听所有状态,并在li定义@click触发methods里的方法来改变值,
同时要注意的是,**官方定义改变属性并不能改变视图**
,因为只有初始化对象进行双向数据绑定的时候,即修改Object.defineProperty()时添加set/get方法的时候,才会响应更新view。所以,直接修改属性并没有响应这个初始化的过程,也就不能更新,因此也提供方法this.$set方法来操作,简单代码如下:
<template>
<div>
<ul>
<li v-for = "(val,index) in menu" :key = " index" @click="changeshow(index)">
{{val.name}}
<test :menu='val.next' v-if="ifshow[index]"></test>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'test',
props: ['menu', 'type'],
data () {
return {
ifshow: [false]
}
},
methods: {
changeshow (e) {
this.$set(this.ifshow, e, !this.ifshow[e])
}
}
}
</script>
<style scoped>
</style>
vue文件里scoped能够保证文档的CSS属性唯一,即只有在本组件中才会被响应,不怕跟外部其他的重名
设置css之后可以实现下面非常常见的效果