文章目录
写在前面
最近也学习了React
一段时间了,之前都是写Vue
,所以在这个过程中难免就会有一些比较。我也就是一激灵,Vue
的slot
其实蛮神奇的,那React
有slot
吗?
本篇文章你将会了解到:
- 1.
Vue
的插槽使用- a.匿名插槽的使用
- b.具名插槽的使用
- 2.
React
来使用插槽- a.
React
来实现匿名插槽 - b.
React
来实现具名插槽
- a.
- 3.阅读一下
antd
的React
实现 注意
:文章不包括项目搭建,只有核心代码- 项目搭建去看官方文档就可以很快搭起来了的
- 这里不多啰嗦了…
从Vue的角度来认识slot
- 为什么会有
slot
呢?- 你会发现,你写好了一个组件,比如叫
Button
,是个按钮组件,然后你想给Button
一个名字 <Button>我是button</Button>
- 然后你发现
我是button
被吃掉了 - 所以我们就需要匿名插槽来解决这个问题
- 然后你发现
- 你会发现,你写好了一个组件,比如叫
- 看一下
slot
的官方解释<slot>
元素作为组件模板之中的内容分发插槽。<slot>
元素自身将被替换。- 我们组件写好后,想不通过属性就直接传入,就需要提前把位置准备好
从代码角度来认识一下匿名插槽
我们先一个HelloWorld
组件如下,里面实际是一个button
<script setup>
</script>
<template>
<button>
<slot></slot>
</button>
</template>
<style scoped>
</style>
然后我们在App.vue
中来调用
<script setup>
import HelloWorld from './components/HelloWorld.vue'
</script>
<template>
<HelloWorld>
我是按钮
</HelloWorld>
</template>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
因为我们在组件的button
里面放了一个slot
,所以这个位置会空出来用来放输入的插槽内容,那么这个我是按钮
就会被接收
从代码角度认识一下具名插槽
OK!那么现在,我们就对slot
有一个初步的认识了,那么,可以有多个插槽吗?
- 是可以的,每个插槽都有一个名字就可以解决这个问题了
我们改写HelloWorld
组件如下:
<script setup>
</script>
<template>
<div>
<button>
<slot></slot>
</button>
<slot name="41"></slot>
<slot name="42"></slot>
</div>
</template>
<style scoped>
</style>
我们再写一个Children
组件方便来调用具名插槽
<script setup>
const props = defineProps({
msg: String
})
</script>
<template>
<div>
<div>我是子组件</div>
<div>传入的msg是{{ props.msg }}</div>
<div>我被当成一个插槽传入了</div>
</div>
</template>
<style scoped>
</style>
在App.vue
中调用如下:
<script setup>
import HelloWorld from './components/HelloWorld.vue'
import Children from './components/Children.vue';
</script>
<template>
<img alt="Vue logo" src="./assets/logo.png" />
<HelloWorld>
我是按钮
<!-- 匿名插槽还可以写成下面这种形式
<template v-slot>
<div>我是匿名插槽</div>
</template> -->
<template #41>
<Children msg="我是41插槽"></Children>
</template>
<template #42>
<Children msg="我是42插槽"></Children>
</template>
</HelloWorld>
</template>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
#41
是v-slot:41
的缩写- 匿名插槽可以不写
template
直接用的
从React的角度来认识slot
我们先看一下React支不支持上面操作
import './App.css';
function Button () {
return <button></button>
}
function App () {
return (
<div className="App">
<Button>我是button</Button>
</div>
);
}
export default App;
- 你会发现按钮萎缩了,是的,没有插槽功能!
用React
实现一下匿名插槽
- 首先我们要思考,传入的这个
我是按钮
为什么没有实现?- 是没接收到吗?
- 感觉不是,输入的东西应该不会凭空消失!
- 查阅资料我们知道这个值是
props.children
- 是没接收到吗?
- 拿到数据,我们做一次渲染就好了
import './App.css';
function Button (props) {
const { children } = props
return <button>
{children}
</button>
}
function App () {
return (
<div className="App">
<Button>
我是按钮
</Button>
</div>
);
}
export default App;
- 这次
我是按钮
就显示出来了 - 说实话,就这点来说,感觉
react
会让人感觉很清爽
用React
来实现一下具名插槽
- 这个具名好像只能具体定义然后具体传值了
- 实现如下:
import './App.css';
function Button (props) {
const { children, slotTop, slotBottom } = props
return <>
{slotTop()}
<button>
{children}
</button>
{slotBottom()}
</>
}
function slotTop () {
return <div>我是slotTop</div>
}
function slotBottom () {
return <div>我是slotBottom</div>
}
function App () {
return (
<div className="App">
<Button
slotTop={slotTop}
slotBottom={slotBottom}>
我是按钮
</Button>
</div>
);
}
export default App;
对比vue
和react
,谈谈感受
- 我觉得
react
的思维方式更加直接简约- 可能是本人的偏爱吧
- 其实
react
这里实现的具名插槽有一些肤浅- 因为在
vue
中插槽的值是通过template
传入的 - 这里改成了传值
- 之后我们看看
antd
的处理吧
- 因为在
阅读一下antd
的React
实现
- 写得挺复杂
const kids =
children || children === 0
? spaceChildren(children, isNeedInserted() && autoInsertSpace)
: null;
if (linkButtonRestProps.href !== undefined) {
return (
<a {...linkButtonRestProps} className={classes} onClick={handleClick} ref={buttonRef}>
{iconNode}
{kids}
</a>
);
}
- 匿名插槽他这里写得是
{kid}
可以看到上面还有一个{iconNode}
,具体是啥没仔细看 - 然后
kid
是上面的spaceChildren
函数来生成的,我们继续看一层
function spaceChildren(children: React.ReactNode, needInserted: boolean) {
let isPrevChildPure: boolean = false;
const childList: React.ReactNode[] = [];
React.Children.forEach(children, child => {
const type = typeof child;
const isCurrentChildPure = type === 'string' || type === 'number';
if (isPrevChildPure && isCurrentChildPure) {
const lastIndex = childList.length - 1;
const lastChild = childList[lastIndex];
childList[lastIndex] = `${lastChild}${child}`;
} else {
childList.push(child);
}
isPrevChildPure = isCurrentChildPure;
});
// Pass to React.Children.map to auto fill key
return React.Children.map(childList, child =>
insertSpace(child as React.ReactChild, needInserted),
);
}
- 思路解析如下:
- a.用到了数组,soga,所以是可以输入多个的,可以输入
html
标签和组件- 这是我们没有做到的
- b.
string
和number
就直接拼接到上一个children
- c.否则就放到
childList
队列中,这个就应该可以理解为单个组件了
- a.用到了数组,soga,所以是可以输入多个的,可以输入
不再深入探究了,各位道友可以指导一下,个人理解难免会有错误,希望大家批评指正!