Vue3 和 JSX -slot 的一些问题
JSX 和 slot 插槽
- slot 是 Vue 发明的概念,为了完善 template 的能力
- slot 是初学者的“噩梦”,特别是:作用域 slot
- 使用 JSX 就容易理解,JSX 本质是 js
使用 JSX 和 slot 实现 tabs 功能
列子,有这样一个 tabs 组件 ./tabs-jsx/Index.vue
html
<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
html
<template>
<slot></slot>
</template>
<script>
export default {
name: 'TabPanel',
props: ['key', 'title'],
};
</script>
关键的在 ./tabs-jsx/Tabs.jsx
中,直接使用 filter
等 js 语法
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
html
<template>
<slot :msg="msg"></slot>
</template>
<script>
export default {
name: 'Child',
data() {
return {
msg: '作用域插槽 Child',
};
},
};
</script>
在父文件 ./scoped-slot-template/Index.vue
通过作用域 slot 拿到子文件的 msg
值
html
<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
函数
js
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
函数交给父组件显示
js
import { defineComponent, ref } from 'vue';
export default defineComponent({
props: ['render'],
setup(props) {
const msgRef = ref('作用域插槽 Child');
return () => {
return <>{props.render(msgRef.value)}</>;
};
},
});