Vue 组件 Props 传递与单向数据流详解

作者:模板散步时间:2026-10-22 15:50:39浏览:657

本文详解Vue中props的用法,涵盖静态与动态数据传递、命名规范、类型验证及单向数据流机制。重点解析如何避免直接修改props引发的警告,并提供通过局部变量、计算属性及watch监听三种方式安全处理父组件数据更新的方案。

Vue props 基础与父子组件通信

Vue props用法详解

在 Vue 组件开发中,props 是组件接收外部数据的关键选项之一。父子组件之间的交互关系可以概括为:

props down, events up

父组件通过 props 向下传递数据给子组件,而子组件则通过 events 事件向父组件发送消息。

父子级组件

例如,我们需要创建 parent 和 child 两个组件。为了确保每个组件在相对隔离的环境中运行,从而提高代码的可维护性,这种设计至关重要。

首先,我们定义父子两个组件以及一个 Vue 实例:


var childNode = {
 template: `
  <div>childNode</div>
  `
};
var parentNode = {
 template: `
  <div>
   <child></child>
   <child></child>
  </div>
  `,
 components: {
 child: childNode
 }
};
new Vue({
 el: "#example",
 components: {
 parent: parentNode
 }
});

<div id="example">
 <parent></parent>
</div>

在此示例中,childNode 组件的 template 定义为一个 div,其内容直接显示为字符串 "childNode"。

而在 parentNode 的 template 中,定义了一个 class 为 parent 的 div,其中包含了两个 child 组件实例。

若希望更系统地深入学习 Vue.js,建议参考《Vue.js实战》PDF高质量扫描版这一经典著作。

静态 Props 与命名规范

静态 props

组件实例的作用域是独立的,这意味着在子组件的模板中不应直接引用父组件的数据。若要让子组件使用父组件的数据,必须通过子组件的 props 选项进行声明。

父组件向子组件传递数据分为动态和静态两种方式,此处先介绍静态传递方式。

子组件必须显式地使用 props 选项来声明它期望接收的数据。

修改前例代码,为 childNode 添加 props 选项,并指定需要接收的 forChildMsg 数据;

随后,在父组件的模板中,通过添加特性的方式来传递数据。


var childNode = {
 template: `
  <div>
   {{forChildMsg}}
  </div>
  `,
 props: ["for-child-msg"]
};
var parentNode = {
 template: `
  <div>
   <p>parentNode</p>
   <child for-child-msg="aaa"></child>
   <child for-child-msg="bbb"></child>
  </div>
  `,
 components: {
 child: childNode
 }
};

命名规范

对于在 props 中声明的属性,在父组件的 template 模板中,属性名必须使用中划线(kebab-case)写法;

子组件在声明 props 属性时,既可以使用小驼峰(camelCase)也可以使用中划线写法;但在子组件模板中使用从父组件传来的变量时,必须使用对应的小驼峰写法。无需担心,Vue 能够正确识别小驼峰和下划线命名法混用的变量,例如这里的forChildMsg和for-child-msg被视为同一值。

动态 Props 与数据绑定

动态 props

在模板中,若要动态地将父组件的数据绑定到子组件模板的 props 上,与绑定 HTML 标签特性类似,需使用 v-bind 指令;

基于上述静态 props 的代码,此次仅需修改父组件:


var parentNode = {
 template: `
  <div>
   <p>parentNode</p>
   <child :for-child-msg="childMsg1"></child>
   <child :for-child-msg="childMsg2"></child>
  </div>
  `,
 components: {
 child: childNode
 },
 data: function() {
 return {
  childMsg1: "Dynamic props msg for child-1",
  childMsg2: "Dynamic props msg for child-2"
 };
 }
};

在父组件 data 的 return 数据中,childMsg1 和 childMsg2 将被传入子组件中,

Props 验证机制

props 验证

对传入的 props 参数进行数据规格验证,若不符合规格,Vue 会发出警告信息。

可判断的所有类型(即 type 值)包括:String, Number, Boolean, Function, Object, Array, Symbol。


Vue.component("example", {
 props: {
 // 基础类型检测, null意味着任何类型都行
 propA: Number,
 // 多种类型
 propB: [String, Number],
 // 必传且是String
 propC: {
  type: String,
  required: true
 },
 // 数字有默认值
 propD: {
  type: Number,
  default: 101
 },
 // 数组、默认值是一个工厂函数返回对象
 propE: {
  type: Object,
  default: function() {
  console.log("propE default invoked.");
  return { message: "I am from propE." };
  }
 },
 // 自定义验证函数
 propF: {
  isValid: function(value) {
  return value > 100;
  }
 }
 }
});
let childNode = {
 template: "<div>{{forChildMsg}}</div>",
 props: {
 "for-child-msg": Number
 }
};
let parentNode = {
 template: `
   <div class="parent">
   <child :for-child-msg="msg"></child>
   </div>
  `,
 components: {
 child: childNode
 },
 data() {
 return {
  // 当这里是字符串 "123456"时会报错
  msg: 123456
 };
 }
};

此外,还可以在 props 定义的数据中加入自定义验证函数,当该函数返回 false 时,系统将输出警告。

例如,我们将前例中的 childNode 的 for-child-msg 修改为一个对象,并包含一个名为 validator 的函数。注意,该函数名必须规定为 validator,自定义其他函数名不会生效。


let childNode = {
 template: "<div>{{forChildMsg}}</div>",
 props: {
 "for-child-msg": {
  validator: function(value) {
  return value > 100;
  }
 }
 }
};

在此处,我们为 for-child-msg 变量设置了 validator 函数,并要求传入的值必须大于 100,否则将触发警告。

单向数据流与修改策略

单向数据流

props 是单向绑定的:当父组件的属性发生变化时,数据将传导给子组件,但不会反向流动。这是为了防止子组件意外修改父组件的状态。

因此,不应在子组件中直接修改 props 中的值,否则 Vue 会发出警告。


let childNode = {
 template: `
   <div class="child">
   <div>
    <span>子组件数据</span>
    <input v-model="forChildMsg"/>
   </div>
   <p>{{forChildMsg}}</p>
   </div>`,
 props: {
 "for-child-msg": String
 }
};
let parentNode = {
 template: `
   <div class="parent">
   <div>
    <span>父组件数据</span>
    <input v-model="msg"/>
   </div>
   <p>{{msg}}</p>
   <child :for-child-msg="msg"></child>
   </div>
  `,
 components: {
 child: childNode
 },
 data() {
 return {
  msg: "default string."
 };
 }
};

在此示例中,父组件和子组件各有一个输入框,分别显示父组件数据和子组件数据。当我们在父组件的输入框中输入新数据时,子组件的数据也会同步修改;这体现了 props 向子组件传递数据的功能。而当我们尝试修改子组件的输入框时,浏览器控制台则会报错警告。

[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "forChildMsg"

修改 props 数据

通常有两种原因需要处理 props 的修改:

  1. prop 作为初始值传入后,子组件想把它当做局部数据来使用。
  2. prop 作为初始值传入后,由子组件处理成其他数据后输出。

应对办法是:

定义一个局部变量,并用 prop 的值初始化它。

然而,由于定义的 ownChildMsg 只能接受 forChildMsg 的初始值,当父组件传递的值发生变化时,ownChildMsg 无法收到更新。


let childNode = {
 template: `
   <div class="child">
   <div>
    <span>子组件数据</span>
    <input v-model="forChildMsg"/>
   </div>
   <p>{{forChildMsg}}</p>
   <p>ownChildMsg : {{ownChildMsg}}</p>
   </div>`,
 props: {
 "for-child-msg": String
 },
 data() {
 return { ownChildMsg: this.forChildMsg };
 }
};

此处我们添加了一个 <p> 标签用于查看 ownChildMsg 数据是否变化,结果发现只有默认值传递给了 ownChildMsg,父组件的改变只会更新 forChildMsg,而不会修改 ownChildMsg。

定义一个计算属性,处理 prop 的值并返回结果。

由于是计算属性,因此只能显示值,不能设置值。这里我们设定一旦从父组件修改了 forChildMsg 数据,就将 forChildMsg 加上字符串 "---ownChildMsg",然后显示在屏幕上。此时,每当父组件修改了新数据,ownChildMsg 数据都会随之更新。


let childNode = {
 template: `
   <div class="child">
   <div>
    <span>子组件数据</span>
    <input v-model="forChildMsg"/>
   </div>
   <p>{{forChildMsg}}</p>
   <p>ownChildMsg : {{ownChildMsg}}</p>
   </div>`,
 props: {
 "for-child-msg": String
 },
 computed: {
 ownChildMsg() {
  return this.forChildMsg + "---ownChildMsg";
 }
 }
};

更加稳妥的方式是使用变量存储 prop 的初始值,并利用 watch 来观察 prop 值的变化。当发生变化时,更新该变量的值。


let childNode = {
 template: `
   <div class="child">
   <div>
    <span>子组件数据</span>
    <input v-model="forChildMsg"/>
   </div>
   <p>{{forChildMsg}}</p>
   <p>ownChildMsg : {{ownChildMsg}}</p>
   </div>`,
 props: {
 "for-child-msg": String
 },
 data() {
 return {
  ownChildMsg: this.forChildMsg
 };
 },
 watch: {
 forChildMsg() {
  this.ownChildMsg = this.forChildMsg;
 }
 }
};

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

作者最新文章