什么是vue
一套用于构建用户界面的渐进式JavaScript框架
渐进式:Vue可以自底向上逐层的应用
vue的特点
- 采用组件化模式,提高代码复用率,且让代码更好维护
- 声明式编码,让编码人员无需操作DOM,提高开发效率
- 使用虚拟DOM+优秀的Diff算法,尽量复用DOM节点
初识Vue
- 想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象
- root容器里的代码依然符合html规范,只不过混入了一些特殊的Vue语法
- root容器里的代码被称为Vue模板
- Vue实例和容器是一一对应的
- 真实开发中只有一个Vue实例,并且会配合着组件一起使用
- {{xxx}}中的xxx要写js表达式,且xxx可以自动读取到data中的所有属性
- 一旦data中的数据发生改变,那么页面中用到该数据的地方也会自动更新
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="root">
<h1>Hello,{{name}},{{Date.now()}}</h1>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
//创建vue实例
const x = new Vue({
el: '#root',//el用于指定为那个容器服务,值通常为css选择器字符串
data: {//data用于存储数据,数据供el所指定的容器去使用,值暂时写成一个对象
name: 'Vue'
}
})
</script>
</body>
</html>
注意区分js表达式和js代码:
- 表达式:一个表达式会生成一个值,可以放在任何一个需要值的地方
- js代码(语句)
(1) if(){}
(2) for(){}
模板语法
- 插值语法
功能:用于解析标签体内容
写法:{{xxx}},xxx是js代码,且可以直接读取到data中的所有属性
- 指令语法
功能:用于解析标签(包括:标签属性、标签体内容、绑定事件)
举例:v-bind:href="xxx"或 简写为 :href="xxx",xxx同样要写js表达式,且可以直接读取到data中的所有属性
备注:Vue中有很多的指令,且形式都是:v-???,比如v-bind
数据绑定
Vue中有两种数据绑定的方式:
-
单向绑定:(v-bind)数据只能从data流向页面
-
双向绑定:(v-model)数据不仅能从data流向页面,还可以从页面流向data
备注:
- 双向绑定一般都应在表单类元素上
- v-model:value 可以简写为v-model
-
常见指令
指令 作用 示例
v-if 条件渲染 v-if="isShow"
v-else 否则分支 v-else
v-show 控制显示与隐藏 v-show="isShow"
v-for 遍历列表 v-for="item in list"
v-bind 绑定属性 :title="title"
v-on 绑定事件 @click="handleClick"
v-model 双向绑定 v-model="username"
v-html 渲染HTML v-html="html"
v-text 设置纯文本内容
v-cloak 编译前隐藏模板
v-pre 跳过Vue编译
el与data的两种写法
- el的两种写法
- new Vue时候配置el属性
- 先创建Vue实例,随后再通过vm.$mount('#root')指定el的值
- data的两种写法
- 对象式
- 函数式
如何选择:目前哪种写法都可以,学到组件时,data必须使用函数式,否则会报错
- 一个重要的原则:
由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了
object.defineProperty方法
- enumerable: true,//控制属性是否可以枚举(遍历),默认值false
- writable: true,//控制属性是否可以被修改,默认值为false
- configurable: true,//控制属性是否可以被删除,默认值为false
- get函数:当有人读取person的age属性时,get函数(getter)就会被调用,其返回值就是get的值
- set函数://当有人修改person的属性时,set函数(setter)就会被调用
数据代理
通过一个对象代理对另一个对象中属性的操作(读/写)
- Vue中的数据代理:
通过vm对象来代理data对象中属性的操作(读/写) - Vue中数据代理的好处:
更加方便的操作data中的数据 - 基本原理:
通过Object.defineProperty()把data对象中所有属性添加到vm上
为每一个添加到vm上的属性,都指定一个getter/setter
在getter/setter内部去操作(读/写)data中对应的属性
事件处理
事件的基本使用:
- 使用v-on:xxx 或@xxx绑定事件,其中xxx是事件名
- 事件的回调需要配置在methods对象中,最终会在vm上
- methods中配置的函数,不要用箭头函数,否则this就不是vm而是windows
- methods中配置的函数,都是被VUE所管理的函数,this的指向是vm或组件实例对象
- @click="demo" 和 @click = "demo($event)"效果一致,但后者可以传参
Vue中的事件修饰符
- prevent:阻止默认事件
- stop:阻止事件冒泡
- once:事件只触发一次
- capture:使用事件的捕获模式
- self:只有event.target是当前操作的元素是才触发事件
- passive:事件的默认行为立即执行,无需等待
计算属性computed与监听属性watch
computed:根据已有数据计算出一个结果
watch:监听某个数据的变化,并在变化时执行相应的操作
computed适合计算并返回结果,methods适合执行操作,watch适合在数据变化时执行逻辑
响应式属性--Vue.set()
js
Vue.set(object, propertyName, value)
Vue2对对象后来新增的属性,不能自动建立响应式监测
数组下标更新
在 Vue 2 中,直接通过数组下标修改元素,Vue 无法可靠地检测到这种变化,通常使用两种方法解决:
-
splice(0,1,'小赵')
先删除原位置的,再插入到原位置
-
$set(this.students,0,'小赵')
直接设置元素
Vue.set() 与 this.$set() 的使用
- 区别
- Vue.set(object,key,value):通过Vue构造函数调用
- this.$set(object,key,value):通过当前Vue实例调用
- this.$set() 对象新增属性,通过数组下标修改元素
过滤器filters
通常用于格式化显示内容,不负责修改原始数据
html
<p>{{message | hello}}</p>
- 局部过滤
js
new Vue({
filters:{
hello(value) {
return value + '!'
}
}
})
- 全局过滤
js
Vue.filter('hello',function(value) {
return value + '!'
})
- 局部过滤器写在 filters 中
- 全局过滤器使用 Vue.filter('名称', 函数) 注册
- value 是传入过滤器的第一个参数
- 可以通过 {{ message | format('参数') }} 传递额外参数
Vue 3 已移除过滤器功能,通常改用方法或计算属性
自定义指令
- 自定义指令用于封装直接操作 DOM 的行为。例如,让输入框在元素插入页面后自动获得焦点
- Vue 2 中可以使用 Vue.directive() 注册全局指令,也可以在组件中通directives 注册局部指令
学生管理页面知识总结
列表渲染与数据管理
- v-for:遍历数组
v-for 可以把数组中的数据逐条显示在页面上
js
<li v-for="(student, index) in userList" :key="student.id">
{{ index + 1 }}. {{ student.name }}
</li>
- student:当前遍历到的学生对象
- index:当前元素的下标,从 0 开始
- student.id:每个学生的唯一标识
- index + 1:让页面序号从 1 开始显示
- 为什么要使用 key?
js
:key="student.id"
key 用来帮助 Vue 识别列表中的每一项
重点:当列表会添加、删除或重新排序时,优先使用稳定且唯一的 ID,不要随意使用 index 作为 key
- 学生数据从字符串升级为对象
最初可能是:
js
userList: ['张三', '李四']
后来升级为:
js
userList: [
{ id: 1, name: '张三' },
{ id: 2, name: '李四' }
]
对象可以同时保存学生的多个属性,比如 id、name、age 等
添加学生时,可以用 nextId 生成唯一 ID:
js
this.userList.push({
id: this.nextId++,
name: this.username.trim()
})
其中:
- push():向数组末尾添加元素
- trim():去掉字符串首尾的空格
- this.nextId++:取出当前 ID,再将 ID 加一
添加、删除与搜索
- 添加学生:v-model + @click
js
<input v-model="username" placeholder="请输入学生姓名">
<button @click="add">添加</button>
- v-model:实现表单输入与 Vue 数据的双向绑定
- @click="add":点击按钮时调用 add 方法
添加前先检查输入是否为空:
js
if (this.username.trim() === '') {
alert('姓名为空,请重新输入')
return
}
这里的 return 用来提前结束函数,避免添加空姓名
添加完成后:
js
this.username = ''
清空输入框
- 删除学生:splice() 与 findIndex()
在没有搜索过滤时,可以根据下标删除:
js
this.userList.splice(index, 1)
但当列表经过搜索过滤后,显示列表的下标不一定等于原数组的下标
所以我们现在采用 ID 删除:
js
del: function (id) {
var index = this.userList.findIndex(function (student) {
return student.id === id
})
if (index !== -1) {
this.userList.splice(index, 1)
}
}
记住这两个方法:
- findIndex():找到满足条件的元素下标;找不到返回 -1
- splice(index, 1):从数组的指定下标删除一个元素
核心原则: 搜索后的列表可以用于展示,但删除时要准确定位原数组中的学生
- 搜索学生:computed + filter()
搜索框:
js
<input v-model="search" placeholder="请输入学生姓名">
计算属性:
js
computed: {
filterstudents: function () {
if (this.search === '') {
return this.userList
}
var keyword = this.search
return this.userList.filter(function (student) {
return student.name.includes(keyword)
})
}
}
这里有三个关键点:
- computed:根据已有数据计算出需要的结果
- filter():筛选出符合条件的数组元素,返回一个新数组
- includes():判断字符串中是否包含指定内容
例如,搜索关键词为"张",那么"张三""张小明"都能被筛选出来
小优化:为了让只输入空格时也显示全部学生,可以把搜索关键词处理为:
js
var keyword = this.search.trim()
然后判断 keyword === ''
- computed 和 methods 有什么区别?
对比 computed methods
主要用途 计算并返回结果 执行操作或处理事件
典型例子 搜索过滤后的学生列表 添加学生、删除学生
缓存 有缓存,依赖数据不变时可复用结果 调用时通常重新执行
简单记忆:计算结果用 computed,点击后执行操作用 methods
条件渲染与统计
- v-if 和 v-else-if
可以根据不同条件显示不同提示:
js
<p v-if="userList.length === 0">
暂无学生,请先添加
</p>
<p v-else-if="filterstudents.length === 0">
没有找到匹配学生
</p>
<p v-else>
当前学生人数:{{ filterstudents.length }}
学生总人数:{{ userList.length }}
</p>
这三个条件按顺序判断:
- 原始数组为空:还没有学生
- 原始数组不为空,但搜索结果为空:没有匹配项
- 其他情况:显示人数统计
- 区分两种人数
- filterstudents.length:当前搜索结果中的人数。
- userList.length:所有学生的总人数。
注意:如果想统计所有学生,即使正在搜索,也应该使用 userList.length
组件化:把学生列表拆出来
当一个页面越来越复杂时,可以把列表部分拆成独立组件
Vue 2 中可以使用 Vue.component() 注册全局组件:
js
Vue.component('student-list', {
props: ['students'],
template: '<ul>...</ul>'
})
- 父组件向子组件传数据:props
父组件:
js
<student-list :students="filterstudents"></student-list>
子组件:
js
props: ['students']
含义:
- 父组件把 filterstudents 传给子组件
- 子组件通过 students 接收数据
- 子组件使用 v-for 遍历 students
- 子组件向父组件传递事件:$emit
子组件中的删除按钮:
js
<button @click="$emit('delete-student', student.id)">
删除
</button>
父组件监听事件:
js
<student-list
:students="filterstudents"
@delete-student="del">
</student-list>
这表示子组件触发 delete-student 事件,并把学生 ID 传给父组件;父组件收到事件后调用 del 方法
- 父子组件通信总结
父组件
持有 userList、add、del、filterstudents
props:父传子
传递 filterstudents
子组件 student-list
接收 students,展示列表
$emit:子通知父
发送 delete-student 和学生 ID
父组件执行删除
调用 del(id),更新原数组
重点
- 父传子:props
- 子通知父:$emit
- 父组件监听:@事件名="方法名"
生命周期
什么是生命周期?
Vue 实例从创建、初始化数据、编译模板、挂载 DOM,到数据更新以及最终销毁,会经历一系列特定的阶段
这些阶段被称为 Vue 的生命周期
Vue 在不同生命周期阶段提供了对应的生命周期钩子函数(Lifecycle Hooks),开发者可以在这些阶段执行相应的操作
例如:
- 实例创建时初始化数据
- 页面挂载完成后操作 DOM
- 数据更新后执行相关操作
- 实例销毁前清理定时器、事件监听等资源
Vue 2 生命周期的完整执行顺序
Vue 2 常见的生命周期钩子按照执行顺序分为以下八个阶段:
| 执行顺序 | 生命周期钩子 |
| -------- | ------------- | -------------------------------------------------------- |
| 1 | beforeCreate | 实例初始化之后,数据观测和事件配置之前 |
| 2 | created | 实例创建完成,数据观测、计算属性、方法和事件等初始化完成 |
| 3 | beforeMount | 模板编译完成,即将挂载到页面 |
| 4 | mounted | 实例挂载完成,可以访问真实 DOM |
| 5 | beforeUpdate | 响应式数据发生变化,DOM 更新之前 |
| 6 | updated | DOM 更新完成之后 |
| 7 | beforeDestroy | 实例销毁之前 |
| 8 | destroyed | 实例销毁之后 |
可以把生命周期分为四个主要阶段:
- 创建阶段:beforeCreate、created
- 挂载阶段:beforeMount、mounted
- 更新阶段:beforeUpdate、updated
- 销毁阶段:beforeDestroy、destroyed
生命周期执行流程图
text
创建 Vue 实例
|
v
beforeCreate
|
v
初始化数据、事件等
|
v
created
|
v
编译模板、生成渲染函数
|
v
beforeMount
|
v
创建并挂载真实 DOM
|
v
mounted
|
v
-------------------
| |
数据发生变化 数据未变化
|
v
beforeUpdate
|
v
更新虚拟 DOM 和真实 DOM
|
v
updated
|
v
实例准备销毁
|
v
beforeDestroy
|
v
销毁实例、清理相关资源
|
v
destroyed
各生命周期钩子的具体作用
- beforeCreate
执行时机:Vue 实例刚刚初始化完成,此时数据观测和事件配置尚未完成
特点:
data尚未完成响应式处理methods等实例选项尚未完成初始化- 不能正常通过实例访问已经初始化的
data数据
适用场景:很少直接使用,通常用于极早期的初始化逻辑
- created
执行时机:Vue 实例创建完成,数据观测、计算属性、方法和事件等初始化已经完成
特点:
- 可以访问
data中的数据 - 可以调用
methods中的方法 - 可以使用计算属性
- 此时还没有完成 DOM 挂载,不能通过
$el获取已经挂载完成的页面 DOM
适用场景:
- 请求初始化数据
- 初始化非 DOM 相关的数据
- 执行一些初始计算
示例:
javascript
new Vue({
data() {
return {
username: '小明'
}
},
created() {
console.log(this.username)
}
})
- beforeMount
执行时机:模板编译完成,即将把 Vue 实例挂载到页面
特点:
- 数据已经完成初始化
- 模板已经准备好
- 真实 DOM 尚未完成挂载
适用场景:一般不需要在这里进行大量操作
- mounted
执行时机:Vue 实例已经完成挂载,真实 DOM 已经生成
特点:
- 可以通过
this.$el访问当前组件的根 DOM - 可以操作已经挂载的 DOM
- 子组件的挂载通常先于父组件完成
适用场景:
- 初始化依赖 DOM 的第三方插件
- 获取 DOM 元素
- 初始化图表
- 执行需要页面元素存在才能进行的操作
示例:
javascript
new Vue({
el: '#app',
mounted() {
console.log(this.$el)
}
})
注意:mounted 只保证当前实例的 DOM 已挂载,不代表所有异步组件或所有异步更新都已经完成
- beforeUpdate
执行时机:响应式数据发生变化,Vue 准备重新渲染 DOM,但 DOM 还没有更新
特点:
- 数据已经发生变化
- 此时访问 DOM,得到的通常还是更新前的 DOM
- 可以在这里获取更新前的 DOM 状态
适用场景:需要在 DOM 更新之前获取旧状态时使用
- updated
执行时机:由于响应式数据变化,DOM 更新完成之后
特点:
- 数据已经更新
- DOM 已经更新
- 可以获取更新后的 DOM
适用场景:需要在数据更新后执行与 DOM 有关的操作
注意:
不要在 updated 中无条件修改响应式数据,否则可能引起重复更新,甚至无限更新
- beforeDestroy
执行时机:Vue 实例即将销毁,但此时实例仍然可以正常使用
特点:
- 实例仍然存在
- 数据和方法仍然可以访问
- 适合在销毁前清理资源
适用场景
- 清除定时器
- 移除手动添加的事件监听
- 取消不再需要的订阅
- 清理第三方插件
示例:
javascript
beforeDestroy() {
clearInterval(this.timer)
window.removeEventListener(
'resize',
this.handleResize
)
}
- destroyed
执行时机:Vue 实例已经销毁
特点:
- Vue 实例的响应式系统已经停止
- 实例的事件监听器已经被移除
- 子组件和相关指令也会被销毁
适用场景:执行销毁完成后的通知或清理工作
生命周期中的数据与 DOM
数据初始化完成,不代表 DOM 已经生成。
| 生命周期 | 能否访问 data | 是否完成 DOM 挂载 |
|---|---|---|
| beforeCreate | 尚未完成初始化 | 否 |
| created | 可以 | 否 |
| beforeMount | 可以 | 否 |
| mounted | 可以 | 是 |
| beforeUpdate | 可以 | 已有旧 DOM |
| updated | 可以 | 已完成本次 DOM 更新 |
| beforeDestroy | 可以 | 尚未完成销毁 |
| destroyed | 不应再依赖实例正常运行 | 已销毁 |
重点记忆:
- created:适合请求数据,不适合直接操作已挂载 DOM
- mounted:适合操作已经挂载的 DOM
- beforeUpdate:获取更新前的 DOM 状态
- updated:获取更新后的 DOM 状态
- beforeDestroy:清理资源
$nextTick:等待 DOM 更新完成
Vue 采用异步更新策略
当修改响应式数据时,Vue 不一定立即更新真实 DOM,而是会把更新任务放入异步更新队列,在合适的时机批量执行
因此:
javascript
this.message = '你好'
console.log(this.$el.innerText)
这里不一定能立即获取到更新后的 DOM 内容
如果希望在 DOM 更新完成之后执行操作,可以使用:
javascript
this.message = '你好'
this.$nextTick(() => {
console.log(this.$el.innerText)
})
$nextTick() 的作用:
等待 Vue 完成本轮 DOM 更新后,再执行传入的回调函数
常见使用场景:
- 修改数据后获取更新后的 DOM。
- 修改列表数据后获取新生成的元素。
- 数据更新后设置输入框焦点。
重点区别:
- 修改 data:数据变化。
- DOM 更新:Vue 根据变化重新渲染页面。
$nextTick():等待本轮 DOM 更新完成后执行回调。
v-if 和 v-show 的区别
这两个指令经常与生命周期和组件销毁联系在一起。
| 对比 | v-if | v-show |
|---|---|---|
| 实现方式 | 条件性创建和销毁元素 | 修改 CSS display 属性 |
| 初始条件为 false | 不创建对应元素 | 元素仍然创建 |
| 切换成本 | 较高 | 较低 |
| 初始渲染成本 | 条件为 false 时较低 | 无论条件如何都会渲染 |
| 适用场景 | 不经常切换 | 频繁切换 |
示例:
html
<div v-if="isVisible">
使用 v-if
</div>
<div v-show="isVisible">
使用 v-show
</div>
区别:
当 isVisible 为 false 时:
- v-if:对应元素不会被渲染到 DOM 中
- v-show:元素仍然存在,只是通过
display: none隐藏
如果 v-if 控制的是组件,条件切换为 false 时,组件实例会被销毁;再次变为 true 时,会重新创建实例
如果 v-show 控制的是组件,组件实例通常仍然存在,只是显示或隐藏
注意:v-if 和 v-show 的区别不只是显示效果,还会影响组件的创建、销毁和生命周期执行
Vue 2 组件化开发
什么是组件?
组件(Component)是 Vue 中非常重要的概念
可以把组件理解为一个独立、可复用的页面功能模块
组件化开发的优点:
- 提高代码复用性
- 降低代码耦合度
- 便于维护和管理
- 让页面结构更加清晰
- 方便多人协作开发
组件的注册方式
Vue2 中主要有两种组件注册方式:
- 全局组件
- 局部组件
全局组件
通过 Vue.component() 注册。
语法:
javascript
Vue.component('组件名称', {
template: '<div>组件内容</div>'
})
示例:
javascript
Vue.component('my-box', {
template: '<div>我是一个全局组件</div>'
})
使用:
html
<div id="app">
<my-box></my-box>
</div>
特点:
- 全局注册后,可以在当前 Vue 应用的组件范围内使用
- 不需要在每个使用它的父组件中重复注册
- 适合多个地方都需要使用的公共组件
局部组件
通过 Vue 实例或其他组件的 components 选项注册
示例:
javascript
var MyBox = {
template: '<div>我是一个局部组件</div>'
}
new Vue({
el: '#app',
components: {
'my-box': MyBox
}
})
特点:
- 只在注册它的组件及其后代组件的模板范围内可用
- 其他互不相关的组件不能直接使用
- 适合只在特定区域使用的组件
如果 app1 和 app2 都需要使用同一个局部组件,可以分别注册:
javascript
new Vue({
el: '#app1',
components: {
'my-box': MyBox
}
})
new Vue({
el: '#app2',
components:{
'my-box':MyBox
}
})
这里不需要重新定义 MyBox,只需要分别注册
全局组件和局部组件的区别

组件模板的根元素要求
Vue 2 的组件模板通常只能有一个根元素
错误示例:
js
Vue.component('my-box', {
template: `
<h3>商品名称</h3>
<p>商品价格:100元</p>
`
})
这里存在两个并列的根元素
正确示例:
js
Vue.component('my-box', {
template: `
<div>
<h3>商品名称</h3>
<p>商品价格:100元</p>
</div>
`
})
使用一个 div 将多个元素包裹起来,就只有一个根元素了
注意:这是 Vue 2 的要求,不要与 Vue 3 支持多根节点的特性混淆
组件复用与组件实例
一个组件定义可以重复使用多次
例如:
js
Vue.component('my-box', {
template: '<div>我是一个盒子</div>'
})
页面中:
html
<div id="app">
<my-box></my-box>
<my-box></my-box>
<my-box></my-box>
</div>
页面会显示三个盒子
重点理解两个概念:
组件定义: 相当于一个模具,只需要定义一次
组件实例: 每使用一次组件,Vue 就会创建一个对应的组件实例
例如:
js
<my-box></my-box>
<my-box></my-box>
这里使用了两次组件,因此会创建两个独立的组件实例
组件中的 data 为什么必须是函数?
普通 Vue 实例:
js
new Vue({
data: {
count: 0
}
})
组件中的 data:
js
Vue.component('my-counter', {
data() {
return {
count: 0
}
},
template: `
<button @click="count++">
{{ count }}
</button>
`
})
为什么组件中的 data 必须是函数?
因为组件可能被重复使用多次
如果多个组件实例共享同一个 data 对象,那么修改其中一个实例的数据,其他实例也可能受到影响
例如:
js
var obj = {
count: 0
}
Vue.component('my-counter', {
data() {
return obj
}
})
这里虽然 data 是函数,但每次都返回同一个 obj 对象,因此多个组件实例仍然会共享数据
正确的做法是:
js
Vue.component('my-counter', {
data() {
return {
count: 0
}
}
})
每次创建组件实例时,调用 data 函数都会返回一个新的对象
这样每个实例就拥有独立的数据
核心记忆:Vue 2 组件的 data 必须是函数,并且每次调用都应该返回一个新的对象
父组件向子组件传递数据:props
组件之间经常需要传递数据
父组件向子组件传递数据,可以使用 props
父组件:
js
new Vue({
el: '#app',
data() {
return {
student: {
no: 1001,
name: '小明'
}
}
}
})
父组件模板:
html
<div id="app">
<student-info :student="student"></student-info>
</div>
子组件:
js
Vue.component('student-info', {
props: ['student'],
template: `
<div>
<p>学号:{{ student.no }}</p>
<p>姓名:{{ student.name }}</p>
</div>
`
})
理解:
父组件通过 :student="student" 传递数据
子组件通过 props 接收数据
子组件通过 student 访问传入的数据
注意:
student 是子组件接收的 prop 名称,右侧的 student 是父组件中的数据
props 是父组件向子组件传递数据的主要方式
props 的基本类型校验
props 不仅可以接收数据,还可以指定数据类型
示例:
js
Vue.component('student-info', {
props: {
name: String,
age: Number,
hobbies: Array,
user: Object,
isStudent: Boolean
}
})
常见数据类型:

props 的默认值 default
当父组件没有传递某个 prop 时,可以使用 default 设置默认值
示例:
js
props: {
name: {
type: String,
default: '匿名用户'
},
age: {
type: Number,
default: 18
}
}
如果父组件没有传递 name 和 age,组件就会使用默认值
特别注意:
对于 Array 和 Object 类型,默认值应该使用工厂函数返回
数组:
js
props: {
list: {
type: Array,
default: () => []
}
}
对象:
js
props: {
user: {
type: Object,
default: () => ({})
}
}
原因:
避免多个组件实例共享同一个数组或对象引用
props 的必填校验 required
可以使用 required 设置某个 prop 是否为必填项
示例:
js
props: {
title: {
type: String,
required: true
}
}
含义:
父组件必须传递 title 属性
如果没有传递,Vue 会在控制台给出警告
注意:
required 和 default 的作用不同
required:规定是否必须传递
default:规定没有传递时使用什么默认值
required 使用布尔值:
required: true
不要写成:
required: 'true'
props 自定义校验 validator
除了基本的数据类型校验,还可以使用 validator 自定义校验规则
示例:
js
props: {
price: {
type: Number,
validator(value) {
return value >= 0
}
}
}
含义:
商品价格必须大于或等于 0
如果传入负数,就不符合校验规则
validator 的返回值:
return true:校验通过
return false:校验不通过,Vue 给出警告
再看一个年龄校验:
js
props: {
age: {
type: Number,
validator(value) {
return value >= 1 && value <= 120
}
}
}
注意:
validator 只是开发阶段的校验机制,不会自动修改错误数据,也不会阻止组件接收这个值。实际业务中如果必须保证数据合法,还需要在业务逻辑中进行处理
props 的单向数据流
这是组件通信中非常重要的原则
Vue 2 中,props 是单向数据流:
父组件的数据传递给子组件,子组件不应该直接修改父组件传递过来的 prop
例如:
props: 'student'
子组件中:
this.student.name = '小红'
如果 student 是一个对象,这种写法可能会修改父组件共享对象中的属性
虽然 JavaScript 对象是引用类型,修改嵌套属性可能影响父组件中的数据,但这并不意味着应该这样做
Vue 2 会对直接修改 prop 本身给出警告,例如:
this.student = {}
推荐做法:
父组件负责管理数据
子组件负责展示数据
子组件需要修改数据时,通过 $emit 通知父组件
父组件接收事件后修改自己的数据
这就是父子组件通信的基本原则