vue插槽到底是如何运作的

2020/3/1 11:44:43

本文主要是介绍vue插槽到底是如何运作的,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

Vue插槽原理

插槽作为vue重要内容分发手段,很多同学对它的原理比较感兴趣,下面我们来探究一下。

测试代码

<!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出现,并没有什么特别

(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组件实例化时会怎么做,看一下相关代码:

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

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

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

那么谁在使用$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指令时,

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

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

(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,结构如下:

我们知道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把作用域插槽转换为函数形式在子组件中调用了。

还有哪些疑问没有解答,欢迎大家留言。



这篇关于vue插槽到底是如何运作的的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程