重学React基础知识整理——组件间的另类通信“插槽”(五)

什么是插槽

slot是HTML <slot> 元素 , 是 Web 组件内的一个占位符。该占位符可以在后期使用自己的标记语言填充,这样您就可以创建单独的 DOM 树,并将它与其它的组件组合在一起。这个是官方解释看起来很晦涩,用人话说的意思是“Slot插槽是个占位符(就是一个占位置的),它可以和其它组件一起合用,就是他是可以复用,比如一个模态框组件我在底部按钮那里加了个插槽,如果别人引用那个组件,不修改那底部按钮就按组件里设置的使用,如果加上slot的name就是启用插槽对底部按钮进行修改,你可以去掉按钮或者修改按钮的个数都全凭君意”。

template是什么?

各位是不是很奇怪我们说插槽为什么讲template,是不是要说埃尔斯在挂羊头卖狗肉,各位稍安勿躁,听我娓娓道来。在HTML中solt一般与template合用,做成一个个可以复用的组件。

1、<template> 标记用作容纳页面加载时对用户隐藏的 HTML 内容的容器。如果您有一些需要重复使用的 HTML 代码,则可以使用 <template> 标记。也就是说<template>包裹的内容是隐藏的在浏览器中不会渲染出来

2、<template> 中的内容可以稍后使用 JavaScript 呈现。也就是说让<template>包裹的内容在浏览器中渲染需要使用js去创造一个dom节点去挂载到HTML中任意一处dom下就能看得到<template>里的内容了。

一个完整的是slot组件是怎样的?

代码如下所示:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <template id="element-details-template">
        <style>
          details {font-family: "Open Sans Light", Helvetica, Arial, sans-serif }
          .name {font-weight: bold; color: #217ac0; font-size: 120% }
          h4 {
            margin: 10px 0 -8px 0;
            background: #217ac0;
            color: white;
            padding: 2px 6px;
            border: 1px solid #cee9f9;
            border-radius: 4px;
          }
          .attributes { margin-left: 22px; font-size: 90% }
          .attributes p { margin-left: 16px; font-style: italic }
        </style>
        <details>
          <summary>
            <code class="name">&lt;<slot name="element-name">NEED NAME</slot>&gt;</code>
            <i class="desc"><slot name="description">NEED DESCRIPTION</slot></i>
          </summary>
          <div class="attributes">
            <h4>Attributes</h4>
            <slot name="attributes"><p>None</p></slot>
          </div>
        </details>
        <hr>
      </template>
      <element-details>
         <!-- 下面没有加slot的内容p标签在浏览器中渲染不出来 -->
        <p>默认模板----</p>
      </element-details>
      <element-details>
        <!-- 下面没有加slot的内容在浏览器中渲染不出来 -->
        <p>wdfwerwer</p>修改传递下来数据---
        <a slot="attributes"><p>wwwe334ee</p></a>
      </element-details>
      
</body>
<script>
    customElements.define('element-details',
        class extends HTMLElement {
            constructor() {
            super();
            var template = document
                .getElementById('element-details-template')
                .content;
            const shadowRoot = this.attachShadow({mode: 'open'})
                .appendChild(template.cloneNode(true));
        }
        })
</script>
</html>

运行结果如下所示:

image.png
第一个是默认的模板组件显示,没有任何改变渲染出来。
第二个是改了attributes的slot内容,显示了我修改的内容。
在引用的时候组件里包裹的没有加slot属性的内容没有被渲染出来,如我在代码的注释里所描述的一样。
现在说完执行完的结果我们来聊聊组件里<template>里的事,这里面到底干了啥?

1)<template>里的内容没有被js创造成element-details组件前是没有渲染的,不然就会生成三块内容,而不是两块。

2)<template>里有<h4>``<i>等标签和<slot>标签,进行混用,也就是说<slot>标签可以和其他标签混用生成业务组件。

3)<template>]用<slot>标签和<slot>标签的属性生成一个 命名的槽 (named slots)

 <slot name="element-name">
 <slot name="description">
 <slot name="attributes">
<element-details>
        <!-- 下面没有加slot的内容在浏览器中渲染不出来 -->
        <p>wdfwerwer</p>修改传递下来数据---
         <!-- 下面将插槽名赋值给slot的属性内容在浏览器中能渲染出来 -->
        <a slot="attributes"><p>wwwe334ee</p></a>
      </element-details>

在vue中插槽是怎么使用的?

在vue中使用插槽有两种半方式。

1、直接像web component里一样使用<slot>标签包裹。

建立一个<FancyButton>的组件,它的模板代码如下:

<button class="fancy-btn">
  <slot></slot> <!-- 插槽插口 -->
</button>

这个和web component里一样,有一个<slot>标签,这里<slot> 元素是一个插槽的插口,标示了父元素提供的插槽内容将在哪里被渲染。
这里要引用这个FancyButton组件实现这个插槽作用的代码如下所示:

<FancyButton>
  Click me! <!-- 插槽内容 -->
</FancyButton>

这段代码作用如下图所示:

最终渲染出的 DOM 是这样:

<button class="fancy-btn">
  Click me!
</button>

插槽内容还可以是任意合法的模板内容。例如我们可以传入多个元素,甚至是组件,就是我们想插什么内容都可以。

1.5 具名插槽。

表现形式代码如下:

<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

这里的name=“header”,name="footer"就是具名的slot.

使用 JavaScript 函数可能更有助于你来理解具名插槽:

// 传入不同的内容给不同名字的插槽
BaseLayout({
  header: `...`,
  default: `...`,
  footer: `...`
})

// <BaseLayout> 渲染插槽内容到对应位置
function BaseLayout(slots) {
  return (
    `<div class="container">
      <header>${slots.header}</header>
      <main>${slots.default}</main>
      <footer>${slots.footer}</footer>
    </div>`
  )
}
2、v-slot实现插槽功能,它的缩写为#。表示具名插槽或准备接收 prop 的插槽。

代码如下:

   <!-- 具名插槽 -->
   <BaseLayout>
     <template v-slot:header>
       Header content
     </template>

     <template v-slot:default>
       Default slot content
     </template>

     <template v-slot:footer>
       Footer content
     </template>
   </BaseLayout>

   <!-- 接收 prop 的具名插槽 -->
   <InfiniteScroll>
     <template v-slot:item="slotProps">
       <div class="item">
         {{ slotProps.item.text }}
       </div>
     </template>
   </InfiniteScroll>

   <!-- 接收 prop 的默认插槽,并解构 -->
   <Mouse v-slot="{ x, y }">
     Mouse position: {{ x }}, {{ y }}
   </Mouse>

在React中插槽是怎么使用的?

在普通web组件中和vue中的使用都已介绍,在React中呢,又该怎么样呢?
在我们React中组件里直接包裹一些html标签,html标签里的内容会不会显示呢?来我们来上代码:

import React, { Component } from 'react'

export default class MySlot extends Component {
  render() {
    return (
      <div>
        <div>MySlot--Parent</div>
        <Child>
           <p>111111</p> 
        </Child>
      </div>
    )
  }
}

class Child extends Component {
    render() {
      return (
        <div>Child</div>
      )
    }
  }

上面代码运行结果如下图所示:

image.png

由此可见<p>111111</p>并没有渲染出来。那么我们要使它渲染出来,得怎么修改呢?欲知后事如何请听下回。。。别急,我现在道来。组件中props有一个固定的children属性去在Child组件中占一个位,做成插槽。代码如下所示:

import React, { Component } from 'react'

export default class MySlot extends Component {
  render() {
    return (
      <div>
        <div>MySlot--Parent</div>
        <Child>
           <p>111111</p> 
        </Child>
      </div>
    )
  }
}

class Child extends Component {
    render() {
      return (
        <div>
            <div>Child</div>
            {/* vue中有slot这个标签去表示插槽,React中是props属性中固定的children属性表示 */}
            <div>{this.props.children}</div>
        </div>
      )
    }
  }

运行结果:

image.png

由此11111给渲染出来了。

那如果父组件中传递的html比较多呢又会有什么样的效果呢,上代码:

import React, { Component } from 'react'

export default class MySlot extends Component {
  render() {
    return (
      <div>
        <div>MySlot--Parent</div>
        <Child>
           <div><p>111111</p> </div>
           <div><h2>222222</h2> </div>
           <div><i>333333</i> </div>
        </Child>
      </div>
    )
  }
}

class Child extends Component {
    render() {
        console.log('this.props.children',this.props.children)
      return (
        <div>
            <div>Child</div>
            {/* vue中有slot这个标签去表示插槽,React中是props属性中固定的children属性表示 */}
            <h1>this.props.children获取的是一个父组件传递过来的全部dom</h1>
            <div>{this.props.children}</div>
            <h1>类似于vue中的具名插槽显示</h1>
            <div>{this.props.children[0]}</div>
            <div>{this.props.children[2]}</div>
            <div>{this.props.children[2]}</div>
            <div>{this.props.children[1]}</div>
        </div>
      )
    }
  }

上述代码渲染结果如下所示:

image.png

由上图结果可知:

1)this.props.children 获取的是父组件传递过来的整个dom生成的数组,并每个并列的html元素是其的一个元素,上面代码是传递了3个div包裹的元素,所以this.props.children生成了也length为3的数组。
2)在页面中的展示顺序是和Child中this.props.children的数组下标有关和父组件传递过来的顺序没关。

至此插槽的相关知识也总结的七七八八了,我就抛砖引玉了,欢迎各位大佬莅临指导。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值