vue组件——slot插槽(个人理解解释)

什么是插槽?

概念:插槽,也就是slot,是组件的一块HTML模板,这块模板显示不显示、以及怎样显示由父组件来决定。

核心是:显示不显示怎样显示

插槽是一块模板,从模板种类的角度来分,其实都可以分为非插槽模板插槽模板两大类。

从模板种类的角度来分,其实都可以分为非插槽模板插槽模板两大类。它的显示与隐藏以及怎样显示由插件自身控制。

插槽模板是slot,它是一个空壳子,因为它显示与隐藏以及最后用什么样的html模板显示由父组件控制。但是插槽显示的位置确由子组件自身决定,slot写在组件template的哪块,父组件传过来的模板将来就显示在哪块

  • 单个插槽 | 默认插槽 | 匿名插槽

单个插槽是vue的官方叫法,但是其实也可以叫它默认插槽,或者与具名插槽相对,我们可以叫它匿名插槽。因为它不用设置name属性。

可以放置在组件的任意位置,但只能有一个。

例子:

父组件

<template>
    <div class="father">
        <h3>这里是父组件</h3>
        <child>
            <div class="tmpl">
              <span>菜单1</span>
              <span>菜单2</span>
              <span>菜单3</span>
              <span>菜单4</span>
              <span>菜单5</span>
              <span>菜单6</span>
            </div>
        </child>
    </div>
</template>

子组件

<template>
    <div class="child">
        <h3>这里是子组件</h3>
        <slot></slot>
    </div>
</template>

渲染结果为

<template>
    <div class="father">
        <h3>这里是父组件</h3>
        <div class="child">
            <h3>这里是子组件</h3>
            <div class="tmpl">
              <span>菜单1</span>
              <span>菜单2</span>
              <span>菜单3</span>
              <span>菜单4</span>
              <span>菜单5</span>
              <span>菜单6</span>
            </div>
        </div>
    </div>
</template>

它将父组件child标签包括的内容全部替换为了子组件的内容,在有name的情况下父组件内的内容会自动根据子组件中的slot的name互相对应,但这里没有设置name,所以它就自动将没有对应的全部显示在匿名插槽中。也就是<slot></slot>标签中。

 

  • 具名插槽

具名插槽加了name属性,它可以在一个组件中出现N次。出现在不同的位置。

下面的例子,就是一个有两个具名插槽单个插槽的组件,这三个插槽被父组件用同一套css样式显示了出来,不同的是内容上略有区别。

父组件

<template>
  <div class="father">
    <h3>这里是父组件</h3>
    <child>
      <div class="tmpl" slot="up">
        <span>菜单1</span>
        <span>菜单2</span>
        <span>菜单3</span>
        <span>菜单4</span>
        <span>菜单5</span>
        <span>菜单6</span>
      </div>
      <div class="tmpl" slot="down">
        <span>菜单-1</span>
        <span>菜单-2</span>
        <span>菜单-3</span>
        <span>菜单-4</span>
        <span>菜单-5</span>
        <span>菜单-6</span>
      </div>
      <div class="tmpl">
        <span>菜单->1</span>
        <span>菜单->2</span>
        <span>菜单->3</span>
        <span>菜单->4</span>
        <span>菜单->5</span>
        <span>菜单->6</span>
      </div>
    </child>
  </div>
</template>

子组件

<template>
  <div class="child">
    // 具名插槽
    <slot name="up"></slot>
    <h3>这里是子组件</h3>
    // 具名插槽
    <slot name="down"></slot>
    // 匿名插槽
    <slot></slot>
  </div>
</template>

渲染后为

<template>
  <div class="father">
    <h3>这里是父组件</h3>
    


  <div class="child">
    // 具名插槽
    <slot name="up">
      <div class="tmpl" slot="up">
        <span>菜单1</span>
        <span>菜单2</span>
        <span>菜单3</span>
        <span>菜单4</span>
        <span>菜单5</span>
        <span>菜单6</span>
      </div>
    </slot>
    <h3>这里是子组件</h3>
    // 具名插槽
    <slot name="down">
        <div class="tmpl" slot="down">
        <span>菜单-1</span>
        <span>菜单-2</span>
        <span>菜单-3</span>
        <span>菜单-4</span>
        <span>菜单-5</span>
        <span>菜单-6</span>
      </div>
    </slot>
    // 匿名插槽
    <slot>
        <div class="tmpl">
        <span>菜单->1</span>
        <span>菜单->2</span>
        <span>菜单->3</span>
        <span>菜单->4</span>
        <span>菜单->5</span>
        <span>菜单->6</span>
      </div>
    </slot>
  </div>
  </div>
</template>

它根据slot插槽不同的name来不同渲染,最后把没有name的分配到匿名插槽中。

  • 作用域插槽 | 带数据的插槽

作用域插槽要求,在slot上面绑定数据

例如:

<slot name="up" :data="data"></slot>
 export default {
    data: function(){
      return {
        data: ['zhangsan','lisi','wanwu','zhaoliu','tianqi','xiaoba']
      }
    },
}

前面说了,插槽最后显示不显示是看父组件有没有在child下面写模板,像下面那样。

<child>
   html模板
</child>

 有html模板的情况,就是父组件会往子组件插模板的情况,具体插槽的样式呢,由父组件的html+css共同决定,但是这套的内容(数据)是作用域插槽决定。正因为作用域插槽绑定了一套数据,父组件可以拿来用。

于是,情况就变成了这样:样式父组件说了算,但内容可以显示子组件插槽绑定的

作用域插槽和单个插槽和具名插槽的区别

因为单个插槽和具名插槽不绑定数据,所以父组件是提供的模板要既包括样式由包括内容的,就好像在上面的例子中,你看到的文字内容,“菜单1”,“菜单2”什么的都是父组件自己提供的内容;而作用域插槽父组件只需要提供一套样式(在确实用作用域插槽绑定的数据的前提下)。

例子:

父组件

<template>
  <div class="father">
    <h3>这里是父组件</h3>
    <!--第一次使用:用flex展示数据-->
    <child>
      <template slot-scope="user">
        <div class="tmpl">
          <span v-for="item in user.data">{{item}}</span>
        </div>
      </template>

    </child>

    <!--第二次使用:用列表展示数据-->
    <child>
      <template slot-scope="user">
        <ul>
          <li v-for="item in user.data">{{item}}</li>
        </ul>
      </template>

    </child>

    <!--第三次使用:直接显示数据-->
    <child>
      <template slot-scope="user">
       {{user.data}}
      </template>

    </child>

    <!--第四次使用:不使用其提供的数据, 作用域插槽退变成匿名插槽-->
    <child>
      我就是模板
    </child>
  </div>
</template>

子组件

<template>
  <div class="child">

    <h3>这里是子组件</h3>
    // 作用域插槽
    <slot  :data="data"></slot>
  </div>
</template>

 export default {
    data: function(){
      return {
        data: ['zhangsan','lisi','wanwu','zhaoliu','tianqi','xiaoba']
      }
    }
}

效果如图:

其中代码:

slot-scope="user"中 的"user’是可以自定义的,只是承载data的载体而已。而子组件中的:data="data"中的data是下面的模拟数据,:data不需要声明。

大意:

父子组件编译模版是有作用域的,在父组件是无法直接使用子组件数据的,所以在子组件插槽中使用v-bind(例子中用的是:语法糖)给插槽绑定了子组件的data数据,这样在父组件中可以使用slot-scope=xxx (后面这个xxx和函数形参一样,是个变量,随便什么名字都可以)来使用子组件中的数据了 这样就做到了父组件既决定html模版+样式 ,还使用了子组件中的状态。

 

关于slot-scope解释:从子组件传过来的名为data值在父组件将会被slot-scope接收并命名成为为user的数据。子组件的数据通过slot-scope属性传递到了父组件。

 

补充:

关于父子组件通讯的话,我们常用的是父子组件数据时刻同步的

所以给个例子

这是父组件中的template:

<son :foo="bar" v-on:update="val => bar = val"></son>

在子组件中, 我们通过props声明的方式接收foo并使用

props: {
     foo: [type]
}

同时每当子组件中数据改变的时候,通过

this.$emit('update', newValue)

把参数newValue传递给父组件template中监听函数中的"val"。然后通过

val => bar = val

这个表达式就实现了bar = newValue. 这个时候,我们发现父组件中的关键数据bar被子组件改变(相等)了! 通过数据的双向绑定, 父(组件)可以修改子的数据, 子也可以修改父的数据 Vue提供了sync修饰符简化上面的代码,例如:

<comp :foo.sync="bar"></comp>

会被扩展为:

<comp :foo="bar" @update:foo="val => bar = val"></comp>

然后你需要在子组件中改变父组件数据的时候, 需要触发以下的自定义事件:

this.$emit("update:foo", newValue)

 

注意:

  父可以改变子(数据), 但子不能直接改变父(数据)!, 父中数据的变动只能由它自己决定

 

参考:https://segmentfault.com/a/1190000012996217

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值