vue-js-学习-3-响应式基础
上一级页面:vue-js-学习索引
本系列关注前端部分,根据学习路线图达到学习Vue.js的目的
developer路线图developer-roadmap/translations/chinese at master · kamranahmedse/developer-roadmap
前言
预先工作
新建一个src/components/TestReactivity.vue
,内容如下:
<script>
export default {
data() {
return {
message: "Hello Vue! TestReactivity!",
};
},
methods: {
doSomething() {
alert("弹窗!!!");
},
},
};
</script>
<template>
<div class="greetings">
<h1>{{ message }}</h1>
</div>
</template>
<style scoped>
h1 {
font-weight: 500;
font-size: 2.6rem;
top: -10px;
}
h3 {
font-size: 1.2rem;
}
.greetings h1,
.greetings h3 {
text-align: center;
}
@media (min-width: 1024px) {
.greetings h1,
.greetings h3 {
text-align: left;
}
}
</style>
在src/App.vue
中导入
<script setup>
// 保留之前的内容
import HelloWorld from "./components/HelloWorld.vue";
import TheWelcome from "./components/TheWelcome.vue";
// 导入新的内容
import TestReactivity from "./components/TestReactivity.vue";
</script>
更改src/App.vue
的template
<template>
<!-- 这里的内容保持不变...... -->
<main>
<!-- 这里只留TestReactivity,其他的删了...... -->
<TestReactivity/>
</main>
</template>
![Pasted image 20230126170157.png](https://webdav-1309345210.file.myqcloud.com/images/Pasted image 20230126170157.png)
效果如下,注意右侧:
![Pasted image 20230126170043.png](https://webdav-1309345210.file.myqcloud.com/images/Pasted image 20230126170043.png)
响应式基础
声明响应式状态
之前都做过的了
响应式代理 vs. 原始值
export default {
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++
}
},
mounted() {
// 在其他方法或是生命周期中也可以调用方法
this.increment()
}
}
与 Vue 2 不同的是,这里原始的 newObject
不会变为响应式:请确保始终通过 this
来访问响应式状态。
声明方法
前一节使用过了,就是methods方法
methods: {
increment() {
this.count++
}
},
DOM 更新时机
注意这个要导包
import { nextTick } from 'vue'
深层响应性
直接看文档就行,没什么难点,例如这样用
<button @click="mutateDeeply">
mutateDeeply : {{ this.obj.nested.count }} / {{ this.obj.arr }}
</button>
有状态方法--防抖debounce
在前端项目开发工作中,我们经常会遇到搜索查询等类似功能,用户在不断输入值时,只要按下键盘就会触发函数调用。
但有些时候我们并不希望在事件持续触发的过程中那么频繁地去请求接口,只需要在输入完成后做请求。
通常这种情况下我们怎么去解决的呢?debounce
就是用来处理这种情况的。
debounce
,又称防抖动函数。
定义:如果一个函数持续地触发,那么只在它结束后过一段时间只执行一次。
节流是类似的一个东西。节流和防抖存在的含义都是为了性能问题。
但他们还是有区别的。区别在于:
- 防抖适合于输入事件, 等到最后一次输入才执行需要进行的操作。
- 节流适合于点击事件, 第一下点击就能生效, 之后指定时间段内的点击不生效。
debounce最佳实践
这个我还没用过,vue提出的最佳实践如下:
<script>
export default {
created() {
// 每个实例都有了自己的预置防抖的处理函数
this.debouncedClick = _.debounce(this.click, 500)
},
unmounted() {
// 最好是在组件卸载时
// 清除掉防抖计时器
this.debouncedClick.cancel()
},
methods: {
onClick() {
// ... 对点击的响应 ...
}
}
}
</script>
计算属性
计算属性值会基于其响应式依赖被缓存
例如下面这个数据
export default {
data() {
return {
author: {
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
}
}
}
}
author.books
和author.name
在这里创建,它们被称为响应式依赖
- vue默认创建时是响应式的
只要 author.books
不改变,无论多少次访问 publishedBooksMessage
都会立即返回先前的计算结果,而不用重复执行 getter 函数。
computed: {
// 一个计算属性的 getter
publishedBooksMessage() {
// `this` 指向当前组件实例
return this.author.books.length > 0 ? 'Yes' : 'No'
}
}
下面的计算属性永远不会更新,因为 Date.now()
并不是一个响应式依赖:
computed: {
now() {
return Date.now()
}
}
可写计算属性
这个有点难理解,
分析get和set
computed里的方法其实是默认使用了get方法
computed: {
editData () {
return this.value
}
}
// 相当于
computed: {
editData: {
get () {
return this.value
}
}
}
set方法的意义是:如果对计算属性设置值,会阻断赋值的动作,而改为调用计算属性的set方法,
即: 如果你使用this.editData = 2
对editData
赋值,会执行了set方法里的内容。
做一个小实验:
computed: {
editData: {
get () {
return this.value
},
set(tempValue){
// do nothing
}
}
}
这时候你尝试使用this.editData = 2
对editData
赋值,会被阻断,转而调用set方法,
而set方法里面什么也没做(do nothing),这样你的editData
就根本没变。后面会给出一个示例来证明。
这里
get
和set
是固定命名,不可更改newValue是临时参数,可以随意替换,比如改成
set(text)
官方用例
官方给的例子如下
export default {
data() {
return {
firstName: 'John',
lastName: 'Doe'
}
},
computed: {
fullName: {
// getter
get() {
return this.firstName + ' ' + this.lastName
},
// setter
set(newValue) {
// 注意:我们这里使用的是解构赋值语法
[this.firstName, this.lastName] = newValue.split(' ')
}
}
}
}
编写一个用例如下:
添加一个方法changeFullName(text)
methods: {
changeFullName(text){
this.fullName = text
}
},
用法
<p>
<input v-model="text" placeholder="Type here" /> <br />
<button @click="changeFullName(text)">set fullName</button>
</p>
<p>fullName is : {{ fullName }}</p>
set方法拦截赋值操作
changeFullName(text)
内的this.fullName = text
会被拦截,转而调用computed里的set方法。
我们可以做一个小实验,将computed里的set方法更改成这样
computed: {
fullName: {
// getter
get() {
return this.firstName + ' ' + this.lastName
},
// setter
set(newValue) {
// 注意
alert("set操作被拦截了,并且没有更改fullName值!!")
}
}
}
效果如下:
![Pasted image 20230126183815.png](https://webdav-1309345210.file.myqcloud.com/images/Pasted image 20230126183815.png)
并且值也没有改变
![Pasted image 20230126183829.png](https://webdav-1309345210.file.myqcloud.com/images/Pasted image 20230126183829.png)
最佳实践
不要在 getter 中做异步请求或者更改 DOM!
Getter 不应有副作用,计算属性的 getter 应只做计算而没有任何其他的副作用,这一点非常重要,请务必牢记。
避免直接修改计算属性值。计算属性的返回值应该被视为只读的,并且永远不应该被更改——应该更新它所依赖的源状态以触发新的计算。