通过对比 vue3 和 vue2 的语法的异同加以练习来学习vue3。来看下 slot 的在 2.x和 3.x的用法。
2.x syntax
在 2.x 中主要掌握这两个的用法。
• $slots
• $scopedSlots
vm.$slots 模版用法
• Type: { [name: string]: ?Array<VNode> }
未指明slot name 定义在$slots.default ,指定 propsName 定义在 $slots.propsName
// fahter
<template>
<Book>
<span>默认值</span>
<span slot="header">header</span>
<span slot="content">content1</span>
<span slot="content">content2</span>
</Book>
</template>
// child
<template>
<div>
<slot>default 1</slot>
<slot name="header">default 2</slot>
<slot name="content">default 3</slot>
</div>
</template>
- slot 默认
- slot name 具名slot,这样我们可以指定多个 slot,并任意排列位置,实现布局需求。
vm.$slots 渲染函数中用法
首先来看下渲染函数的语法
createElement('div',{//...data object},[
'some text',
createElement('h1'),
createElement(MyComponent,{props:{someProp:'foobar'}})
])
• 参数1: Book 组件
• 参数2: 是对第一个参数 {html tag | component} 进行参数设置,具体参考 createElement 参数
• 参数3: children VNodes {String | Array}可以字符串,有多个使用数组的形式
render(h) {
return h(Book,{},[
h('span',{},'默认值'),
h('span',{slot:'header'},'header'),
h('span',{slot:'content'},'content1'),
h('span',{slot:'content'},'content2')
])
}
实现自定义默认值
export default {
render(h) {
return h('div',{},[
this.$slots.default || 'default1',
this.$slots.header || 'default2',
this.$slots.content || 'default3'
])
}
}
vm.$scopedSlots 模版用法
Type: { [name: string]: props => Array<VNode> | undefined }
与 $slots 的区别是,$scopedSlots 可以从组件内部向父级作用域传递数据。
<template>
<div>
<Child>
<template v-slot:default="slotProps">
{{ slotProps.user.name }}
</template>
</Child>
</div>
</template>
<template>
<div>
<slot :user="user"></slot>
</div>
</template>
vm.$scopedSlots 渲染函数中用法
- $scopedSlots.default 是一个函数可以想父组件传递数据
- 父组件中通过 scopedSlots 来接收子组件传递的数据,使用方法如下:
// fahter
render(h) {
return h(Book,{
scopedSlots: {
default:function(props){
return [
props.user.name,
<div>text</div>,
'sdfsdf'
]
}
}
})
}
// Book.vue
render(h) {
return h('div',{},[
this.$scopedSlots.default({
user:this.user
})
])
}
3.x syntax
• this.$slots 现在将 slots 作为函数公开 (2.x是一个对象)
• 非兼容:移除 this.$scopedSlots
• 指定函数来定义传递给子组件的 slot 语法:h(Component,{},{header:()=>Component)
,使用方式如下:
// father
<script lang='ts'>
import { h } from 'vue';
import Child from './Child.vue'
export default {
components:{
Child
},
render() {
return h(Child,{},
{
header:(props:{year:number})=>{
return h('div',`this year is ${props.year}`)
},
title:()=> h('h1', {
innerHTML:'标题',
onClick(){
console.log('点击了标题')
},
style:{
color:'#f66'
}
})
}
)
}
}
</script>
// child
<script lang='ts'>
import { h } from 'vue';
export default {
render() {
return h('div',[this.$slots.title(),this.$slots.header({year:2021})])
}
}
</script>
• h 现在全局导入,而不是作为参数传递给渲染函数,可以查看官网详细信息。
• 现在 slot 是指定一个函数(2.x 是直接定义一个VNode,h('span',{slot:'header'},'header'))
• 这里之所以指定一个函数来代替之前 scopedSlot 的功能,这样组件内部可以将参数传递出来
- h(Child,{},{header:Function}) 使用 scopedSlots 时候第三个参数必须为对象
- createElement 参数第三个参数 {String | Array} ,猜测这里必须为对象是想通过 propName 才找到对应的函数来渲染我们指定的 header、title VNode 。如果有多个子元素无法确定是哪个。
h 渲染函数
这里还需要注意的是,在 3.x 中渲染函数的语法有些改动如下:
2.x 语法
在 2.x 中,domProps 包含 VNode props 中的嵌套列表:
// 2.x
{
class: ['button', 'is-outlined'],
style: { color: '#34495E' },
attrs: { id: 'submit' },
domProps: { innerHTML: '' },
on: { click: submitForm },
key: 'submit-button'
}
3.x 语法
在 3.x 中,整个 VNode props 结构是扁平的,使用上面的例子,下面是它现在的样子
// 3.x 语法
{
class: ['button', 'is-outlined'],
style: { color: '#34495E' },
id: 'submit',
innerHTML: '',
onClick: submitForm,
key: 'submit-button'
}