Vue3 和 JSX -slot 的一些问题
JSX 和 slot 插槽
- slot 是 Vue 发明的概念,为了完善 template 的能力
- slot 是初学者的“噩梦”,特别是:作用域 slot
- 使用 JSX 就容易理解,JSX 本质是 js
使用 JSX 和 slot 实现 tabs 功能
列子,有这样一个 tabs 组件 ./tabs-jsx/Index.vue
<template>
<tabs default-active-key="1" @change="onTabsChange">
<tab-panel key="1" title="title1">
<div>tab panel content 1</div>
</tab-panel>
<tab-panel key="2" title="title1">
<div>tab panel content 2</div>
</tab-panel>
<tab-panel key="3" title="title1">
<div>tab panel content 3</div>
</tab-panel>
</tabs>
</template>
<script>
import TabPanel from "./TabPanel";
import Tabs from "./Tabs";
export default {
name: "index",
components: {TabPanel, Tabs},
methods: {
onTabsChange(key) {
console.log('tab changed', key)
}
},
}
</script>
然后 ./tabs-jsx/TabPanel.vue
有一个 slot
<template>
<slot></slot>
</template>
<script>
export default {
name: "TabPanel",
props: ['key', 'title']
}
</script>
关键的在 ./tabs-jsx/Tabs.jsx
中,直接使用 filter
等 js 语法
import {ref} from "vue";
export default {
name: "Tabs",
props: ['defaultActiveKey'],
emits: ['change'],
setup(props, context) {
const children = context.slots.default()
const titles = children.map(panel => {
const {title, key} = panel.props || {}
return {
title,
key
}
})
// 当前 actKey
const actKey = ref(props.defaultActiveKey)
function changeActKey(key) {
actKey.value = key
context.emit('change', key)
}
// jsx
const render = () => <>
<div>
{/* 渲染 buttons */}
{titles.map(titleInfo => {
const {title, key} = titleInfo
return <button
key={key}
style={{color: actKey.value === key ? 'blue' : '#333'}}
onClick={() => changeActKey(key)}
>{title}</button>
})}
</div>
<div>
{children.filter(panel => {
const {key} = panel.props || {}
// 匹配上,则显示,否则隐藏
return actKey.value === key ? true : false
})}
</div>
</>
return render
}
}
使用 JSX 实现作用域 slot 插槽
首先回顾下作用域 slot 的使用
在 template 中使用作用域 slot 插槽
子文件 ./scoped-slot-template/Child.vue
<template>
<slot :msg="msg"></slot>
</template>
<script>
export default {
name: "Child",
data() {
return {
msg: '作用域插槽 Child'
}
}
}
</script>
在父文件 ./scoped-slot-template/Index.vue
通过作用域 slot 拿到子文件的 msg
值
<template>
<child>
<template v-slot:default="msgProp">
<p>scoped slot template {{ msgProp.msg }}</p>
</template>
</child>
</template>
<script>
import Child from "./Child";
export default {
name: "Index",
components: {Child}
}
</script>
在 JSX 实现作用域 slot 插槽
父文件 ./scoped-slot-jsx/Index.jsx
,传递 render
函数
import {defineComponent} from "vue"
import Child from './Child'
export default defineComponent(() => {
function render(msg) {
return <>{msg}</>
}
return () => {
return <>
<p>scoped slot Jsx <Child render={render}></Child></p>
</>
}
})
在子文件 ./scoped-slot-jsx/Child.jsx
执行 render
函数交给父组件显示
import {defineComponent, ref} from "vue"
export default defineComponent({
props: ['render'],
setup(props) {
const msgRef = ref('作用域插槽 Child')
return () => {
return <>
{props.render(msgRef.value)}
</>
}
}
})