• 10.4 作用域插槽
    • 10.4.1 父组件编译阶段
    • 10.4.2 子组件渲染
    • 10.4.3 思考

    10.4 作用域插槽

    最后说说作用域插槽,我们可以利用作用域插槽让父组件的插槽内容访问到子组件的数据,具体的用法是在子组件中以属性的方式记录在子组件中,父组件通过v-slot:[name]=[props]的形式拿到子组件传递的值。子组件<slot>元素上的特性称为插槽Props,另外,vue2.6以后的版本已经弃用了slot-scoped,采用v-slot代替。

    1. var child = {
    2. template: `<div><slot :user="user"></div>`,
    3. data() {
    4. return {
    5. user: {
    6. firstname: 'test'
    7. }
    8. }
    9. }
    10. }
    11. var vm = new Vue({
    12. el: '#app',
    13. components: {
    14. child
    15. },
    16. template: `<div id="app"><child><template v-slot:default="slotProps">{{slotProps.user.firstname}}</template></child></div>`
    17. })

    作用域插槽和具名插槽的原理类似,我们接着往下看。

    10.4.1 父组件编译阶段

    作用域插槽和具名插槽在父组件的用法基本相同,区别在于v-slot定义了一个插槽props的名字,参考对于具名插槽的分析,生成render函数阶段fn函数会携带props参数传入。即:

    1. with(this){return _c('div',{attrs:{"id":"app"}},[_c('child',{scopedSlots:_u([{key:"default",fn:function(slotProps){return [_v(_s(slotProps.user.firstname))]}}])})],1)}

    10.4.2 子组件渲染

    在子组件编译阶段,:user="user"会以属性的形式解析,最终在render函数生成阶段以对象参数的形式传递_t函数。

    1. with(this){return _c('div',[_t("default",null,{"user":user})],2)}

    子组件渲染Vnode阶段,根据前面分析会执行renderSlot函数,这个函数前面分析过,对于作用域插槽的处理,集中体现在函数传入的第三个参数。

    1. // 渲染slot组件vnode
    2. function renderSlot(
    3. name,
    4. fallback,
    5. props, // 子传给父的值 { user: user }
    6. bindObject
    7. ) {
    8. // scopedSlotFn拿到父组件插槽的执行函数,默认slotname为default
    9. var scopedSlotFn = this.$scopedSlots[name];
    10. var nodes;
    11. // 具名插槽分支
    12. if (scopedSlotFn) { // scoped slot
    13. props = props || {};
    14. if (bindObject) {
    15. if (!isObject(bindObject)) {
    16. warn(
    17. 'slot v-bind without argument expects an Object',
    18. this
    19. );
    20. }
    21. // 合并props
    22. props = extend(extend({}, bindObject), props);
    23. }
    24. // 执行时将子组件传递给父组件的值传入fn
    25. nodes = scopedSlotFn(props) || fallback;
    26. }

    最终将子组件的插槽props作为参数传递给执行函数执行。回过头看看为什么具名插槽是函数的形式执行而不是直接返回结果。学完作用域插槽我们发现这就是设计巧妙的地方,函数的形式让执行过程更加灵活,作用域插槽只需要以参数的形式将插槽props传入便可以得到想要的结果。

    10.4.3 思考

    作用域插槽这个概念一开始我很难理解,单纯从定义和源码的结论上看,父组件的插槽内容可以访问到子组件的数据,这不是明显的子父之间的信息通信吗,在事件章节我们知道,子父组件之间的通信完全可以通过事件$emit,$on的形式来完成,那么为什么还需要增加一个插槽props的概念呢。我们看看作者的解释。

    插槽 prop 允许我们将插槽转换为可复用的模板,这些模板可以基于输入的 prop 渲染出不同的内容

    从我自身的角度理解,作用域插槽提供了一种方式,当你需要封装一个通用,可复用的逻辑模块,并且这个模块给外部使用者提供了一个便利,允许你在使用组件时自定义部分布局,这时候作用域插槽就派上大用场了,再到具体的思想,我们可以看看几个工具库Vue Virtual Scroller, Vue Promised对这一思想的应用。