单向数据流

简介: 单向数据流

所有的 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 滤波器
|
Java Maven
Maven三种仓库详解
仓库分类 1、本地仓库 本地仓库就是开发者本地已经下载下来的或者自己打包所有jar包的依赖仓库,本地仓库路径配置在maven对应的conf/settings.xml配置文件。
2093 0
|
网络协议 网络架构 前端开发
带你读《计算机网络原理》之一:计算机网络概述
本书以层次化的网络体系结构为线索,针对通信子网功能详细地介绍了计算机网络的基本概念及数据通信的基本原理。
|
算法
算法题每日一练---第24天:海盗分金币
5 个海盗,相约进行一次帆船比赛。比赛中天气发生突变,他们被冲散了。
635 0
算法题每日一练---第24天:海盗分金币
|
供应链 安全
为什么几十万企业争相申请ISO三体系认证?
为什么几十万企业争相申请ISO三体系认证?
916 1
为什么几十万企业争相申请ISO三体系认证?
|
Ubuntu Linux 数据安全/隐私保护
Ubuntu系统的安装
Linux 操作系统是最受开发人员欢迎的电脑操作系统,现在市场上有很多个发行版本,由于 Linux 系统是开源的,所以任何人都可以免费下载安装。这篇文章以安装 Ubuntu 发行版系统为例,解决如何安装 Linux 操作系统,其他 Linux 发行版系统的安装都类似。
4318 0
Ubuntu系统的安装
|
传感器 存储 芯片
STM32第一课:STM硬件实物图+功能简介
STM32第一课:STM硬件实物图+功能简介
1429 0
STM32第一课:STM硬件实物图+功能简介
|
机器学习/深度学习 算法 知识图谱
视频事件检测(下)| 学习笔记
快速学习视频事件检测(下),介绍了视频事件检测(下)系统机制, 以及在实际应用过程中如何使用。
视频事件检测(下)| 学习笔记
|
机器学习/深度学习 人工智能 缓存
用huggingface.transformers.AutoModelForSequenceClassification在文本分类任务上微调预训练模型
本部分以文本分类任务为例,介绍transformers上如何微调预训练模型。
用huggingface.transformers.AutoModelForSequenceClassification在文本分类任务上微调预训练模型
|
数据采集 自动驾驶 物联网
NR 的需求和目标 | 带你读《5G 空口设计与实践进阶 》之一
相对以往的移动通信系统,NR 的应用场景和服务对象均发生了极大的变化,其系统设计也不再简单地以更高峰值速率和更高频谱效率作为核心目标。为满足差异化的能力指标要求,NR 需要系统性的方案设计,基于一组关键技术以解决不同场景的需求侧重点。需要强调的是,NR选择的技术不一定是理论上最先进的,但一定是可实现和满足需求的。
NR 的需求和目标   | 带你读《5G 空口设计与实践进阶 》之一