夜猫子的知识栈 夜猫子的知识栈
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《Web Api》
    • 《ES6教程》
    • 《Vue》
    • 《React》
    • 《TypeScript》
    • 《Git》
    • 《Uniapp》
    • 小程序笔记
    • 《Electron》
    • JS设计模式总结
  • 《前端架构》

    • 《微前端》
    • 《权限控制》
    • monorepo
  • 全栈项目

    • 任务管理日历
    • 无代码平台
    • 图书管理系统
  • HTML
  • CSS
  • Nodejs
  • Midway
  • Nest
  • MySql
  • 其他
  • 技术文档
  • GitHub技巧
  • 博客搭建
  • Ajax
  • Vite
  • Vitest
  • Nuxt
  • UI库文章
  • Docker
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

夜猫子

前端练习生
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《Web Api》
    • 《ES6教程》
    • 《Vue》
    • 《React》
    • 《TypeScript》
    • 《Git》
    • 《Uniapp》
    • 小程序笔记
    • 《Electron》
    • JS设计模式总结
  • 《前端架构》

    • 《微前端》
    • 《权限控制》
    • monorepo
  • 全栈项目

    • 任务管理日历
    • 无代码平台
    • 图书管理系统
  • HTML
  • CSS
  • Nodejs
  • Midway
  • Nest
  • MySql
  • 其他
  • 技术文档
  • GitHub技巧
  • 博客搭建
  • Ajax
  • Vite
  • Vitest
  • Nuxt
  • UI库文章
  • Docker
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 基础

  • 响应式

  • 组件

    • Prop 验证 与 非 Prop 的 Attribute
    • Attrbuite的继承
      • vue2
        • Attribute 继承
        • 指定元素继承
        • 多根节点的继承
        • 实例:封装三方组件
      • vue3
        • Attributes 继承
        • class 和 style 的合并
        • 禁用 Attributes 继承
        • 多根节点的 Attributes 继承
        • 访问透传属性
        • Vue3 组件封装的变化
        • 1、$attrs 与 $listeners 合并
        • 2、$slot 与 $scopedSlots 合并
        • 3、如何继承第三方组件的Methods
    • 使用组件的细节点
    • 祖先组件给后代传值
    • 父组件给子组件传值
    • 子组件派发事件和值给父组件
    • 自定义事件
    • 兄弟组件传值
    • 非父子组件传值
    • 父组件调用子组件方法并传入值
    • 插槽slot
    • 异步组件
    • 动态组件与 v-once 指令
    • vue父子组件的生命周期顺序
  • 过渡&动画

  • 可复用性

  • 工具

  • 规模化

  • 路由(核心)

  • 组合式

  • Vuex

  • Pinia

  • 其他

  • Vue3 源码学习记录

  • 《Vue》笔记
  • 组件
夜猫子
2022-08-03
目录

Attrbuite的继承

# 非 Prop 的 Attribute

没有进行 prop 定义的 attrbuite 可以通过 $attr 来获取。 常见的如 class、style 和 id 等 attribute。

# vue2

# Attribute 继承

当只有单节点时,非 prop 的 attribute 将自动添加到根节点的 attribute 中。

<!-- 具有非 prop 的 attribute 的 date-picker 组件-->
<date-picker data-status="activated"></date-picker>

<!-- 渲染后的 date-picker 组件 -->
<div class="date-picker" data-status="activated">
  <input type="datetime-local" />
</div>
1
2
3
4
5
6
7

同样的规则也适用于事件监听器:

<date-picker @change="submitChange"></date-picker>
1
app.component('date-picker', {
  created() {
    console.log(this.$attrs) // { onChange: () => {}  }
  }
})
1
2
3
4
5

# 指定元素继承

  • 设置 inheritAttrs: false,可以阻止根元素继承 attribute。

  • 使用 $attrs,将除 props 和 emits 外的所有 attribute 应用到其它元素上。

app.component('date-picker', {
  inheritAttrs: false,
  template: `
    <div class="date-picker">
      <input type="datetime-local" v-bind="$attrs" />
    </div>
  `
})
1
2
3
4
5
6
7
8

这样做的结果是,data-status attribute 将应用于 input 元素!

<!-- date-picker 组件使用非 prop 的 attribute -->
<date-picker data-status="activated"></date-picker>

<!-- 渲染后的 date-picker 组件 -->
<div class="date-picker">
  <input type="datetime-local" data-status="activated" />
</div>
1
2
3
4
5
6
7

# 多根节点的继承

多根节点时就必须显式绑定 $attrs,否则就会报错。

<custom-layout id="custom-layout" @click="changeValue"></custom-layout>
1
// 指定绑定 attrs 的根节点
app.component('custom-layout', {
  template: `
    <header>...</header>
    <main v-bind="$attrs">...</main>
    <footer>...</footer>
  `
})
1
2
3
4
5
6
7
8

# 实例:封装三方组件

透传属性

$attrs:获取一个组件没有声明的包含所有父作用域的绑定。

透传事件

$listeners:包含了父作用域中的(不含.native修饰器的)v-on事件监听器。

暴露插槽

$slots:普通插槽,使用$slots这个变量拿到非作用域的插槽,然后循环渲染对应的普通具名插槽,这样就可以使用第三方组件提供的原插槽;

$scopedSlots:作用域插槽则绕了一圈,使用了一个插槽的语法糖(具名插槽的缩写)并且结合着动态插槽名的用法;循环$scopedSlots作用插槽位置和传递对应的参数,,这样就可以使用第三方组件提供的作用域插槽。

暴露内部方法

透过遍历目标组件实例,将实例内部方法挂载到当前实例

<template>
  <el-autocomplete ref="el-autocomplete" v-bind="$attrs" v-on="$listeners">
    <!-- 遍历子组件非作用域插槽,并对父组件暴露 -->
    <slot v-for="(_, slotName) in $slots" :name="slotName" :slot="slotName" />
    <!-- 遍历子组件作用域插槽,并对父组件暴露 -->
    <template v-for="(index, name) in $scopedSlots" #[name]="data">
      <slot :name="name" v-bind="data" />
    </template>
  </el-autocomplete>
</template>

<script>
export default {
  //   inheritAttrs: false, // 是否禁用样式透传
  data() {
    return {};
  },
  methods: {
    extendMethod() {
      const refMethod = Object.entries(this.$refs["el-autocomplete"]);
      for (const [key, value] of refMethod) {
        if (!(key.includes("$") || key.includes("_"))) {
          this[key] = value;
        }
      }
    },
  },
  created() {},
  mounted() {
    this.extendMethod();
  },
};
</script>
<style lang="css" scoped></style>
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

# vue3

# Attributes 继承

未被 props 或 emits 声明的属性默认会被透传并被根元素继承。

<!-- <MyButton> 的模板 -->
<button>Click Me</button>
1
2

一个父组件使用了这个组件,并且传入了 class:

<MyButton class="large"  @click="onClick" />
1

最后渲染出的 DOM 结果是:

<button class="large"  @click="onClick" >Click Me</button>
1

这里,<MyButton> 并没有将 class 声明为一个它所接受的 prop,所以 class 被视作透传 attribute,自动透传到了 <MyButton> 的根元素上。

此外,如果透传进的还是一个组件,那么它会进行深度透传,即继续向下透传

# class 和 style 的合并

如果透传的属性是 class 或 style ,她会将透传的属性和原有的属性进行合并。

<!-- <MyButton> 的模板 -->
<button class="btn">Click Me</button>
1
2

则最后渲染出的 DOM 结果会变成:

<button class="btn large">Click Me</button>
1

# 禁用 Attributes 继承

如果你不想要一个组件自动地继承 attribute,你可以在组件选项中设置 inheritAttrs: false。

你也可以直接在 <script setup> 中使用 defineOptions (opens new window):vue 3.3版本支持

<script setup>
defineOptions({
  inheritAttrs: false
})
// ...setup 逻辑
</script>
1
2
3
4
5
6

# 多根节点的 Attributes 继承

和单根节点组件有所不同,有着多个根节点的组件没有自动 attribute 透传行为。如果 $attrs 没有被显式绑定,将会抛出一个运行时警告。

父组件:

<CustomLayout id="custom-layout" @click="changeValue" />
1

对应组件:

<header>...</header>
<main>...</main>
<footer>...</footer>
1
2
3

如果 $attrs 被显式绑定,则不会有警告:

<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>
1
2
3

# 访问透传属性

如果需要,你可以在 <script setup> 中使用 useAttrs() API 来访问一个组件的所有透传 attribute:

<script setup>
import { useAttrs } from 'vue'

const attrs = useAttrs()
</script>
1
2
3
4
5

如果没有使用 <script setup>,attrs 会作为 setup() 上下文对象的一个属性暴露:

export default {
  setup(props, ctx) {
    // 透传 attribute 被暴露为 ctx.attrs
    console.log(ctx.attrs)
  }
}
1
2
3
4
5
6

需要注意的是,虽然这里的 attrs 对象总是反映为最新的透传 attribute,但它并不是响应式的 (考虑到性能因素)。你不能通过侦听器去监听它的变化。如果你需要响应性,可以使用 prop。或者你也可以使用 onUpdated() 使得在每次更新时结合最新的 attrs 执行副作用。

# Vue3 组件封装的变化

# 1、$attrs 与 $listeners 合并

在 Vue 3.x 当中,取消了$listeners这个组件实例的属性,将其事件的监听都整合到了$attrs这个属性上了,因此直接通过v-bind=$attrs属性就可以进行 props 属性和 event 事件的透传。

# 2、$slot 与 $scopedSlots 合并

在Vue 3.x当中取消了作用域插槽$scopedSlots的属性,将所有插槽都统一在$slots当中,因此在 Vue 3.x 当中不需要分默认插槽、具名插槽和作用域插槽,可以进行统一的处理。

# 3、如何继承第三方组件的Methods

element-plus el-table组件没有使用defineExpose,无法用这种方式继承

<template>
  <a-table v-bind="attrs" ref="componentRef">
    <!-- 遍历子组件作用域插槽,并对父组件暴露 -->
    <template v-for="k in Object.keys(slots)" #[k]="slotProps" :key="k">
      <slot :name="k" v-bind="slotProps || {}"></slot>
    </template>
  </a-table>
</template>

<script setup>
import { useAttrs, useSlots, useTemplateRef } from 'vue'
const attrs = useAttrs()
const slots = useSlots()

const componentRef = useTemplateRef('componentRef')
defineExpose({
  componentRef
})
</script>

<style></style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

提示

此外,我们绑定的属性(包括 class 和 style)同时会在根元素(上面的例子是class="my-input"的Dom节点)上起作用。要阻止这个默认行为,我们需要设置inheritAttrs为false

编辑 (opens new window)
上次更新: 2024/10/17 16:38:36
Prop 验证 与 非 Prop 的 Attribute
使用组件的细节点

← Prop 验证 与 非 Prop 的 Attribute 使用组件的细节点→

最近更新
01
IoC 解决了什么痛点问题?
03-10
02
如何调试 Nest 项目
03-10
03
Provider注入对象
03-10
更多文章>
Copyright © 2019-2025 Study | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式