文章目录
环境
- Ubuntu 24.04
- Chrome Version 146.0.7680.164 (Official Build) (64-bit)
- VSCode 1.109.5
- npm 11.6.2
- Vue 3.5.32
准备
创建一个Vue项目。创建过程略,具体可参见 https://blog.csdn.net/duke_ding2/article/details/159510007 。
关于 props ,参见 https://blog.csdn.net/duke_ding2/article/details/159696940 。
背景
在Vue中, v-bind 指令用来动态的绑定属性。
注: v-bind:xxx 可以简写为 :xxx ,本文统一采用简写形式。
需求:有一个 <input> 输入框,其初始值是 1 。
例1
不使用动态绑定,修改 App.vue 如下:
html
<template>
<input value="1">
</template>
效果如下:

例2
使用 v-bind 动态绑定 value 属性值:
html
<template>
<input :value="myValue">
</template>
<script>
export default {
data() {
return {
myValue: "1"
}
}
}
</script>
效果是一样的。
注:也可以赋值给 myValue 数值 1 :
javascript
myValue: 1
效果也是一样的。
例3
使用 v-bind 设置属性值,并直接设置常量:
html
<template>
<input :value="1">
</template>
效果也是一样的。
注:也可以使用字符串常量 1 :
html
<input :value="'1'">
效果也是一样的。
那么问题来了:上述的这几种情况,它们的效果和本质是完全相同的吗?如果不相同,具体差别是什么?
分析
对于原生的HTML元素,其属性值是literal的字符串。
比如:
html
<template>
<input value="1+2">
</template>
其中, 1+2 是literal字符串。效果如下:

所以,不能使用显式的字符串:
html
<input value="'1'">
因为这样就相当于字符串 '1' (注意这是3个字符)。
而对于 v-bind 绑定的属性值,其值是一个JavaScript表达式。
比如:
html
<template>
<input :value="1+2">
</template>
其中, 1+2 是一个表达式,Vue会对该表达式求值。效果如下:

注意:上面这两种情况的本质区别在于:
<input value="1">里的1是字符串<input :value="1">里的1是数值
同理,看下面的代码:
html
<input :value="myValue">
v-bind 绑定的属性值是一个表达式,只不过此处表达式就是变量 myValue 本身,所以表达式的结果取决于 myValue 的具体值,而表达式的类型则取决于 myValue 的类型:
- 若
myValue是字符串1,则绑定的值是字符串1 - 若
myValue是数值1,则绑定的值是数值1
注意:对于 <input> , value 属性值是字符串 1 或者数值 1 其实并无太大区别,这是因为 <input> 的 value 属性值一定会被当作字符串来处理,所以即使 v-bind 绑定的表达式是数值,也会隐式的转换为字符串。
考一考
题目1
看下面的代码:
html
<input value="1">
把它换成等价的 v-bind 写法。
答案:
html
<input :value="'1'">
这是因为,在第一种写法中,属性的值是literal字符串(无需加引号)。而在第二种写法中,属性的值是JavaScript表达式,要想表示字符串,必须显式加上引号。
题目2
下面的代码:
html
<template>
<div v-if="true">aaa</div>
<div v-if="false">bbb</div>
<div v-if="'true'">ccc</div>
<div v-if="'false'">ddd</div>
</template>
结果是什么?
答案:效果如下:

这是因为, v-if 的判断条件是JavaScript表达式,其返回值是布尔类型,因此:
true:布尔类型常量truefalse:布尔类型常量false'true':字符串类型,由于字符串非空,会被看做true'false':字符串类型,由于字符串非空,会被看做true
组件的自定义属性
我们知道, v-bind 可用于组件的自定义属性,用来给子组件传递数据。在子组件中,通过 props 来声明自定义属性。
<input> 的 value 属性值天然就是字符串,即使提供的是其它类型,也会隐式转换。而自定义属性则是完全独立自由的。因此,使用自定义属性时,更要加倍小心,别把类型搞错了。
创建 MyComponent.vue 文件如下:
html
<template>
<div>
姓名: {{ name }},语文成绩: {{ chinese }},数学成绩: {{ math }},英语成绩: {{ english }}
</div>
</template>
<script>
export default {
props: ['id', 'name', 'chinese', 'math', 'english'],
}
</script>
该组件的功能是,列出某个学生的各科成绩。
修改 App.vue 如下:
html
<template>
<div>
<ul v-for="(student, index) in students" :key="id">
<li>
<MyComponent :id="student.id" :name="student.name" :chinese="student.chinese" :math="student.math" :english="student.english" />
</li>
</ul>
</div>
</template>
<script>
import MyComponent from './MyComponent.vue';
export default {
components: {
MyComponent
},
data() {
return {
students: [
{ id: 1, name: "张三", chinese: '85', math: '90', english: '88' },
{ id: 2, name: "李四", chinese: '92', math: '87', english: '91' },
{ id: 3, name: "王五", chinese: '78', math: '85', english: '82' }
]
}
},
}
</script>
效果如下:

看上去一切OK。
现在,有一个新的需求:对于每个学生,要显示其"平均成绩"。
修改 MyComponent.vue 如下:
html
<template>
<div>
姓名: {{ name }},语文成绩: {{ chinese }},数学成绩: {{ math }},英语成绩: {{ english }},平均成绩: {{ average }}
</div>
</template>
<script>
export default {
props: ['id', 'name', 'chinese', 'math', 'english'],
computed: {
average() {
return ((this.chinese + this.math + this.english) / 3).toFixed(2);
}
}
}
</script>
可见,在子组件中,添加了一个计算属性,用来计算三门课程的平均成绩。
效果如下:

可以看出,计算出来的平均成绩是错误的。
来看一下计算平均成绩的逻辑:
javascript
computed: {
average() {
return ((this.chinese + this.math + this.english) / 3).toFixed(2);
}
}
该代码中,使用了一个计算属性 average ,计算三门课程成绩的平均值,并保留两位小数。
代码看来没有什么问题,那么问题到底出在哪儿呢?
答案:父组件所传递的数据的类型有问题。
javascript
data() {
return {
students: [
{ id: 1, name: "张三", chinese: '85', math: '90', english: '88' },
{ id: 2, name: "李四", chinese: '92', math: '87', english: '91' },
{ id: 3, name: "王五", chinese: '78', math: '85', english: '82' }
]
}
},
可以看出,三门课程成绩都是字符串类型,导致平均成绩计算错误。
要fix这个bug,只需把课程成绩改为数值类型:
javascript
data() {
return {
students: [
{ id: 1, name: "张三", chinese: 85, math: 90, english: 88 },
{ id: 2, name: "李四", chinese: 92, math: 87, english: 91 },
{ id: 3, name: "王五", chinese: 78, math: 85, english: 82 }
]
}
},
效果如下:

这次,平均成绩计算正确了。
实际上,子组件在 props 声明自定义属性时,可以添加校验。
javascript
props: ['id', 'name', 'chinese', 'math', 'english'],
本例中,没有任何校验,这也是导致错误的一个间接原因。
可以加上类型限定:
javascript
props: {
id: Number,
name: String,
chinese: Number,
math: Number,
english: Number
},
这样,如果传递的数据不满足条件,在开发环境下,会收到警告信息,比如:
Vue warn\]: Invalid prop: type check failed for prop "english". Expected Number with value 82, got String with value "82". at \
at \
效果如下:

总结
- 对于原生的HTML元素,其属性值是literal的字符串,例如
1+2,会被看做字符串- 对于
<input>元素,其value属性一定会作为字符串来处理- 即使提供的数据是数值,也会隐式转换为字符串
- 对于
- 对于
v-bind绑定的属性值,其值是一个JavaScript表达式- 要注意表达式的返回类型是否合适
- 对于
v-if,需要布尔类型的表达式,如果表达式不是布尔类型,则会隐式转换- 例如:
v-if="'false'",字符串false会隐式转换为true
- 例如:
- 在使用自定义属性给子组件传递数据时,一定要注意别把类型搞错了
- 子组件的
props声明自定义属性时,可以添加校验逻辑,包括类型限定
- 子组件的