Vue.js 你需要知道的 scopedSlots
scopedSlots和 slot-scope 的区别?
- 作用相同:都是作用域插槽
- 场景不同:
slot-scope是模板语法,scopedSlots则是编程式语法 - 使用不同:在
<template>中使用slot-scope,在render()函数中使用scopedSlots
在渲染函数中如何使用?
假设我们有一个叫 <base-layout> 的组件,它的模板内容如下:
<div class="child-node"><slot name="header" :text="headerText"></slot><slot :text="defaultText"></slot><slot name="footer" :text="footerText"></slot>
</div>
复制代码
可以看到,div#child-node 容器中有三个插槽,分别是 header, default, footer。正常情况下我们会用一个块级标签分别把他们包裹,这里为了简单起见我没有这么做。接下来我们在渲染函数(render)中重构上面的代码:
<script>
export default {data() {return {headerText: "child header text",defaultText: "child default text",footerText: "child footer text"};},render(h) {return h("div", { class: "child-node" }, [// 相当于 <slot name="header" :text="headerText"></slot>this.$scopedSlots.header({ text: this.headerText }),// 相当于 <slot :text="defaultText"></slot>this.$scopedSlots.default(this.defaultText),this.$scopedSlots.footer({ text: this.footerText })]);}
};
</script>
复制代码
假设我们有一个叫 <scoped-slots> 的父组件。按照模板语法的定义,我们可以使用 slot-scope或者 v-slot 获取插值内容,从而达到自定义内容的效果,这里我们使用 Vue@2.6.x 提供的最新语法 v-slot 的简写形式,来演示一下如何在父组件中使用:
<div class="parent-node">parent content<base-layout><template #header="{ text }"><p style="color: red">{{ text }}</p></template><template #default="text"><!-- 默认内容是个字符串直接输出 --><p style="color: deeppink">{{ slotProp }}</p></template><template #footer="{ text }"><p style="color: orange">{{ text }}</p></template></base-layout>
</div>
复制代码
以上只是模板语法的写法,接下来我们在渲染函数(render)中利用 scopedSlots 属性重构上面的代码:
<script>
import BaseLayout from "./base-layout.vue";
export default {name: "ScopedSlots",components: {BaseLayout},render(h) {return h("div", { class: "parent-node" }, [this.$slots.default,h("base-layout", {scopedSlots: {// 相当于下面代码:// <template #header="props">// <p style="color:red">// {{ props.text }}// </p>// <template>header: props => {return h("p", { style: { color: "red" } }, [props.text]);},default: props => {return h("p", { style: { color: "deeppink" } }, [props]);},footer: props => {return h("p", { style: { color: "orange" } }, [props.text]);}}})]);}
};
</script>
复制代码
在 Jsx 中如何使用?
我们知道,Vue中的大部分语法在 Jsx 中的写法是不一样的,具体看这里,本文不再赘述。但文档中并没有介绍 scopedSlots 的用法,今天我们来看下如何使用。
使用之前我们需要安装解析 Jsx 语法相关的插件:
npm install\babel-plugin-syntax-jsx\babel-plugin-transform-vue-jsx\babel-helper-vue-jsx-merge-props\babel-preset-env\--save-dev
复制代码
接着配置 .babelrc 文件:
{"presets": ["env"],"plugins": ["transform-vue-jsx"]
}
复制代码
最后我们使用 Jsx 语法重构上面 render 函数中的代码:
<script>
import BaseLayout from "./base-layout.vue";
export default {name: "ScopedSlots",render() {return (<div class="parent-node">parent content<BaseLayout{...{scopedSlots: {header: props => {return <p style={{ color: "red" }}>{props.text}</p>;},default: props => {return <p style={{ color: "deeppink" }}>{props}</p>;},footer: props => {return <p style={{ color: "orange" }}>{props.text}</p>;}}}}/></div>);}
};
</script>
复制代码
你会发现跟 render 函数相比起来还是有些不同的:
BaseLayout导入进来可以直接使用- render 函数省略了
h参数。因为 babel-plugin-transform-vue-jsx@3.4.0+ 帮你自动注入了 - 使用拓展运算符的解构赋值
{...{xxx}}语法。低版本的 Babel 需要 babel-plugin-transform-object-rest-spread 做兼容性处理