Date: July 29, 2023
Sum: Computed计算属性、watch侦听器、水果车
概念:
基于现有的数据,计算出来的新属性。 依赖的数据变化,自动重新计算。
计算属性本质上就是一个 function 函数,它可以实时监听 data 中数据的变化,并 return 一个计算后的新值,供组件渲染 DOM 时使用。
应用场景:
表单中的数据变化,会导致结果也跟着变化
语法:先声明后使用
1-声明在 computed 配置项中,一个计算属性对应一个函数
2-使用起来和普通属性一样使用 {{ 计算属性名}}
案例: 小黑的礼物清单
需求:礼物总数与表单中的数量之和同步
Code:
Document
小黑的礼物清单
名字
数量
{{ item.name }}
{{ item.num }}个
礼物总数:{{ plus }} 个
reduce参考:https://blog.csdn.net/qq_38970408/article/details/121018660
注意:计算属性侧重于得到一个计算的结果,因此计算属性中必须有 return 返回值!
使用注意点:
① 计算属性必须定义在 computed 节点中
② 计算属性必须是一个 function 函数
③ 计算属性必须有返回值
④ 计算属性必须当做普通属性使用
比如 {{plus}}, 而非是这样用:{{plus()}}
1-computed计算属性
作用:封装了一段对于数据的处理,求得一个结果
语法:
2-methods计算属性
作用:给Vue实例提供一个方法,调用以处理业务逻辑。
语法:
计算属性的优势
1-缓存特性:(提升性能)计算属性会对计算出来的结果缓存,再次使用直接读取缓存,依赖项变化了,会自动重新计算 → 并再次缓存
2-methods没有缓存特性
案例:
理解:计算属性会对计算的结果缓存,下一次直接提缓存结果即可,如图计算属性就仅计算了一次,而methods是调用几次就计算几次。很明显,前者资源开销更低。
Code:
{{ count }} 乘以2的值为: {{ plus }}
{{ count }} 乘以2的值为: {{ plus }}
{{ count }} 乘以2的值为: {{ plus }}
{{ count }} 乘以2的值为: {{ m_plus() }}
{{ count }} 乘以2的值为: {{ m_plus() }}
{{ count }} 乘以2的值为: {{ m_plus() }}
注意:
计算属性的结果会被缓存,性能好
方法的计算结果无法被缓存,性能低
计算属性也是属性,能访问,也应能修改
注:建议结合以下例子理解
computed: {
计算属性名: {
get(): {
代码逻辑
return res
},
set(修改的值): {
代码逻辑
}
}
}
案例:通过改名卡,能够修改姓和名
Code:
Document
姓: +
名: =
{{ fullName }}
案例需求,使用计算属性动态计算:
① 已勾选的商品总个数 ② 已勾选的商品总价 ③ 结算按钮的禁用状态
Code:
computed: {
//动态计算出勾选水果的总数量
total() {
let t = 0
this.fruitlist.forEach(x => {
if(x.state) {
t += x.count
}
})
return t
},
amount() {
let a = 0
this.fruitlist
.filter(x => x.state)
.forEach(x => {
a += x.price * x.count
})
return a
},
isDisabled() {
this.total === 0
}
},
效果:
注意:
这里的this指的是当前组件实例
computed: {
isDisabled() {
this.toal === 0
}
}
计算属性
概念:计算属性本质上就是一个 function 函数,它可以实时监听 data 中数据的变化,并 return 一个计算后的新值,供组件渲染 DOM 时使用。
export default {
name: 'MyCounter',
data() {
return {
count: 1,
}
},
computed: {
// 计算属性:监听data中count值的变化,自动计算出count*2之后的新值
plus() {
return this.count * 2
}
}
}
概念:
监视数据变化,执行一些业务逻辑或异步操作。
应用场景:
1-监视用户名的变化并发起请求,判断用户名是否可用。
2-实时翻译
语法:
在 watch 节点下,定义自己的侦听器。
export default {
data() {
return {
username: ''
}
},
watch: {
// 监听 username 的值的变化
// 形参列表中,第一个值是“变化后的新值”,第二个值是“变化之前的旧值”
// 注:oldVal 可以不传
username(newVal, oldVal) {
console.log(newVal, oldVal);
}
}
}
进一步:如果你想要监视某个复杂数据类型中的子属性
const app = new Vue({
el: '#app',
data: {
// words: ''
obj: {
words: ''
}
},
watch: {
'obj.words' (newValue) {
console.log('变化了', newValue)
}
}
案例:实时翻译
Code:
Document
翻译成的语言:
⌨️文档翻译
mela
补充案例:使用 watch 检测用户名是否可用
特点:采用解构获取数据
监听 username 值的变化,并使用 axios 发起 Ajax 请求,检测当前输入的用户名是否可用:
复习并理解:
返回promise对象:
const res = axios.get('http://www.escook.cn/api/finduser/' + newVal)
若返回promise对象,我们可以通过await/async进行简化,返回的则是一个数据对象,
async username(newVal, oldVal) {
console.log(newVal, oldVal);
const res = await axios.get('http://www.escook.cn/api/finduser/' + newVal)
console.log(res);
}
案例:实时翻译
需求:防止一直输入,一直及时翻译,而是等输入完,再翻译,以提高性能
Code:
Document
翻译成的语言:
⌨️文档翻译
{{ result }}
注意:如果你想使用一些非响应式的数据,你可以通过this把它们挂载到对象身上,而非存到data中
如果结果需要受到多个条件来查询控制,我们通常会把它放入到一个obj对象中进行控制。
比如下面:翻译的结果受到 语言 与 被翻译的内容 两方面控制
问题:如果结果会受到多方面的控制,该怎么办?
解决方案:添加额外配置项
(1) deep: true 对复杂类型进行深度件事
(2) immediate: true 初始化立刻执行一次handler方法
采用deep:翻译结果会受到 语言与被翻译内容 二者影响
watch: {
// 该方法会在数据变化时调用执行
// newValue新值, oldValue老值(一般不用)
// words (newValue) {
// console.log('变化了', newValue)
// }
obj: {
deep: true,
async handler(newValue) { // newValue指obj
clearTimeout(this.timer)
// 你可以把上面data中的timer清除,下面依然可以执行
// 因为this会把timer挂载到obj身上
this.timer = setTimeout(async() => {
const res = await axios({
url: 'https://applet-base-api-t.itheima.net/api/translate',
params: newValue
})
this.result = res.data.data
console.log(res.data.data);
}, 200)
}
},
}
注:以上我直接把obj作为对象以参数的方式传入,axios会把obj转成参数(用&拼接对象属性)
采用immediate: 文本框中预留内容,页面一加载就会被自动翻译。原理上讲,就是一进页面就会执行一次下面的 handler 方法
watch: {
obj: {
deep: true, // 深度监视
immediate: true, // 立刻执行,一进入页面handler就立刻执行一次
handler (newValue) {
clearTimeout(this.timer)
this.timer = setTimeout(async () => {
const res = await axios({
url: 'https://applet-base-api-t.itheima.net/api/translate',
params: newValue
})
this.result = res.data.data
console.log(res.data.data)
}, 300)
}
}
}
结合以上二者的案例效果:
Code:
Document
翻译成的语言:
⌨️文档翻译
{{ result }}
默认情况下,组件在初次加载完毕后不会调用 watch 侦听器。如果想让 watch 侦听器立即被调用,则需要使用 immediate 选项。实例代码如下:
当 watch 侦听的是一个对象,如果对象中的属性值发生了变化,则无法被监听到。此时需要使用 deep 选项,代码示例如下:
data() {
return {
username: 'admin',
info: {
username: 'zs' //info中包含 username 属性
}
}
},
watch: {
info: { //直接监听 info 对象的变化
async handler(newVal) {
const { data: res } = await axios.get('https://www.escook.cn/api/finduser' + newVal.username)
console.log(res);
},
deep: true // 需要使用 deep 选项, 否则 username 值的变化无法被监听到
}
}
如果只想监听对象中单个属性的变化,则可以按照如下的方式定义 watch 侦听器:
计算属性和侦听器侧重的应用场景不同:
计算属性侧重于监听多个值的变化,最终计算并返回一个新值
计算属性侧重于得到最后的一个结果
侦听器侧重于监听单个数据的变化,最终执行特定的业务处理,不需要有任何返回值
侦听器侧重于去执行某个业务逻辑,业务逻辑执行完,侦听器的目的就达到了
watch 侦听器:
概念:watch 侦听器允许开发者监视数据的变化,从而针对数据的变化做特定的操作
简单写法:
watch: {
数据属性名(newVal, oldVal) {
业务逻辑 或 异步操作
}
‘对象.属性名’(newVal, oldVal) {
业务逻辑 或 异步操作
}
}
完整写法:
watch: {
数据属性名:{
deep: true, // 深度件事
immediate: true, // 是否立刻执行一次 handler
handler (newValue) {
业务逻辑 或 异步操作
}
}
}
注意:
组件在初次加载完毕后不会调用 watch 侦听器。如果想让 watch 侦听器立即被调用,则需要使用 immediate 选项。
当 watch 侦听的是一个对象,如果对象中的属性值发生了变化,则无法被监听到。此时需要使用 deep 选项
需求说明:
实现思路:
1.基本渲染: v-for遍历、:class动态绑定样式
2.删除功能 : v-on 绑定事件,获取当前行的id
3.修改个数 : v-on绑定事件,获取当前行的id,进行筛选出对应的项然后增加或减少
4.全选反选
4-1 必须所有的小选框都选中,全选按钮才选中 → every
4-2 如果全选按钮选中,则所有小选框都选中
4-3 如果全选取消,则所有小选框都取消选中
声明计算属性,判断数组中的每一个checked属性的值,看是否需要全部选
5.统计 选中的 总价 和 总数量 :通过计算属性来计算选中的总价和总数量
6.持久化到本地: 在数据变化时都要更新下本地存储 watch
参考:day2
实现渲染功能:
对于表单数据,一般采用v-mode处理。
比如处理勾选框
div class="td">
对于处理图片地址,一般用 v-bind ,而不能这样用:
以上这样会报错
插值表达式一般用于渲染标签内内容,用法如下:
姓名:{{username}}
Code: 已实现渲染功能
购物车
选中
图片
单价
个数
小计
操作
{{ item.price }}
{{ item.num }}
{{ item.price * item.num }}
总价 : ¥ 24
空空如也
实现删除、修改与全选反选:
正反选问题:
采用computed来实现正选:即如果上面都勾上,那么下面的全选也勾上
问题是反选,如果我在下面勾上反选,会报计算属性的错误,也就是计算属性没设置全的问题
完整的计算属性
computed: {
计算属性名: {
get(): {
代码逻辑
return res
},
set(修改的值): {
代码逻辑
}
}
}
解决方案:
computed: {
isAll: {
get() {
return this.fruitList.every(item => item.isChecked === true)
},
set(value) {
this.fruitList.forEach(item => item.isChecked = value)
}
}
},
Code:
购物车
选中
图片
单价
个数
小计
操作
{{ item.price }}
{{ item.num }}
{{ item.price * item.num }}
总价 : ¥ 24
空空如也
实现总价:
问题:
reduce与箭头的使用方式:如果里面箭头函数{}包含多行代码,记得要return
let total = this.fruitList.reduce((sum, item) => {
if(item.isChecked === true){
return sum += item.num
}else {
return sum
}
}, 0)
Code:
持久化到本地:
问题:任何数据的变化,都需要监视并存取
策略:通过watch的deep: true以及 localStorage来进行处理
watch: {
fruitList: {
deep: true,
handler(newValue) {
localStorage.setItem('list', JSON.stringify(newValue))
}
}
}
通过浏览器开发者模式查看信息:
补充:通过这个键清空本地缓存
所有代码:
Code:
购物车
选中
图片
单价
个数
小计
操作
{{ item.price }}
{{ item.num }}
{{ item.price * item.num }}
总价 : ¥ {{ totalPrice }}
空空如也
业务总结: