如果一个值依赖多个属性(多对一),用computed肯定是更加方便的。 如果一个值变化后会引起一系列操作,或者一个值变化会引起一系列值的变化(一对多),用watch更加方便一些。 watch 支持异步代码而computed 不支持
特点:
- 支持缓存,只有依赖数据发生改变,才会重新进行计算;
- 不支持异步,当 computed 内有异步操作时无效,无法监听数据的变化;
- computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的。也就是基 于 data 中声明过或者父组件传递的 props 中的数据通过计算得到的值;
- 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性 是一个多对一或者一对一,一般用computed;
- 如果 computed 属性值是函数,那么默认会走 get 方法,函数的返回值就是属性的属性值;在computed中的,属性都有一个get和一个 set 方法,当数据变化时,调用 set 方法;
使用例子:
<template>
<div>
<span>{{testName}}</span>
<el-input v-model="firstText"></el-input>
<el-input v-model="lastText"></el-input>
<el-input v-model="mergeText1"></el-input>
<el-input v-model="mergeText2"></el-input>
<div>{{fullNameFun()}}</div>
</div>
</template>
<script>
import { defineComponent, computed, ref } from 'vue'
export default defineComponent({
setup() {
let firstText = ref('hello')
let lastText = ref('world')
const mergeText1 = computed(() => firstText.value + ' ' + lastText.value)
const mergeText2 = computed({
// getter
get() {
// 回调函数 当需要读取当前属性值时执行,根据相关数据计算并返回当前属性的值
return `${firstText.value} ${lastText.value}`
},
// setter
set(val) {
//监视当前属性值的变化,当属性值发生变化时执行,更新相关的属性数据,val就是fullName的最新属性值
const names = val.split(' ')
console.log(names)
firstText.value = names[0]
lastText.value = names[names.length - 1]
},
})
function fullNameFun(){
return firstText.value+ ' ' + lastText.value
}
return {
firstText,
lastText,
mergeText1,
mergeText2,
fullNameFun
}
}
})
</script>
优点:
- 当改变 ref 或者 reactive 响应式变量值时,整个应用会重新渲染,vue 会被数据重新渲染到 dom 中。这时,如果我们模板中使用了 methods 中的fullNameFun函数,或者使用了组合式return的函数。随着渲染,方法也会被调用。但是 如果computed中所依赖的变量没有发生改变,则不会进行重新的计算,从而性能开销比较小。当新的值需要大量计算才能得到,缓存的意义就非常大;
- 如果 computed 所依赖的数据发生改变时,计算属性才会重新计算,并进行缓存;当改变其他数据时,computed 属性 并不会重新计算,从而提升性能;
- 当拿到的值需要进行一定处理使用时,就可以使用 computed;
特点:
- 完全等同于vue2 中的watch
- 不支持缓存,数据变化,直接会触发相应的操作;
- watch 支持异步操作;
- 监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
当一个属性发生变化时,需要执行对应的操作,一对多;- 监听数据必须是 data 中声明过或者组合式api中声明的响应式值或者父组件传递过来的 props 中的数据。当数据变化时触发其他操作,函数有两个参数:
immediate:组件加载立即触发回调函数执行;deep: 深度监听;为了发现对象内部值的变化,复杂类型的数据时使用,例如:数组中的对象内容的改变,注意:监听数组的变动不需要这么做。注意:deep无法监听到数组的变动和对象的新增,参考vue数组变异,只有以响应式的方式触发才会被监听到;
注:当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的,这是和 computed 最大的区别。
注:监听一个函数的返回值,当函数里面中所用到的变量发生变化都会触发回调
// 侦听一个 getter
const state = reactive({ count: 0 })
watch(
() => state.count,
(count, prevCount) => {
/* ... */
}
)
// 直接侦听一个 reactive
const count = ref(0)
watch(state , (newValue, oldValue) => {
/* ... */
})
// 直接侦听一个 ref
const count = ref(0)
watch(count, (count, prevCount) => {
/* ... */
})
// 监听多个数据源
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
/* ... */
})
// watch 可以监听一个函数的返回值
watch(() => {
return otherName.firstName + otherName.lastName
},
value => {
// 当otherName中的 firstName或者lastName发生变化时,都会进入这个函数
console.log(`我叫${value}`)
}
)
不使用 deep 时,当我们改变 obj.a 的值时,watch 不能监听到数据变化,默认情况下,watch 只监听属性引用的变化,也就是只监听了一层,但改对象内部的属性是监听不到的。
immerdiate 属性: 通过声明 immediate 选项为 true,可以立即执行一次 watch里面的函数。
import { watch, ref, reactive } from 'vue'
export default {
setup() {
let obj= reactive({
text:'hello'
})
watch(obj, (newValue, oldValue) => {
// 回调函数
}, {
immediate: true,
deep: true
})
return {
obj
}
}
}
通过使用 deep: true 进行深入观察,我们监听 obj,会把 obj 下面的属性层层遍历,都加上监听事件,这样做性能开销也会变大,只要修改 obj 中任意属性值,都会触发回调,那么如何优化性能呢?
可以直接对用对象 . 属性的方法拿到属性,也就是上面说道的侦听一个 getter
import { watch, ref, reactive } from 'vue-router'
export default {
setup() {
let obj= reactive({
text:'hello'
})
watch(()=>obj.text, (newValue, oldValue) => {
// 回调函数
}, {
immediate: true,
deep: true
})
return {
obj
}
}
}
- watch 中的函数名称必须是所依赖 data 中的属性名称;(vue2)
- watch 中的函数是不需要调用的,只要函数所依赖的属性发生了改变 那么相对应的函数就会执行;
- watch 中的函数会有2个参数 一个是新值,一个是旧值;
- watch 默认情况下无法监听对象的改变,如果需要进行监听则需要进行深度监听 深度监听需要配置 handler
(vue2中才需要)函数以及 deep 为true。(因为它只会监听对象的地址是否发生了改变,而值是不会监听的);(vue2)在vue3中有些许区别。在另外一篇文章中再说- watch 默认情况下第一次的时候不会去做监听,如果需要在第一次加载的时候也需要去做监听的话需要设置 immediate:true;
vue2中数组响应式原理:
1 重新定义原生数组方法push unshift shift pop splice sort reverse 因为这些方法可以修改原数组。
2 拿到原生数组方法 Object.create(Array.prototype)
3 AOP拦截,再执行重写数组方法前,先执行原生数组方法
watch 在特殊情况下是无法监听到数组的变化
所以,vue2中对数组的解决方案:
- 通过下标来更改数组中的数据;
- 通过 length 来改变数组的长度;
通过 Vue 实例方法 set 进行设置 $set( target, propertyName/index, value)
参数: target {Object | Array} , propertyName/index {string | number}, value {any}
this.$set(this.arr,0,100);
通过 splice 来数组清空 $delete( target, propertyName/index )
参数:target {Object | Array} , propertyName/index {string | number}
this.$delete(this.arr,0)
vue2中深度监听对应的函数名必须为 handler ,否则无效果,因为 watche r里面对应的是对 handler 的调用
划重点:在vue3中利用的是ES6的proxy,对数据响应式进行一个数据的代理,可以监控到数组的变化。vue3中如果想要让一个对象变为响应式数据,可以使用
reactive或ref。因此$set在vue3中废弃
methods跟前面的都不一样,我们通常在这里写入方法,只要调用就会重新执行一次,相应的有一些触发条件,在某些时候methods和computed看不出来具体的差别,但是一旦在运算量比较复杂的页面中,就会体现出不一样。
注意:computed是具有缓存的,这就意味着只要计算属性的依赖没有进行相应的数据更新,那么computed会直接从缓存中获取值,多次访问都会返回之前的计算结果。
在 computed 和 watch 方面,一个是计算,一个是观察,在语义上是有区别的。
计算是通过变量计算来得出数据,而观察是观察一个特定的值,根据被观察者的变动进行相应的变化,在特定的场景下不能相互混用,所以还是需要注意 api 运用的合理性和语义性。
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
请帮助我理解范围运算符...和..之间的区别,作为Ruby中使用的“触发器”。这是PragmaticProgrammersguidetoRuby中的一个示例:a=(11..20).collect{|i|(i%4==0)..(i%3==0)?i:nil}返回:[nil,12,nil,nil,nil,16,17,18,nil,20]还有:a=(11..20).collect{|i|(i%4==0)...(i%3==0)?i:nil}返回:[nil,12,13,14,15,16,17,18,nil,20] 最佳答案 触发器(又名f/f)是
我正在检查一个Rails项目。在ERubyHTML模板页面上,我看到了这样几行:我不明白为什么不这样写:在这种情况下,||=和ifnil?有什么区别? 最佳答案 在这种特殊情况下没有区别,但可能是出于习惯。每当我看到nil?被使用时,它几乎总是使用不当。在Ruby中,很少有东西在逻辑上是假的,只有文字false和nil是。这意味着像if(!x.nil?)这样的代码几乎总是更好地表示为if(x)除非期望x可能是文字false。我会将其切换为||=false,因为它具有相同的结果,但这在很大程度上取决于偏好。唯一的缺点是赋值会在每次运行
我正在阅读一本关于Ruby的书,作者在编写类初始化定义时使用的形式与他在本书前几节中使用的形式略有不同。它看起来像这样:classTicketattr_accessor:venue,:datedefinitialize(venue,date)self.venue=venueself.date=dateendend在本书的前几节中,它的定义如下:classTicketattr_accessor:venue,:datedefinitialize(venue,date)@venue=venue@date=dateendend在第一个示例中使用setter方法与在第二个示例中使用实例变量之间是
转自:spring.profiles.active和spring.profiles.include的使用及区别说明下文笔者讲述spring.profiles.active和spring.profiles.include的区别简介说明,如下所示我们都知道,在日常开发中,开发|测试|生产环境都拥有不同的配置信息如:jdbc地址、ip、端口等此时为了避免每次都修改全部信息,我们则可以采用以上的属性处理此类异常spring.profiles.active属性例:配置文件,可使用以下方式定义application-${profile}.properties开发环境配置文件:application-dev
打印1:defsum(i)i=i+[2]end$x=[1]sum($x)print$x打印12:defsum(i)i.push(2)end$x=[1]sum($x)print$x后者是修改全局变量$x。为什么它在第二个例子中被修改而不是在第一个例子中?类Array的任何方法(不仅是push)都会发生这种情况吗? 最佳答案 变量范围在这里无关紧要。在第一段代码中,您仅使用赋值运算符=为变量i赋值,而在第二段代码中,您正在修改$x(也称为i)使用破坏性方法push。赋值从不修改任何对象。它只是提供一个名称来引用一个对象。方法要么是破坏性
Ruby中的Fixnum方法.next和.succ有什么区别?看起来它的工作原理是一样的:1.next=>21.succ=>2如果有什么不同,为什么有两种方法做同样的事情? 最佳答案 它们是等价的。Fixnum#succ只是Fixnum#next的同义词。他们甚至在thereferencemanual中共享同一block. 关于ruby-Ruby中.next和.succ的区别,我们在StackOverflow上找到一个类似的问题: https://stacko
我明白了defa(&block)block.call(self)end和defa()yieldselfend导致相同的结果,如果我假设有这样一个blocka{}。我的问题是-因为我偶然发现了一些这样的代码,它是否有任何区别或者是否有任何优势(如果我不使用变量/引用block):defa(&block)yieldselfend这是一个我不理解&block用法的具体案例:defrule(code,name,&block)@rules=[]if@rules.nil?@rules 最佳答案 我能想到的唯一优点就是自省(introspecti
由于匿名block和散列block看起来大致相同。我正在玩它。我做了一些严肃的观察,如下所示:{}.class#=>Hash好的,这很酷。空block被视为Hash。print{}.class#=>NilClassputs{}.class#=>NilClass为什么上面的代码和NilClass一样,下面的代码又显示了Hash?puts({}.class)#Hash#=>nilprint({}.class)#Hash=>nil谁能帮我理解上面发生了什么?我完全不同意@Lindydancer的观点你如何解释下面几行:print{}.class#NilClassprint[].class#A
在Ruby中,我试图理解to_enum和enum_for方法。在我提出问题之前,我提供了一些示例代码和两个示例来帮助理解上下文。示例代码:#replicatesgroup_bymethodonArrayclassclassArraydefgroup_by2(&input_block)returnself.enum_for(:group_by2)unlessblock_given?hash=Hash.new{|h,k|h[k]=[]}self.each{|e|hash[input_block.call(e)]示例#1:irb(main)>puts[1,2,3].group_by2.ins