vue中的多级结构(vue-router, slot)
tags: vue2 vue-router slot date: 2020/12/06
从 react 转入 vue, 深切感觉 vue 的官方文档真香.
虽然文档比较详细, 但有一些还是让我摸不着头脑, 问了一些用过 vue 的同学, 也不能给一个好的答案.
比如 3(多) 级路由, 3(多)级之间的 slot 传递。
1. vue-router 的通常使用方式
// router.js
// router 中配置对应的路由
const router = new VueRouter({
routes: [
{path: '/foo', component: Foo, children: [
{
path: '/foo/foo-child',
component: FooChild
}
]},
{path: '/bar', component: Bar},
]
})
// foo.vue
<template>
<div>
foo
</div>
</template>
// App.vue 中对应页面设置 link 以及显示
// router-view 写的地方就会渲染 router 路径指向的 component
<template>
<div id="app">
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/foo/foo-child">Go to Foo-Child</router-link>
<router-link to="/bar">Go to Bar</router-link>
<div>
<router-view :key="key"></router-view>
</div>
</div>
</template>
总结: router-view 写的地方就会渲染 router 路径指向的 component
三级或者更多级别的时候的使用方式
但是我们发现, /foo/foo-child 这个路径仍然只显示 foo, 并不显示我们的 FooChild 的内容.
我们也发现 只是对当前子级的组件进行了显示.
如果按照这个思路, 那么我们需要在 Foo 组件中增加一个 <router-viwe> 来显示三级
// FooChild.vue
<div>
foo-child
<router-view :key="key"></router-view>
</div>
其他多级的路由, 也是同理.
官方介绍: 参考链接 嵌套路由 这里边很好的说明了上面的情况
官方介绍:
<div id="app">
<router-view></router-view>
</div>
默认的一级 routes 配置会渲染在这里边. 对应的 children 内容
肯定是对应组件的 children, 所以由对应的组件负责渲染.
见如下的解释
总结:
router-view 并没有默认实现多层次的渲染, 只是对当前层次的页面进行一级显示, 如果想实现多层次的无线结构, 需要依次进行配置显示.
2. slot 的多级别传递
属性(数据)传递的方法
默认的方法如下:
//一个 slot 的组件, 并传递简单的数据
<template>
<div>
<slot name="header" :data="headerData"></slot>
<slot name="body"></slot>
<slot name="footer"></slot>
</div>
</template>
export default {
data() {
return {
headerData: {
pos: 'header'
}
}
}
}
//一个使用 slot 的页面, 有属性(数据)的传递
<div>
<has-slot>
<div slot="header" slot-scope="{data}">header, {{ data.pos }}</div>
<div slot="body">body</div>
<div slot="footer">footer</div>
</has-slot>
</div>
事件传递
-
认为可以实现的方式: emit 的方式. (不要被误导, 这是不可以的-_-)
//一个 slot 的组件, 并传递简单的数据
<template>
<div @clickdiv="dosomething">
<slot name="header" :data="headerData"></slot>
<slot name="body"></slot>
<slot name="footer"></slot>
</div>
</template>
<script>
export default {
data() {
return {
headerData: {
pos: 'header'
}
}
},
methods: {
// 不起作用
dosomething() {
console.log('dosomething')
}
},
}
</script>
//一个使用 slot 的页面, 有属性(数据)的传递, 和 事件接收
<template>
<div id="app">
<router-link to="/foo">Go to Foo</router-link>
<br/>
<router-link to="/foo/foo-child">Go to Foo-Child</router-link>
<br/>
<router-link to="/bar">Go to Bar</router-link>
<div>
<router-view :key="key"></router-view>
</div>
<div>
<has-slot>
<div slot="header" slot-scope="{data}" @click="$emit('clickdiv')">header, {{ data.pos }}</div>
<div slot="body">body</div>
<div slot="footer">footer</div>
</has-slot>
</div>
</div>
</template>
<script>
import HasSlot from './components/HasSlot'
export default {
name: 'App',
components: {
HasSlot
},
computed: {
key() {
return this.$router.path
}
}
}
</script>
总结:emit 只是对父组件, 即调用本组件的上层组件才会起作用。 所以, 对于 slot 来说, 原来的接受 slot 的组件, 是不知道这个事件的。 所以我们只能通过类似属性传递的方式进行
正确的方式
//通过属性进行传递事件
<template>
<div @clickdiv="dosomething">
<slot name="header"
:data="{data: headerData, evt: callback}"
></slot>
<slot name="body"></slot>
<slot name="footer"></slot>
</div>
</template>
<script>
export default {
data() {
return {
headerData: {
pos: 'header'
}
}
},
methods: {
dosomething() {
console.log('dosomething')
},
callback(param) {
console.log('callback', param)
}
},
}
</script>
//使用的页面
<template>
<div id="app">
<router-link to="/foo">Go to Foo</router-link>
<br/>
<router-link to="/foo/foo-child">Go to Foo-Child</router-link>
<br/>
<router-link to="/bar">Go to Bar</router-link>
<div>
<router-view :key="key"></router-view>
</div>
<div>
<has-slot>
<div slot="header" slot-scope="{data}" >header, {{ data.data + data.evt }}
<div @click="data.evt('outer')">click</div>
</div>
<div slot="body">body</div>
<div slot="footer">footer</div>
</has-slot>
</div>
</div>
</template>
<script>
import HasSlot from './components/HasSlot'
export default {
name: 'App',
components: {
HasSlot
},
computed: {
key() {
return this.$router.path
}
},
methods: {
},
}
</script>
因为js方法也是一个一等公民, 所以方法也是可以通过引用进行传递出来的
slot多级别的传递
上边说了事件和属性的传递, 那么对于多级的传递是怎么进行呢?
其实了解了上边的情况, 多级也是比较好理解的.
正常使用 slot 的时候, 我们是一个组件, slot= 声明组件的时候, 是 <slot name=""> 的方式.
所以三级组件会是这个形式:
<template>
<div @clickdiv="dosomething">
<!--外部声明-->
<slot name="header" :data="{ data: headerData, evt: callback }">
<inner-slot>
<!--内部使用-->
<div slot="inner" slot-scope="{data, evt}">
{{ data + ':' + evt}}
</div>
</inner-slot>
</slot>
<slot name="body"></slot>
<slot name="footer"></slot>
</div>
</template>
总结:
vue 在文档中给出了 slot 的编译后的代码的样子
//vue文档中关于 slot 的描述 function (slotProps) { // 插槽内容 }所以我们知道, 其实 slot 所有的内容只能通过这个参数进行传递, 并且我们根据 emit 的机制, 也应该推算到, 我们通过 emit 是传递不出去这个事件的. 只能向外部使用的父组件进行传递.
代码位置
必须要有总结, 有反思, 并且代码化才能更好的解决问题。
将所有的代码都放在地址如下的项目: