vue插槽究竟是如何运做的

Vue插槽原理

插槽做为vue重要内容分发手段,不少同窗对它的原理比较感兴趣,下面咱们来探究一下。html

测试代码

<!DOCTYPE html>
<html>

<head>
    <title>Vue事件处理</title>
    <script src="../../../dist/vue.js"></script>
</head>

<body>
    <div id="demo">
        <h1>插槽处理机制</h1>
        <comp1>
            <span>abc</span>
        </comp1>
    </div>
    <script> // 声明自定义组件 Vue.component('comp1', { template: '<div><slot></slot></div>' }) // 建立实例 const app = new Vue({ el: '#demo' }); console.log(app.$options.render); </script>
</body>
</html>
复制代码

输出结果以下:可见只是做为span的children出现,并无什么特别vue

(function anonymous() {
with(this){return _c('div',{attrs:{"id":"demo"}},[
    _c('h1',[_v("插槽处理机制")]),_v(" "),
    _c('comp1',[_c('span',[_v("abc")])]), 
})
复制代码

插槽处理流程分析

没有使用v-slot指令,此时组件包含内容做为父组件的children出现,内部有没有slot对编译结果没有影响。下一步就是comp1组件实例化时会怎么作,看一下相关代码:node

// core/instance/render.js
vm.$slots = resolveSlots(options._renderChildren, renderContext)
复制代码

resolveSlots()函数从父组件中获取渲染结果VNode,它们被存入default,这就是默认插槽的内容。这里的renderContext就是父组件实例,显然若是有动态内容要从它里面获取。 markdown

这告诉咱们为何咱们能在render函数中访问this.$slots.default获取默认插槽内容app

那么谁在使用$slots中的内容,显然是comp1组件的渲染函数,输出看一下:函数

(function anonymous( ) {
with(this){return _c('div',[_t("default"),_v(" "),
	_t("foo",null,{"abc":"abc from comp"})],2)}
})
复制代码

这里的_t就是renderSlot()的别名,它会用到$slots或$scopedSlots的内容测试

//src/core/instance/render-helpers/render-slot.js
const scopedSlotFn = this.$scopedSlots[name]
let nodes
if (scopedSlotFn) {
  // ...
}
else {
  nodes = this.$slots[name] || fallback
}
复制代码

存在v-slot指令时

若是使用v-slot指令时,this

<comp>
  <template v-slot:default>abc</template>
  <template v-slot:foo="ctx">{{ctx.abc}}</template>
</comp>
复制代码

编译结果将成为做用域插槽形式:spa

(function anonymous( ) {
with(this){return _c('div',{attrs:{"id":"demo"}},[
  _c('h1',[_v("attr update")]),
  _c('comp',{scopedSlots:_u([
    {key:"default",fn:function(){return [_v("abc")]},proxy:true},
    {key:"foo",fn:function(ctx){return [_v(_s(ctx.abc))]}}])})],1)}
})
复制代码

上面的_u是resolveScopedSlots()的别名,v-slot的参数会做为key,值会做为fn函数的参数,好比上面的ctx。根组件首次渲染时会调用该函数,返回做用域插槽描述对象$scopedSlots,结构以下: code

咱们知道ctx来自于子组件,它是怎么传进来的呢?先看一下comp1的渲染函数:

//...
_t("foo",null,{"abc":"abc from comp"})
复制代码

comp1组件的渲染函数调用_t即renderSlot()时会将属性对象做为参数3传递,它们都来自comp1中名称为foo的具名插槽。因此最后返回的结果是renderSlot()的返回值,也就是前面fn的执行结果:

//src/core/instance/render-helpers/render-slot.js
const scopedSlotFn = this.$scopedSlots[name]
nodes = scopedSlotFn(props) 
复制代码

这就解答了为何做用域插槽可以使用子组件中的数据,由于vue把做用域插槽转换为函数形式在子组件中调用了。

总结

你们平时使用插槽时常常会记不住不一样插槽的用法,通过原理分析后咱们可以更清楚的了解到,其实无论匿名插槽、具名插槽仍是做用域插槽,最终的编译结果是一致的。既然如此彻底能够写成相一样子:

<comp1>
    <template v-slot:default>abc</template>
    <template v-slot:foo>foo</template>
</comp1>
复制代码

而后只需记住若是我要的数据是父组件的仍是子组件的,若是是后者就给v-slot设置一个属性对象值:

<comp1>
    <!--foo1来自承载comp1的父组件-->
    <template v-slot:foo>{{foo1}}</template>
    <!--bar1来自comp1-->
    <template v-slot:bar="{bar1}">{{bar1}}</template>
</comp1>
复制代码

还有哪些疑问没有解答,欢迎你们留言。

相关文章
相关标签/搜索