十三、slot

Vue

# 模板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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

# 编译的render函数

  • 父组件编译后的 render(vm.$options.render)

    ƒ 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)}
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
  • 子组件编译后的 render(vm.$children[0].$options.render)

    ƒ anonymous(
    ) {
    with(this){
    // a标签,属性href
    // slot变成_t 创建插槽,名字是default
    // 里面是一个纯文本节点abc
      return _c('a',{attrs:{"href":'#' + to}},[_t("default",[_v("abc")])],2)}
    }
    
    1
    2
    3
    4
    5
    6
    7
    8

可以看出,slot不对应任何的标签,那么如何判定组件里面显示的内容是什么呢?看源码

  • src/core/instance/render-helpers/index.js里面可以看到_t就是renderSlot,里面是怎么使用的
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
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  • init.js ---> initInternalComponent 中存储 opts._renderChildren --> slot 的内容

  • render.js ---> initRender()

vm.$slots = resolveSlots(options._renderChildren, renderContext)
1

这些最终将vm.$slots转化成了键值对的形式,通过名字可以找到其对应的vnode节点,判断render函数中要有哪个,最终渲染到页面上.

更新时间: 2022-01-26 22:04