React有slot吗?

写在前面

最近也学习了React一段时间了,之前都是写Vue,所以在这个过程中难免就会有一些比较。我也就是一激灵,Vueslot其实蛮神奇的,那Reactslot吗?


本篇文章你将会了解到:

  • 1.Vue的插槽使用
    • a.匿名插槽的使用
    • b.具名插槽的使用
  • 2.React来使用插槽
    • a.React来实现匿名插槽
    • b.React来实现具名插槽
  • 3.阅读一下antdReact实现
  • 注意:文章不包括项目搭建,只有核心代码
    • 项目搭建去看官方文档就可以很快搭起来了的
    • 这里不多啰嗦了…

从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>

  • #41v-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;

对比vuereact,谈谈感受

  • 我觉得react的思维方式更加直接简约
    • 可能是本人的偏爱吧
  • 其实react这里实现的具名插槽有一些肤浅
    • 因为在vue中插槽的值是通过template传入的
    • 这里改成了传值
    • 之后我们看看antd的处理吧

阅读一下antdReact实现

  • 写得挺复杂
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.stringnumber就直接拼接到上一个children
    • c.否则就放到childList队列中,这个就应该可以理解为单个组件了

不再深入探究了,各位道友可以指导一下,个人理解难免会有错误,希望大家批评指正!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值