vue中v-solt的基本用法及总结
具名插槽
<div class="container">
<header>
<!-- 我们希望把页头放这里 -->
</header>
<main>
<!-- 我们希望把主要内容放这里 -->
</main>
<footer>
<!-- 我们希望把页脚放这里 -->
</footer>
</div>
对于这样的情况,<slot>
元素有一个特殊的 attribute:name
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
在向具名插槽提供内容的时候,我们可以在一个 <template>
元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称:(注意这里<template v-slot:header>
v-slot指令必须放在<template>
内)
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
然后会被渲染出来
<div class="container">
<header>
<h1>Here might be a page title</h1>
</header>
<main>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</main>
<footer>
<p>Here's some contact info</p>
</footer>
</div>
作用域插槽
作用域插槽这些理解来源于这个链接
我觉得要从组件之间的数据流向来思考作用域插槽的应用场景。
假设第一个场景,需要你写一个商品卡片组件,并通过循环去展示多个卡片,并且要求能响应每个卡片上的图片或者其他内容的点击事件而跳转到商品详情页,你会怎么写?
我会使用如下的处理方式,首先将商品卡片写成一个组件Commodity.vue,而在CommodityList.vue中用一个v-for来处理商品卡片列表的展示。
<commodity v-for="(item,index) in commodities" @clickCommodity="onCommodityClick"></commodity>
Commodity组件通过$emit像父组件传递clickCommodity事件,并携带商品数据,父组件即可在onCommodityClick方法中得到数据,进行业务处理,这样便完成了一个基本的由子到父的数据传递
如果再往上抽象一下呢?比如我有多个运营栏目,像淘宝首页有“有好货”,“爱逛街”这样两个栏目,每个栏目下都需要有一个商品卡片列表,那么商品卡片列表CommodityList.vue就要抽成组件了。而这个包含多个运营栏目的vue组件我假设它叫ColumnList.vue,在其中通过v-for调用了CommodityList组件。
注意:业务来了,我希望把点击商品卡片的业务放在ColumnList.vue中处理。你们想象一下要怎么做?一种土办法就是商品按钮点击时,Commodity组件 e m i t 通 知 C o m m o d i t y L i s t . v u e , 而 C o m m o d i t y L i s t 接 着 把 事 件 用 emit通知CommodityList.vue,而CommodityList接着把事件用 emit通知CommodityList.vue,而CommodityList接着把事件用emit往上抛,那么ColumnList.vue就能处理这个点击事件了。这样做完全没有问题,但是显得子组件很不纯粹,跟业务都扯上关系了。
那么如何优雅地解决这个问题呢?这个时候,作用域插槽真正派上用场了。
通过作用域插槽将本应该由CommodityList处理的商品卡片点击业务onCommodityClick提升到ColumnList处理。
<el-row :gutter="20">
<el-col :span="12" v-for="(column, index) in columnList" :key="index">
<el-card class="box-card card-column">
<div slot="header" class="clearfix">
<span>{
{column.columnName}}</span>
</div>
<commodity-list :commodities="column.commodityList">
<template slot-scope="scope">
<!-- 这里只需要给Commodity组件传入数据,响应Commodity组件的clickCommodity事件即可。
事件不必携带参数,完全符合父到子的数据流向,而不会发生子组件又给父组件反向发数据的情况 -->
<commodity :modityData="scope.row" @clickCommodity="onCommodityClick(scope.row)"></commodity>
</template>
</commodity-list>
</el-card>
</el-col>
</el-row>
而CommodityList组件内部应该是改造成这样,slot接收来自父组件的商品卡片组件,这里面不涉及关于商品组件的业务,只关注其他业务和布局即可。
<el-row :gutter="20">
<el-col :span="8" v-for="(item, index) in commodities" :key="index" style="margin-top:20px;">
<slot :row="item"></slot>
</el-col>
</el-row>
总结一下,作用域插槽适合的场景是至少包含三级以上的组件层级,是一种优秀的组件化方案!