XingYun blog
  • JS基础

    • 图解js原型链
    • JS Event Loop
    • 对象的底层数据结构
    • 让你的JavaScript代码简单又高效
    • 函数参数按值传递
    • 判断数据类型
    • 浮点数精度问题和解决办法
    • 常用方法snippet
    • 实现Promise
    • 防抖和节流
    • 巧用sort排序
  • CSS && HTML

    • CSS也需要性能优化
    • class命名规范
    • em、px、rem、vh、vw 区别
    • CSS揭秘阅读笔记
  • 浏览器

    • 浏览器是如何渲染页面的
    • 重排和重绘
    • BOM浏览器对象模型
    • DOM事件
    • 浏览器存储
  • 数据结构

    • JS实现链表
    • JS实现栈与栈应用
    • JS实现常见排序
    • 哈夫曼编码
    • MD5算法
  • vue原理浅析

    • Vue虚拟dom与Diff算法
    • 前端打包文件的缓存机制
    • vue数组为什么不是响应式
    • v-for为什么不能用index做key
  • 前端工程化

    • 浏览器是如何渲染页面的
    • 前端打包需要gzip压缩吗
    • 前端打包文件的缓存机制
    • webpack loader和plugin
  • 轮子&&组件库

    • 实现水波浪进度球
  • 文字转语音mp3文件
  • 文件上传前后端实现
  • moment.js给定时间获取自然月、周的时间轴
  • 实现文件上传功能
  • 批量下载照片
  • leaflet改变坐标原点
  • 网络

    • 有了MAC地址 为什么还需要IP地址
    • 为什么IP地址老是变
    • 我们为什么需要IPV6
    • TCP与UDP
  • 计算机组成原理

    • ASCII、Unicode、UTF-8和UTF-16
  • VSCode

    • VSCode图片预览插件 Image preview
    • rsync:linux间的高效传输工具

XingYun

冲!
  • JS基础

    • 图解js原型链
    • JS Event Loop
    • 对象的底层数据结构
    • 让你的JavaScript代码简单又高效
    • 函数参数按值传递
    • 判断数据类型
    • 浮点数精度问题和解决办法
    • 常用方法snippet
    • 实现Promise
    • 防抖和节流
    • 巧用sort排序
  • CSS && HTML

    • CSS也需要性能优化
    • class命名规范
    • em、px、rem、vh、vw 区别
    • CSS揭秘阅读笔记
  • 浏览器

    • 浏览器是如何渲染页面的
    • 重排和重绘
    • BOM浏览器对象模型
    • DOM事件
    • 浏览器存储
  • 数据结构

    • JS实现链表
    • JS实现栈与栈应用
    • JS实现常见排序
    • 哈夫曼编码
    • MD5算法
  • vue原理浅析

    • Vue虚拟dom与Diff算法
    • 前端打包文件的缓存机制
    • vue数组为什么不是响应式
    • v-for为什么不能用index做key
  • 前端工程化

    • 浏览器是如何渲染页面的
    • 前端打包需要gzip压缩吗
    • 前端打包文件的缓存机制
    • webpack loader和plugin
  • 轮子&&组件库

    • 实现水波浪进度球
  • 文字转语音mp3文件
  • 文件上传前后端实现
  • moment.js给定时间获取自然月、周的时间轴
  • 实现文件上传功能
  • 批量下载照片
  • leaflet改变坐标原点
  • 网络

    • 有了MAC地址 为什么还需要IP地址
    • 为什么IP地址老是变
    • 我们为什么需要IPV6
    • TCP与UDP
  • 计算机组成原理

    • ASCII、Unicode、UTF-8和UTF-16
  • VSCode

    • VSCode图片预览插件 Image preview
    • rsync:linux间的高效传输工具
  • 3个提升Vue性能的写法
  • 重读Vue文档
  • moment.js给定时间获取自然月、周的时间轴
  • Vue虚拟dom与Diff算法
  • 深入响应式原理
  • Echart样例
  • 我的Vue指令库
  • 滚动到底部加载更多
  • 实现一键换肤
  • 手写Vue数据劫持
  • vue-router核心原理与手写实现
  • computed和watch
  • vue数组为什么不是响应式
  • v-for为什么不能用index做key
  • webpack loader和plugin
  • keep-alive组件原理
  • vue插槽进化
    • Vue多层嵌套组件
    • vue生命周期hook
    • vue监听dom元素的resize事件
    • 前端打包需要gzip压缩吗
    • 实现水波浪进度球
    • Vue
    XingYun
    2022-01-24
    目录

    vue插槽进化

    插槽分为普通插槽和具名插槽

    两种插槽的目的都是让子组件 slot 占位符生成的内容由父组件来决定

    # 一、普通插槽

    父组件

    <div class="container">
      <header>
        <slot name="header">
          default
        </slot>
      </header>
    </div>
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    <template slot="header">
      <div class="header">
        header
      </div>
    </template>
    
    
    1
    2
    3
    4
    5
    6

    # 二、具名插槽

    # before vue 2.6.0

    父组件

    <div class="container">
      <header>
        <slot name="header" :info="info">
          {{info.text}}
        </slot>
      </header>
    </div>
    
    1
    2
    3
    4
    5
    6
    7

    子组件

    <template slot="header" slot-scope="info">
      <div class="header">
        {{ info.text }}
      </div>
    </template>
    
    1
    2
    3
    4
    5

    # after vue 2.6.0

    新增指令 v-slot

    子组件

    <template v-slot:avatar="info">
      <div class="header">
        {{ info.text }}
      </div>
    </template>
    
    1
    2
    3
    4
    5

    # 三、两种插槽的区别

    before vue 2.6.0

    # 普通插槽

    在子组件编译和渲染阶段生成 vnodes,所以数据的作用域是父组件实例,子组件渲染的时候直接拿到这些渲染好的 vnodes

    # 作用域插槽

    父组件在编译和渲染阶段并不会直接生成 vnodes,而是在父节点 vnode 的 data 中保留一个 scopedSlots 对象,

    存储着不同名称的插槽以及它们对应的渲染函数,只有在编译和渲染子组件阶段才会执行这个渲染函数生成 vnodes,

    由于是在子组件环境执行的,所以对应的数据作用域是子组件实例。

    由于生成 slot 的作用域是在父组件中,所以明明是子组件的插槽 slot 的更新是会带着父组件一起更新的

    after vue 2.6.0

    1. slot 和 slot-scope 在组件内部被统一整合成了 函数
    2. 他们的渲染作用域都是 子组件
    3. 并且都能通过 this.$scopedSlots去访问

    如果是 普通插槽,就直接调用函数生成 vnode,如果是 作用域插槽,

    就直接带着 props 也就是 { msg } 去调用函数生成 vnode。 2.6 版本后统一为函数的插槽降低了理解难度。

    Vue 2.6 也尽可能的让 slot 的更新不触发父组件的渲染,通过一系列巧妙的判断和算法去尽可能避免不必要的渲染

    vue 2.6.* 更详细的代码分析 (opens new window)

    # 四、Vue3中的slot渲染

    Vue3中作者延续vue 2.6的优化

    Vue3 source renderSlot.ts (opens new window)

    export function renderSlot(
      slots: Slots,
      name: string,
      props: Data = {},
      // this is not a user-facing function, so the fallback is always generated by
      // the compiler and guaranteed to be a function returning an array
      fallback?: () => VNodeArrayChildren,
      noSlotted?: boolean
    ): VNode {
      // 兼容插件方指定了某个vue版本, 和项目的版本不同,会报错的问题
      if (currentRenderingInstance!.isCE) {
        return createVNode(
          'slot',
          name === 'default' ? null : { name },
          fallback && fallback()
        )
      } 
    
      let slot = slots[name]
    
      if (__DEV__ && slot && slot.length > 1) {
        warn(
          `SSR-optimized slot function detected in a non-SSR-optimized render ` +
            `function. You need to mark this component with $dynamic-slots in the ` +
            `parent template.`
        )
        slot = () => []
      }
    
      // a compiled slot disables block tracking by default to avoid manual
      // invocation interfering with template-based block tracking, but in
      // `renderSlot` we can be sure that it's template-based so we can force
      // enable it.
      // 打上未编译标记
      if (slot && (slot as ContextualRenderFn)._c) {
        ;(slot as ContextualRenderFn)._d = false
      }
    
      const validSlotContent = slot && ensureValidVNode(slot(props)) // 确保节点合法
      const rendered = createBlock(
        Fragment,
        { key: props.key || `_${name}` },
        validSlotContent || (fallback ? fallback() : []),
        validSlotContent && (slots as RawSlots)._ === SlotFlags.STABLE
          ? PatchFlags.STABLE_FRAGMENT
          : PatchFlags.BAIL
      )
      if (!noSlotted && rendered.scopeId) {
        // 给渲染后的对象加上slotScopeIds属性 在slot数据发生变化时,更新父组件和子组件时将参与update规则
        rendered.slotScopeIds = [rendered.scopeId + '-s']
      }
      // 打上编译完成标记 避免重复编译
      if (slot && (slot as ContextualRenderFn)._c) {
        ;(slot as ContextualRenderFn)._d = true
      }
      return rendered
    }
    
    
    export function createBlock(
      type: VNodeTypes | ClassComponent,
      props?: Record<string, any> | null,
      children?: any,
      patchFlag?: number,
      dynamicProps?: string[]
    ): VNode {
      return setupBlock(
        createVNode(
          type,
          props,
          children,
          patchFlag,
          dynamicProps,
          true /* isBlock: prevent a block from tracking itself */
        )
      )
    }
    
    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
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    #vue
    上次更新: 2023/04/05, 09:41:10
    keep-alive组件原理
    Vue多层嵌套组件

    ← keep-alive组件原理 Vue多层嵌套组件→

    最近更新
    01
    JavaScript-test
    07-20
    02
    二维码的原理
    07-20
    03
    利用ChatGPT优化代码
    07-20
    更多文章>
    Theme by Vdoing | Copyright © 2021-2023 XingYun | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式