声明:本文基于黑马程序员VUE课程学习,文中大量引用了公开资料中的图片及代码,如有侵权请联系本人
1.watch 监听器
watch 侦听器允许开发者监视数据的变化
,从而针对数据的变化做特定的操作
。
在 watch 节点
下,定义
自己的侦听器:
1.2 使用 watch 检测用户名是否可用
(axios 发起 Ajax 请求)
这时候返回的是一个promise对象
,我们可以用await
去简化,与之对应的该方法前要加上async
关键字。
此时,res
返回的就是一个data对象
。
用{ }
解构赋值,赋值出data属性,并通过:
对解构出的数据进行重命名
打印值:
1.3 immediate 选项
默认情况下,组件在初次加载完毕后不会调用 watch 侦听器。如果想让 watch 侦听器立即被调用,则需要使用 immediate 选项。
1.4 deep 选项
当 watch 侦听的是一个对象
,如果对象中的属性值发生了变化
,则无法被监听到。
此时需要使用 deep 选项
,
1.5 监听对象单个属性的变化
1.6 计算属性 vs 侦听器
计算属性和侦听器侧重的应用场景不同:
- 计算属性侧重于监听
多个值的变化
,最终计算并返回一个新值
- 侦听器侧重于监听
单个数据的变化
,最终执行特定的业务处理
,不需要有任何返回值
2. 生命周期
2.1 组件运行的过程
组件的生命周期指的是:组件从创建 -> 运行(渲染) -> 销毁
的整个过程,强调的是一个时间段
vue 框架为组件内置了不同时刻的生命周期函数,生命周期函数会伴随着组件的运行而自动调用,例如
- 当组件在
内存
中被创建完毕之后,会自动调用created
函数 - 当组件
被成功的渲染到页面上
之后,会自动调用mounted
函数 - 当组件
被销毁完毕
之后,会自动调用unmounted
函数
2.2 如何监听组件的更新
当组件的 data 数据更新之后,vue 会自动重新渲染组件的 DOM 结构,从而保证 View 视图展示的数据和Model 数据源保持一致。
当组件被重新渲染完毕之后,会自动调用 updated 生命周期函数。
2.3 组件中主要的生命周期函数
2.4 组件中全部的生命周期函数
因为beforeCreate的过程中是无法访问到date() 的
,发送ajax申请获取到的数据无法存到date()里供组件渲染或后续使用的。
2.5 完整的生命周期函数
官方介绍:生命周期图示
3.组件之间的数据共享
在项目开发中,组件之间的关系分为如下 3 种:
- ① 父子关系
AB AC
- ② 兄弟关系
BC BE BF BI
- ③ 后代关系
AD AG AH
3.1 父子之间的数据共享
父子组件之间的数据共享又分为:
- ①
父 -> 子
共享数据 - ②
子 -> 父
共享数据 - ③
父 <-> 子
双向数据同步
3.1.1 父传子 v-bind 和 props
父组件
通过 v-bind
属性绑定向子组件共享数据。
同时,子组件
需要使用 props
接收数据。
以字符串数组
形式列出的 prop:
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
但是,通常你希望每个 prop 都有指定的值类型。这时,你可以以对象形式
列出 prop,这些属性的名称和值分别是 prop 各自的名称和类型:
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object
}
3.1.2 子传父 $emit自定义事件
子组件
通过自定义事件
的方式向父组件共享数据。
3.1.3 父子共享 v-model
父组件在使用子组件期间,可以使用 v-model 指令
维护组件内外数据的双向同步
:
3.2 兄弟组件之间的数据共享 EventBus
兄弟组件之间实现数据共享的方案是 EventBus
。可以借助于第三方的包 mitt
来创建 eventBus 对象
,从而实现兄弟组件之间的数据共享。
3.2.1 安装 mitt 依赖包
npm install mitt@2.1.0
3.2.2 创建公共的 EventBus 模块
3.2.3 在数据接收方自定义事件
在数据接收方
,调用 bus.on('事件名称', 事件处理函数)
方法注册一个自定义事件
。
3.2.4 在数据接发送方触发事件
在数据发送方
,调用 bus.emit('事件名称', 要发送的数据)
方法触发自定义事件
。
3.3 后代组件之间的数据共享 provide 和 inject
后代关系组件之间共享数据,指的是父节点的组件
向其子孙组件
共享数据。此时组件之间的嵌套关系比较复杂,可以使用 provide
和 inject
实现后代关系组件之间的数据共享。
如果两个组件之间没有直接或间接的嵌套关系
,则不能使用
provide和inject方法实现数据共享的。比如 B E
就不行
3.3.1 父节点通过 provide 共享数据
父节点
的组件可以通过 provide
方法,对其子孙组件
共享数据
3.3.2 子孙节点通过 inject 接收数据
子孙节点
可以使用 inject 数组
,接收父级节点向下共享的数据
。
如果还接收一个count
就是
inject:["color","count"],
3.3.3 父节点对外共享响应式的数据
- 什么是相应式数据?
- 就是 data 中的数据发生改变,provide return的值也随之改变。
父节点使用 provide 向下共享数据时,可以结合 computed 函数
向下共享响应式的数据
。注意在一开始导入computed
3.3.4 子节点使用响应式的数据
如果父级节点共享的是响应式的数据
,则子孙节点必须以 .value
的形式进行使用。
3.4 vuex
vuex 是终极的
组件之间的数据共享方案。
在企业级的 vue 项目开发中,vuex 可以让组件之间的数据共享变得高效、清晰、且易于维护
。
4. 全局配置 axios
4.1 为什么要全局配置 axios
在实际项目开发中,几乎每个组件中都会用到 axios 发起数据请求。此时会遇到如下两个问题:
- 每个组件中都需要
导入 axios
(代码臃肿) - 每次发请求都需要填写
完整的请求路径
(不利于后期的维护)
4.2 如何全局配置 axios
首先安装axios
npm install axios -g
在 main.js
入口文件中,通过 app.config.globalProperties
全局挂载 axios,
$http 是自定义属性的名称,随便写保持一致即可
5. ref 引用
5.1 what is ref?
ref 用来辅助开发者在不依赖于 jQuery 的情况下,获取 DOM 元素或组件
的引用。
每个 vue 的组件实例上,都包含一个 $refs 对象
,里面存储着对应的DOM 元素或组件的引用。默认情况下,组件的 $refs 指向一个空对象
。
5.2 ref 引用 DOM 元素
如果想要使用 ref 引用页面上的 DOM 元素
,则可以按照如下的方式进行操作:
console.log(this.$refs)
打印结果:
5.3 ref 引用组件实例
如果想要使用 ref 引用页面上的组件实例
,则可以按照如下的方式进行操作:
实现功能: 点击父组件的button,重置子组件的counter值。
App 组件
<template>
<div>
<h1 ref="myh11">App 根组件</h1>
<hr />
<button type="button" class="btn btn-primary" @click="getRefs">获取 $refs 引用</button>
<my-counter ref="counterRef"></my-counter>
</div>
</template>
<script>
// 导入组件
import MyCounter from './Counter.vue'
export default {
name: 'MyApp',
// 注册组件
components: {
MyCounter,
},
methods: {
getRefs() {
// console.log(this.$refs.counterRef)
this.$refs.counterRef.reset()
},
},
}
</script>
MyCounter组件:
<template>
<div class="counter-container">
<h3>Counter 组件 --- {{ count }}</h3>
<button type="button" class="btn btn-info" @click="count += 1">+1</button>
</div>
</template>
<script>
export default {
name: 'MyCounter',
data() {
return {
count: 0,
}
},
methods: {
reset() {
this.count = 0
},
},
}
</script>
5.4 文本框和按钮的按需切换并让自动获得焦点
这样会报错:Cannot read property 'focus' of undefined
原因:此时ipt 还未定义
DOM 元素的更新是异步的,当赋值inputVisible = true
后,会立即执行this.$ref.ipt.focus()
,从而希望拿到 ipt
对象,但是此时DOM还未更新,页面上还只有botton
没有input
,拿不到 ipt
对象的。
5.5 this.$nextTick(cb) 方法
组件的 $nextTick(cb) 方法,会把 cb 回调推迟到下一个 DOM 更新周期之后
执行。
通俗的理解是:等组件的DOM 异步地重新渲染完成
后,再执行 cb 回调函数
。从而能保证 cb 回调函数可以操作到最新的 DOM 元素。
6. 动态组件
动态组件指的是动态切换组件的显示与隐藏
。vue 提供了一个内置的 <component>
组件,专门用来实现组件的动态渲染
。
① <component> 是组件的占位符
② 通过 is 属性动态指定要渲染的组件名称
③ <component is="要渲染的组件的名称"></component>
6.2 保持动态组件的状态
默认情况下,切换动态组件时无法保持组件的状态
。此时可以使用 vue 内置的 <keep-alive>
组件保持动态组件的状态。
7. 插槽 Slot
7.1 what is
插槽(Slot)是 vue 为组件的封装者提供的能力。允许开发者在封装组件时,把不确定的、希望由用户指定的部分
定义为插槽。
可以把插槽认为是组件封装期间,为用户预留的内容的占位符
。
7.2 use
7.2.1 基础用法
在封装组件时,可以通过 <slot> 元素
定义插槽,从而为用户预留内容占位符。
//定义
<template>
<p>Mycom1的第一个标签</p>
//为用户预留占位符
<slot></slot>
<p>Mycom1的最后一个标签</p>
</template>
//使用
<Mycom1>
//使用组件的时候用户自定义内容
<p>用户自定义的内容</p>
</Mycom1>
7.2.2 没有预留插槽的内容会被丢弃
如果在封装组件时没有预留任何 <slot> 插槽
,则用户提供的任何自定义内容都会被丢弃
。
7.2.3 后备内容
封装组件时,可以为预留的 插槽提供后备内容
(默认内容)。如果组件的使用者没有为插槽提供任何内容,则后备内容会生效。
//定义
<template>
<p>Mycom1的第一个标签</p>
//为用户预留占位符 如果用户使用过程中提供插槽内容,则会覆盖后备内容
<slot>这里是后备内容</slot>
<p>Mycom1的最后一个标签</p>
</template>
7.2.4 具名插槽
如果在封装组件时需要预留多个插槽节点
,则需要为每个 插槽指定具体的 name 名称
。这种带有具体名称的插槽叫做“具名插槽”。
注意:没有指定 name 名称的插槽,会有隐含的名称叫做 “default”。
在向具名插槽提供内容的时候,我们可以在一个 <template> 元素
上使用 v-slot 指令
,并以 v-slot 的参数的形式提供其名称。
只有默认插槽在使用过程中可以省略template标签包裹
简写形式:
跟 v-on 和 v-bind 一样,v-slot 也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符 #。例如 v-slot:header可以被重写为 #header
<template #header>
<h1>滕王阁序</h1>
</template>
7.2.5 作用域插槽(带props数据)
在封装组件的过程中,可以为预留的 <slot> 插槽绑定 props 数据
,这种带有 props 数据的 叫做“作用域插槽”。
<template>
<div>
<h3>这是 TEST 组件</h3>
<slot :info="infomation" :msg="message"></slot>
</div>
</template>
<script>
export default {
name: 'MyTest',
data() {
return {
// 信息数据
infomation: {
phone: '138xxxx6666',
address: '中国北京',
},
message: 'abc'
}
},
}
</script>
<template>
<div>
<h1>App 根组件</h1>
<hr />
<!-- 使用自定义组件 -->
<my-test>
<template #default="scope">
<p>{{ scope }}</p>
</template>
//渲染出来是一个大对象
// { infomation:{phone: '138xxxx6666',address: '中国北京',}, message: 'abc'}
// 作用域插槽对外提供的数据对象,可以使用解构赋值简化数据的接收过程
<template #default="{ msg, info }">
<p>{{ msg }}</p>
<p>{{ info.address }}</p>
</template>
</my-test>
</template>
声明作用域插槽:
在封装 MyTable 组件的过程中,可以通过作用域插槽
把表格每一行的数据传递给组件的使用者。
<template>
<table class="table table-bordered table-striped table-dark table-hover">
<!-- 表头区域 -->
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>State</th>
</tr>
</thead>
<!-- 表格主体区域 -->
<tbody>
<!-- 循环渲染表格数据 -->
<tr v-for="item in list" :key="item.id">
<slot :user="item"></slot>
</tr>
</tbody>
</table>
</template>
<script>
export default {
name: 'MyTable',
data() {
return {
// 列表的数据
list: [
{ id: 1, name: '张三', state: true },
{ id: 2, name: '李四', state: false },
{ id: 3, name: '赵六', state: false },
],
}
},
}
</script>
在使用 MyTable 组件时,自定义单元格的渲染方式
,并接收作用域插槽对外提供的数据
<my-table>
<template #default="{ user }">
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td>
<input type="checkbox" :checked="user.state" />
</td>
</template>
</my-table>