vm.$slots和vm.$scopedSlots在渲染函数内的使用
探索 vm.$slots 和 vm.$scopedSlots以下例子是在Vue@2.6.2中运行的。vm.$slots内包含当前组件实例中所有插槽(包括默认插槽、具名插槽、作用域插槽)。<!-- App.vue --><template>&
vm.$slots和vm.$scopedSlots 在渲染函数内的使用
以下例子是在Vue@2.6.2中运行的。
vm.$slots
与 vm.$scopedSlots
是两个与插槽相关的属性。在使用渲染函数替代vue的模板功能时我们可能需要用到这两个属性来实现插槽的功能。
下面利用一个例子来研究 vm.$slots
vm.$scopedSlots
的区别。
<!-- App.vue -->
<template>
<div id="app">
<HelloWorld>
<h1 slot="head" slot-scope="{author}">this is head slot -- {{author}}</h1>
<div>this is default slot</div>
<div slot="foot">this is foot slot</div>
</HelloWorld>
</div>
</template>
<!-- 后面代码 略 -->
<!-- HelloWorld.vue -->
<template>
<div class="hello">
<slot name="head" author="Allen"></slot>
<slot name="aside"></slot>
<slot></slot>
<slot name="foot"></slot>
</div>
</template>
<!-- 后面代码 略 -->
我们在 HelloWorld.vue
内定义三个具名插槽和一个默认插槽。在 App.vue
内实例化HelloWorld组件时,只使用了 head
default
foot
插槽,其中 head
插槽是一个作用域插槽。
然后我们将 vm.$slots
vm.$scopedSlots
打印的到控制台上,chrome浏览器上打印的结果如下:
vm.$slots
观察发现
- 只有被使用到的插槽才会出现在
vm.$slots
内。 - 默认插槽和具名插槽的值是虚拟dom数组(
VNode[]
),作用域插槽对应的值是一个get
方法且属性修饰为不可枚举。
vm.$scopedSlots
观察发现
- 只有被使用到的插槽才会出现在
vm.$scopedSlots
内。 - 这里面多了
$stable
_normalized
属性。具体什么作用,未知。 - 无论是具名插槽、默认插槽、作用域插槽都在,而且值是一个工厂函数,调用后会返回虚拟dom数组(
VNode[]
)。 - 作用域插槽对应的函数内多个参数
scope
,可以通过它将参数传给插槽。 - 所有值都是可枚举的。
vm.$slots在开发中的使用
以下例子来自Vue官方文档,仅做了些许的改动。
简单的例子
<!-- MyHeadline.vue -->
<script>
export default {
name: "MyHeadline",
props: {
level: {
type: Number,
required: true
}
},
render(createElement) {
return createElement("h" + this.level, this.$slots.default);
}
};
</script>
<!-- App.vue -->
<template>
<div id="app">
<MyHeadline :level="1">这是level 1</MyHeadline>
<MyHeadline :level="2">这是level 2</MyHeadline>
<MyHeadline :level="3">这是level 3</MyHeadline>
<MyHeadline :level="4">这是level 4</MyHeadline>
<MyHeadline :level="5">这是level 5</MyHeadline>
<MyHeadline :level="6">这是level 6</MyHeadline>
</div>
</template>
<!-- 后面代码 略 -->
页面渲染的结果:
页面的html结构:
MyHeadline
组件的目的很明显,就是根据传入的 level
属性生成对应级别的h标签。使用vue的模板是很难实现这样的功能的,所以这里用到了vue的渲染函数(其实vue模板最终也是编译成渲染函数)。
进阶例子
<!-- AnchoredHeading.vue -->
<script>
var getChildrenTextContent = function(children) {
return children
.map(function(node) {
return node.children ? getChildrenTextContent(node.children) : node.text;
})
.join("");
};
export default {
name: "AnchoredHeading",
props: {
level: {
type: Number,
required: true
}
},
render(createElement) {
// 创建 kebab-case 风格的ID
var headingId = getChildrenTextContent(this.$slots.default)
.toLowerCase()
.replace(/\W+/g, "-")
.replace(/(^\-|\-$)/g, "");
return createElement("h" + this.level, [
createElement(
"a",
{
attrs: {
name: headingId,
href: "#" + headingId
}
},
this.$slots.default
)
]);
}
};
</script>
<!-- App.vue -->
<template>
<div id="app">
<AnchoredHeading :level="1">chapter 1 love</AnchoredHeading>
<AnchoredHeading :level="2">chapter 2 marry</AnchoredHeading>
<AnchoredHeading :level="3">chapter 3 rival</AnchoredHeading>
<AnchoredHeading :level="4">chapter 4 hate</AnchoredHeading>
<AnchoredHeading :level="5">chapter 5 divorce</AnchoredHeading>
<AnchoredHeading :level="6">chapter 6 真香</AnchoredHeading>
</div>
</template>
<!-- 后面代码 略 -->
页面渲染结果:
页面html结构:
vm.$scopedSlots在开发中的使用
简单例子
<!-- Menu.vue -->
<script>
export default {
name: "Menu",
render(createElement) {
return createElement(
"div",
{ class: "menuBox" },
this.$scopedSlots.default({ title: "? 菜单栏目 ?" })
);
}
};
</script>
<!-- MenuItem.vue -->
<script>
export default {
name: "MenuItem",
render(createElement) {
return createElement("div", { class: "MenuItem" }, this.$slots.default);
}
};
</script>
<!-- App.vue -->
<template>
<div id="app">
<Menu>
<template slot-scope="{title}">
<h4>{{title}}</h4>
<MenuItem>首页</MenuItem>
<MenuItem>产品</MenuItem>
<MenuItem>公司简介</MenuItem>
<MenuItem>联系我们</MenuItem>
</template>
</Menu>
</div>
</template>
<!-- 后面代码 略 -->
在Menu.vue
内我们通过this.$scopedSlots.default({ title: "? 菜单栏目 ?" })
向作用域插槽传了参数({ title: "? 菜单栏目 ?" }
)。然后我们在 App.vue
内使用 <template slot-scope="{title}">
获得传过来的参数(这里用了解构赋值)。
上面只是为了展示vm.$scopedSlots
在渲染函数内的使用,这个例子内的行为有点多此一举。
页面渲染结果:
页面html结构:
在渲染函数内插入作用域插槽
如果我们把上面那个例子里App.vue
的模板写法改成渲染函数的话,我们该如何插入作用域插槽呢?
在模板写法内插入插槽很简单,只要在组件标签内插入即可。在渲染函数内需要用到createElement
方法内的scopedSlots
选项。
<!-- App.vue -->
<script>
import Menu from "./components/Menu.vue";
import MenuItem from "./components/MenuItem.vue";
export default {
name: "app",
components: {
Menu,
MenuItem
},
render(createElement) {
return createElement("div", { attrs: { id: "app" } }, [
createElement("Menu", {
scopedSlots: {
default: ({ title }) => {
return [
createElement("h4", title),
createElement("MenuItem", "首页"),
createElement("MenuItem", "产品"),
createElement("MenuItem", "公司简介"),
createElement("MenuItem", "联系我们")
];
}
}
})
]);
}
};
</script>
这个写法和上面的模板写法的效果是等同的。
更多推荐
所有评论(0)