什么是作用域插槽?插槽与作用域插槽的区别

什么是作用域插槽?插槽与作用域插槽的区别一、概念//有name的属于具名插槽,没有name属于匿名插槽<app><divslot="a">xxxx</div><div

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

一、概念

  1.  
    // 有name的属于具名插槽,没有name属于匿名插槽

  2.  
    <app>

  3.  
    <div slot=“a”>xxxx</div>

  4.  
    <div slot=“b”>xxxx</div>

  5.  
    </app>

  6.  
    <slot name=“a”></slot>

  7.  
    <slot name=“b”></slot>

  8.  
     
  9.  
    普通插槽渲染的位置是在它的父组件里面,而不是在子组件里面

  10.  
    作用域插槽渲染是在子组件里面

1.插槽slot

在渲染父组件的时候,会将插槽中的先渲染。

创建组件虚拟节点时,会将组件的儿子的虚拟节点保存起来。当初始化组件时,通过插槽属性将儿 子进行分类 {a:[vnode],b[vnode]}

渲染组件时会拿对应的slot属性的节点进行替换操作。(插槽的作用域为父组件,插槽中HTML模板显示不显示、以及怎样显示由父组件来决定)

有name的父组件通过html模板上的slot属性关联具名插槽。没有slot属性的html模板默认关联匿名插槽。

2.作用域插槽slot-scope

作用域插槽在解析的时候,不会作为组件的孩子节点。会解析成函数,当子组件渲染时,会调用此函数进行渲染。

或者可以说成作用域插槽是子组件可以在slot标签上绑定属性值,在父组件可以拿到子组件的数据,通过子组件绑定数据传递给父组件。(插槽的作用域为子组件)

  1.  
    子组件:

  2.  
    <slot :nickName=
    “‘wthreesix'”></slot>

  3.  
     
  4.  
    父组件:

  5.  
    <slot-child>

  6.  
    <template slot-scope=“scope”>

  7.  
    <div>{{scope.nickName}}</div>

  8.  
    </template>

  9.  
    </slot-child>

二、源码图

什么是作用域插槽?插槽与作用域插槽的区别

三、插槽渲染分析

1.插槽

  1.  
    // 父组件:

  2.  
    const VueTemplateCompiler = require(‘vue-template-compiler’);

  3.  
    let ele = VueTemplateCompiler.compile(`

  4.  
    <my-component>

  5.  
    <div slot=”header”>node</div>

  6.  
    <div>react</div>

  7.  
    <div slot=”footer”>vue</div>

  8.  
    </my-component>

  9.  
    `)

  10.  
     
  11.  
    将上面组件编译后:

  12.  
    /**

  13.  
    with(this) {

  14.  
    return _c(‘my-component’, // _c 创建虚拟dom

  15.  
    [ // 组件中包含子节点

  16.  
    _c(‘div’, // 第一个是一个插槽,插槽叫header

  17.  
    {

  18.  
    attrs: {

  19.  
    “slot”: “header”

  20.  
    },

  21.  
    slot: “header”

  22.  
    },

  23.  
    [_v(“node”)] // 将里面内容存起来了(_v是createTextVNode创建文本节点)

  24.  
    ),

  25.  
    _v(” “),

  26.  
    _c(‘div’,[_v(“react”)]),

  27.  
    _v(” “),

  28.  
    _c(‘div’, {

  29.  
    attrs: {

  30.  
    “slot”: “footer”

  31.  
    },

  32.  
    slot: “footer”

  33.  
    }, [_v(“vue”)])

  34.  
    ]

  35.  
    )

  36.  
    }

  37.  
    */

  38.  
    在调render方法的时候已经将组件全部渲染好了

  39.  
     
  40.  
    // 子组件

  41.  
    let ele = VueTemplateCompiler.compile(`

  42.  
    <div>

  43.  
    <slot name=”header”></slot>

  44.  
    <slot name=”footer”></slot>

  45.  
    <slot></slot>

  46.  
    </div>

  47.  
    `);

  48.  
    /**

  49.  
    with(this) {

  50.  
    // 渲染的时候会找header对应的是谁

  51.  
    return _c(‘div’, [

  52.  
    _t(“header”), _v(” “),

  53.  
    _t(“footer”), _v(” “),

  54.  
    _t(“default”)], 2)

  55.  
    }

  56.  
    当找到就会换过来,如下:

  57.  
    return _c(‘div’, [_v(“node”), _v(” “), _v(“vue”), _v(” “),

  58.  
    _t(“default”)], 2)

  59.  
    }

  60.  
    }

  61.  
    **/

  62.  
    // _t是renderSlot

  63.  
     
  64.  
    插槽就是一个替换的过程,将父组件渲染好的结果直接替换到自己的上面,创建的过程相当于在父组件渲染的

2.作用域插槽

  1.  
    父组件:

  2.  
    let ele = VueTemplateCompiler.compile(`

  3.  
    <app>

  4.  
    <div slot-scope=”msg” slot=”footer”>{{msg.a}}</div>

  5.  
    </app>

  6.  
    `);

  7.  
    /**

  8.  
    with(this) {

  9.  
    // 编译出的不是一个child,而是一个属性scopedSlots

  10.  
    return _c(‘app’, {

  11.  
    scopedSlots: _u([{

  12.  
    key: “footer”,

  13.  
    fn: function (msg) { 将子节点变成一个函数,这个函数不调用就不会去渲染这个子节点

  14.  
    return _c(‘div’, {}, [_v(_s(msg.a))])

  15.  
    }

  16.  
    }])

  17.  
    })

  18.  
    }

  19.  
    }

  20.  
    在初始化的时候并不会渲染子节点

  21.  
    */

  22.  
     
  23.  
    子组件:

  24.  
    const VueTemplateCompiler = require(‘vue-template-compiler’);

  25.  
    VueTemplateCompiler.compile(
    `

  26.  
    // 当我们在写slot去执行的时候进行渲染

  27.  
    <div>

  28.  
    <slot name=”footer” a=”1″ b=”2″></slot>

  29.  
    </div>

  30.  
    `);

  31.  
    /**

  32.  
    with(this) {

  33.  
    // 去找footer,找见会调用上面的那个函数,并且将属性传入到这个函数里面,这时候才会把这节点进行渲染,完成之后替换调

  34.  
    return _c(‘div’, [_t(“footer”, null, {

  35.  
    “a”: “1”,

  36.  
    “b”: “2”

  37.  
    })], 2)

  38.  
    }

  39.  
    **/

  40.  
     
  41.  
    // 作用域插槽的内容会被渲染成一个函数

  42.  
    // 作用域插槽渲染是在当前组件的内部,不是在父组件中

四、源码

1.initRender(初始化render,构建vm.$slots)

  1.  
    export function initRender (vm: Component) {

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

  3.  
    vm.$scopedSlots = emptyObject

  4.  
    }

2.resolveSlots(映射slot名字和对应的vnode)

  1.  
    export function resolveSlots (

  2.  
    children: ?Array<VNode>,

  3.  
    context: ?Component

  4.  
    ): { [key: string]: Array<VNode> } {

  5.  
    if (!children || !children.length) {

  6.  
    return {}

  7.  
    }

  8.  
    const slots = {}

  9.  
    for (let i = 0, l = children.length; i < l; i++) {

  10.  
    const child = children[i]

  11.  
    const data = child.data

  12.  
    // remove slot attribute if the node is resolved as a Vue slot node

  13.  
    if (data && data.attrs && data.attrs.slot) {

  14.  
    delete data.attrs.slot

  15.  
    }

  16.  
    // named slots should only be respected if the vnode was rendered in the

  17.  
    // same context.

  18.  
    if ((child.context === context || child.fnContext === context) &&

  19.  
    data && data.slot !=
    null

  20.  
    ) {

  21.  
    const name = data.slot

  22.  
    const slot = (slots[name] || (slots[name] = []))

  23.  
    if (child.tag === ‘template’) {

  24.  
    slot.push.apply(slot, child.children || [])

  25.  
    }
    else {

  26.  
    slot.push(child)

  27.  
    }

  28.  
    }
    else {

  29.  
    (slots.default || (slots.default = [])).push(child)

  30.  
    }

  31.  
    }

  32.  
    // ignore slots that contains only whitespace

  33.  
    for (const name in slots) {

  34.  
    if (slots[name].every(isWhitespace)) {

  35.  
    delete slots[name]

  36.  
    }

  37.  
    }

  38.  
    return slots

  39.  
    }

3.normalizeScopedSlots(core/vdom/helpers/normalize-scoped-slot.js)

  1.  
    export function normalizeScopedSlots (

  2.  
    slots: { [key: string]: Function } | void,

  3.  
    normalSlots: { [key: string]: Array<VNode> },

  4.  
    prevSlots?: { [key: string]: Function } | void

  5.  
    ): any {

  6.  
    let res

  7.  
    const hasNormalSlots = Object.keys(normalSlots).length > 0

  8.  
    const isStable = slots ? !!slots.$stable : !hasNormalSlots

  9.  
    const key = slots && slots.$key

  10.  
    if (!slots) {

  11.  
    res = {}

  12.  
    }
    else if (slots._normalized) {

  13.  
    // fast path 1: child component re-render only, parent did not change

  14.  
    return slots._normalized

  15.  
    }
    else if (

  16.  
    isStable &&

  17.  
    prevSlots &&

  18.  
    prevSlots !== emptyObject &&

  19.  
    key === prevSlots.$key &&

  20.  
    !hasNormalSlots &&

  21.  
    !prevSlots.$hasNormal

  22.  
    ) {

  23.  
    // fast path 2: stable scoped slots w/ no normal slots to proxy,

  24.  
    // only need to normalize once

  25.  
    return prevSlots

  26.  
    }
    else {

  27.  
    res = {}

  28.  
    for (const key in slots) {

  29.  
    if (slots[key] && key[0] !== ‘$’) {

  30.  
    res[key] = normalizeScopedSlot(normalSlots, key, slots[key])
    // 作用域插槽

  31.  
    }

  32.  
    }

  33.  
    }

  34.  
    // expose normal slots on scopedSlots

  35.  
    for (const key in normalSlots) {

  36.  
    if (!(key in res)) {

  37.  
    res[key] = proxyNormalSlot(normalSlots, key)
    // 普通插槽

  38.  
    }

  39.  
    }

  40.  
    // avoriaz seems to mock a non-extensible $scopedSlots object

  41.  
    // and when that is passed down this would cause an error

  42.  
    if (slots && Object.isExtensible(slots)) {

  43.  
    (slots: any)._normalized = res

  44.  
    }

  45.  
    def(res,
    ‘$stable’, isStable)

  46.  
    def(res,
    ‘$key’, key)

  47.  
    def(res,
    ‘$hasNormal’, hasNormalSlots)

  48.  
    return res

  49.  
    }

4.proxyNormalSlot(将slot代理到scopeSlots上)

  1.  
    function proxyNormalSlot(slots, key) {

  2.  
    return () => slots[key]

  3.  
    }

5.normalizeScopedSlot(将scopeSlots对应属性和方法挂载到scopeSlots)

  1.  
    function normalizeScopedSlot(normalSlots, key, fn) {

  2.  
    const normalized = function () {

  3.  
    let res = arguments.length ? fn.apply(null, arguments) : fn({})

  4.  
    res = res &&
    typeof res === ‘object’ && !Array.isArray(res)

  5.  
    ? [res]
    // single vnode

  6.  
    : normalizeChildren(res)

  7.  
    return res && (

  8.  
    res.length ===
    0 ||

  9.  
    (res.length ===
    1 && res[0].isComment) // #9658

  10.  
    ) ?
    undefined

  11.  
    : res

  12.  
    }

  13.  
    // this is a slot using the new v-slot syntax without scope. although it is

  14.  
    // compiled as a scoped slot, render fn users would expect it to be present

  15.  
    // on this.$slots because the usage is semantically a normal slot.

  16.  
    if (fn.proxy) {

  17.  
    Object.defineProperty(normalSlots, key, {

  18.  
    get: normalized,

  19.  
    enumerable: true,

  20.  
    configurable: true

  21.  
    })

  22.  
    }

  23.  
    return normalized

  24.  
    }

6.最后调用renderSlot用函数的返回值进行渲染。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/168374.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • C语言中open函数「建议收藏」

    C语言中open函数「建议收藏」作用:打开和创建文件。  简述:  #include  intopen(constchar*pathname,intoflag,…/*mode_tmode*/);  返回值:成功则返回文件描述符,否则返回-1  对于open函数来说,第三

  • pycharm社区版安装教程 2019_pycharm安装教程2020社区版

    pycharm社区版安装教程 2019_pycharm安装教程2020社区版首先进入JetBrain的官网(国内正常访问):https://www.jetbrains.com/第一眼看到的界面如下图所示:然后找到我们的Pycharm专题页:进入Pycharm的专题页面之后,点击下载按钮(这里有两个按钮,点任何一个都行):然后进入到真正的下载页面你会发现有两个版本的Pycharm,一个是Professional版本(收费),另外一个是Community版本是永久免费的,而且后续升级什么的也都是免费的,我们下载这个就行了,Comm…

  • 矩阵的投影矩阵_正交投影矩阵的性质

    矩阵的投影矩阵_正交投影矩阵的性质线性代数基础知识(上)线性代数基础知识(下)广义逆矩阵投影矩阵投影的定义投影矩阵求法性质投影矩阵的应用从投影角度看广义逆从投影角度看最小二乘。。。投影的定义什么是投影?下图给出了投影的直观理解:如图是在R3R^3R3空间中,一条直线lll与一个平面α\alphaα相交,向量zzz是R3R^3R3中的一个向量。把lll看成是一束光(方向从上到下),光…

  • vue axios轮询更新echarts 页面崩溃问题

    vue axios轮询更新echarts 页面崩溃问题之前做过一个项目测试发现这个问题,就是页面停留3,4个小时之后页面崩溃了后来观察了浏览器的Memory发现echarts实例还有echarts数据缓存在不断累积解决:刚开始看了echarts官方api确实提供了echarts.clear()清空当前实例,会移除实例中所有的组件和图表。用了以后发现作用不是很大还是会崩溃。那就是实例的问题了刚开始只是加了个判断 letmyEcharts if(!myEcharts){ myEcharts=echarts.init

    2022年10月31日
  • DeviceIOControl实战「建议收藏」

    DeviceIOControl实战「建议收藏」实战DeviceIoControl之一:通过API访问设备驱动程序Q 在NT/2000/XP中,我想用VC编写应用程序访问硬件设备,如获取磁盘参数、读写绝对扇区数据、测试光驱实际速度等,该从哪里入手呢?A 在NT/2000/XP中,应用程序可以通过API函数DeviceIoControl来实现对设备的访问—获取信息,发送命令,交换数据等。利用该接口函数向指定的设备驱动发送正确

  • 解决redis雪崩_redis 雪崩

    解决redis雪崩_redis 雪崩缓存雪崩指的是缓存失效了,导致大量的请求直接访问数据库,数据库压力就大了,很容易发生宕机的情况,然后和数据库相关的系统都受到了影响,这就是雪崩。缓存失效->数据库宕机->所有系统出现问题,连锁反应。缓存失效原因:1、redis集群大面积故障2、缓存过期解决方案:1、redis策略,redis高可用设计。2、redis策略,在给缓存加过期时间时,尽量错峰,避免同一时间所有缓存失效。3、访问数据库策略,访问数据库时加锁,单线程访问,可能会导致…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号