单向数据流

简介: 单向数据流

所有的 props 都遵循着单向绑定原则,props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递。这避免了子组件意外修改父组件的状态的情况,不然应用的数据流将很容易变得混乱而难以理解。

另外,每次父组件更新后,所有的子组件中的 props 都会被更新到最新值,这意味着你不应该在子组件中去更改一个 prop。若你这么做了,Vue 会在控制台上向你抛出警告:

const props = defineProps(['foo'])
// ❌ 警告!prop 是只读的!
props.foo = 'bar'

导致你想要更改一个 prop 的需求通常来源于以下两种场景:

当 prop 的校验失败后,Vue 会抛出一个控制台警告 (在开发模式下)。

如果使用了基于类型的 prop 声明 ,Vue 会尽最大努力在运行时按照 prop 的类型标注进行编译。举例来说,defineProps<{ msg: string }> 会被编译为 { msg: { type: String, required: true }}

  1. prop 被用于传入初始值;而子组件想在之后将其作为一个局部数据属性。在这种情况下,最好是新定义一个局部数据属性,从 props 上获取初始值即可:
const props = defineProps(['initialCounter'])
// 计数器只是将 props.initialCounter 作为初始值
// 像下面这样做就使 prop 和后续更新无关了
const counter = ref(props.initialCounter)
  1. 需要对传入的 prop 值做进一步的转换。在这种情况中,最好是基于该 prop 值定义一个计算属性:
const props = defineProps(['size'])
// 该 prop 变更时计算属性也会自动更新
const normalizedSize = computed(() => props.size.trim().toLowerCase())
  1. 更改对象 / 数组类型的 props
    当对象或数组作为 props 被传入时,虽然子组件无法更改 props 绑定,但仍然可以更改对象或数组内部的值。这是因为 JavaScript 的对象和数组是按引用传递,而对 Vue 来说,禁止这样的改动,虽然可能生效,但有很大的性能损耗,比较得不偿失。
    这种更改的主要缺陷是它允许了子组件以某种不明显的方式影响父组件的状态,可能会使数据流在将来变得更难以理解。在最佳实践中,你应该尽可能避免这样的更改,除非父子组件在设计上本来就需要紧密耦合。在大多数场景下,子组件应该抛出一个事件来通知父组件做出改变。
  2. Prop 校验
    Vue 组件可以更细致地声明对传入的 props 的校验要求。比如我们上面已经看到过的类型声明,如果传入的值不满足类型要求,Vue 会在浏览器控制台中抛出警告来提醒使用者。这在开发给其他开发者使用的组件时非常有用。
    要声明对 props 的校验,你可以向 defineProps() 宏提供一个带有 props 校验选项的对象,例如:
defineProps({
  // 基础类型检查
  // (给出 `null` 和 `undefined` 值则会跳过任何类型检查)
  propA: Number,
  // 多种可能的类型
  propB: [String, Number],
  // 必传,且为 String 类型
  propC: {
    type: String,
    required: true
  },
  // Number 类型的默认值
  propD: {
    type: Number,
    default: 100
  },
  // 对象类型的默认值
  propE: {
    type: Object,
    // 对象或数组的默认值
    // 必须从一个工厂函数返回。
    // 该函数接收组件所接收到的原始 prop 作为参数。
    default(rawProps) {
      return { message: 'hello' }
    }
  },
  // 自定义类型校验函数
  // 在 3.4+ 中完整的 props 作为第二个参数传入
  propF: {
    validator(value, props) {
      // The value must match one of these strings
      return ['success', 'warning', 'danger'].includes(value)
    }
  },
  // 函数类型的默认值
  propG: {
    type: Function,
    // 不像对象或数组的默认,这不是一个
    // 工厂函数。这会是一个用来作为默认值的函数
    default() {
      return 'Default function'
    }
  }
})
  1. 一些补充细节:
  2. 所有 prop 默认都是可选的,除非声明了 required: true
  3. Boolean 外的未传递的可选 prop 将会有一个默认值 undefined
  4. Boolean 类型的未传递 prop 将被转换为 false。这可以通过为它设置 default 来更改——例如:设置为 default: undefined 将与非布尔类型的 prop 的行为保持一致。
  5. 如果声明了 default 值,那么在 prop 的值被解析为 undefined 时,无论 prop 是未被传递还是显式指明的 undefined,都会改为 default 值。

运行时类型检查

校验选项中的 type 可以是下列这些原生构造函数:

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol
  • Error

另外,type 也可以是自定义的类或构造函数,Vue 将会通过 instanceof 来检查类型是否匹配。例如下面这个类:

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName
    this.lastName = lastName
  }
}
defineProps({
  author: Person
})

Vue 会通过 instanceof Person 来校验 author prop 的值是否是 Person 类的一个实例。

Boolean 类型转换

为了更贴近原生 boolean attributes 的行为,声明为 Boolean 类型的 props 有特别的类型转换规则。以带有如下声明的 <MyComponent> 组件为例

defineProps({
  disabled: Boolean
})

 

<!-- 等同于传入 :disabled="true" -->
<MyComponent disabled />
<!-- 等同于传入 :disabled="false" -->
<MyComponent />

当一个 prop 被声明为允许多种类型时,Boolean 的转换规则也将被应用。然而,当同时允许 StringBoolean 时,有一种边缘情况——只有当 Boolean 出现在 String 之前时,Boolean 转换规则才适用:

// disabled 将被转换为 true
defineProps({
  disabled: [Boolean, Number]
})
  
// disabled 将被转换为 true
defineProps({
  disabled: [Boolean, String]
})
  
// disabled 将被转换为 true
defineProps({
  disabled: [Number, Boolean]
})
  
// disabled 将被解析为空字符串 (disabled="")
defineProps({
  disabled: [String, Boolean]
})
相关文章
|
Web App开发 测试技术 网络性能优化
WebRTC 拥塞控制 | Trendline 滤波器
本文是 WebRTC 拥塞控制 第 2 篇
WebRTC 拥塞控制 | Trendline 滤波器
|
存储 API 对象存储
OSS新特性:支持文件上传、复制时,指定Object的存储类型以及修改已有文件的存储类型
用户在上传、复制文件时,可灵活地指定文件的存储类型为Standard、IA、Archive;用户也可以修改实时修改文件的存储类型,比如从低频型(IA)修改为标准型。
6015 0
|
存储 自然语言处理 数据可视化
Dataset:数据集集合(NLP方向数据集)——常见的自然语言处理数据集大集合(建议收藏,持续更新)
Dataset:数据集集合(NLP方向数据集)——常见的自然语言处理数据集大集合(建议收藏,持续更新)
|
SQL Java 关系型数据库
实时数仓 Hologres产品使用合集之如何安装和使用Java SDK
实时数仓Hologres是阿里云推出的一款高性能、实时分析的数据库服务,专为大数据分析和复杂查询场景设计。使用Hologres,企业能够打破传统数据仓库的延迟瓶颈,实现数据到决策的无缝衔接,加速业务创新和响应速度。以下是Hologres产品的一些典型使用场景合集。
|
SQL 监控 关系型数据库
Influx Sql系列教程九:query数据查询基本篇二
前面一篇介绍了influxdb中基本的查询操作,在结尾处提到了如果我们希望对查询的结果进行分组,排序,分页时,应该怎么操作,接下来我们看一下上面几个场景的支持
987 0
|
移动开发 安全 数据安全/隐私保护
通过Web端登录无影云桌面
通过Web端登录无影云桌面。
27949 7
|
前端开发 定位技术 开发工具
Jetpack MVVM 错误用法(二)在 launchWhenX 中启动协程
Jetpack MVVM 使用常见错误 :在 launchWhenX 中启动协程可能会隐藏隐患,应该用 repeatOnLifecycle 替代
840 0
|
网络协议 算法 网络架构
IP协议与以太网
本文主要介绍在TCP/IP五层协议模型中位于网络层和数据链路层的协议,在网络层主要是IP协议,而数据链路层的协议主要为以太网。
606 0
IP协议与以太网
|
传感器 存储 芯片
STM32第一课:STM硬件实物图+功能简介
STM32第一课:STM硬件实物图+功能简介
1286 0
STM32第一课:STM硬件实物图+功能简介
|
存储 缓存 Dart
Flutter内存分析
约定: 默认 Android 平台,32位应用 Flutter 版本 1.20 背景 Flutter 接入后,内存的水位升高,oom是较突出的问题。 通过理清以下几个关键问题,可帮助我们更全面认识 Flutter 内存管理,提高解决问题的效率。 Flutter 内存由几部分构成? new space, old space 内存是如何分配,管理的? external 堆内
3434 0
Flutter内存分析