Appearance
十三、slot
模板html
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>compile</title>
</head>
<body>
<div id="app">
<!-- 2.6.0 之后,v-slot必须和 template 一起使用-->
<!-- <route-link to="/abc"><template v-slot:default>hello</template></route-link> -->
<!-- 2.6.0 之前 -->
<!-- <route-link to="/abc"><p slot="default">hello</p></route-link> -->
<route-link to="/abc">hello</route-link>
</div>
<script src="../../dist/vue.js"></script>
<script>
// 子组件
const RouterLink = {
name: 'RouterLink',
props: {
to: {
type: String,
required: true
}
},
// routerLink里如果有内容会替换掉slot里面的内容,如果没有内容就是显示zbc
template: '<a :href="\'#\' + to"><slot name="default">abc</slot></a>'
}
const comp = Vue.component(RouterLink.name, RouterLink)
// 父组件
const vm = new Vue({
el: "#app"
})
</script>
</body>
</html>
编译的render函数
父组件编译后的 render(vm.$options.render)
jsƒ anonymous( ) { with(this){ // 创建一个div,属性是id,值是app // 里面有内容,是router-link,自定义组件会调用createComponent创建,属性是to,值是/abc // hello是纯文本节点 return _c('div',{attrs:{"id":"app"}},[_c('router-link',{attrs:{"to":"/abc"}},[_v("hello")])],1)} }
子组件编译后的 render(vm.$children[0].$options.render)
jsƒ anonymous( ) { with(this){ // a标签,属性href // slot变成_t 创建插槽,名字是default // 里面是一个纯文本节点abc return _c('a',{attrs:{"href":'#' + to}},[_t("default",[_v("abc")])],2)} }
可以看出,slot不对应任何的标签,那么如何判定组件里面显示的内容是什么呢?看源码
- src/core/instance/render-helpers/index.js里面可以看到
_t
就是renderSlot,里面是怎么使用的
js
export function renderSlot (
name: string,
// vnode数组
fallback: ?Array<VNode>,
props: ?Object,
bindObject: ?Object
): ?Array<VNode> {
const scopedSlotFn = this.$scopedSlots[name]
let nodes
// 作用域插槽
if (scopedSlotFn) { // scoped slot
...
} else {
// 重点重点重点
// 根据插槽的名字获取值,如果没有值就使用fallback,fallback的值就是slot定义时候的默认值
// this.$slots[name] 指的就是 [_v("hello")]
// fallback 指的就是 [_v("abc")]
nodes = this.$slots[name] || fallback
}
const target = props && props.slot
if (target) {
return this.$createElement('template', { slot: target }, nodes)
} else {
return nodes
}
}
init.js ---> initInternalComponent 中存储 opts._renderChildren --> slot 的内容
render.js ---> initRender()
js
vm.$slots = resolveSlots(options._renderChildren, renderContext)
这些最终将vm.$slots转化成了键值对的形式,通过名字可以找到其对应的vnode节点,判断render函数中要有哪个,最终渲染到页面上.