前言
在Vue 3.X的文档中,在一个不起眼的地方,有这么一句话:
尤大在采访的时候也是希望如此
视频很短,概括起来有以下几点:
- 官方推荐使用ref
- reactive和this用法接近,vue2转过来的程序员更容易接受
reactive会有过度传递的问题
。- reactive在使用中有时候还需要使用
toRef去取值
,有时候需要computed去拼数据
。相比较而言,ref可以直接进行操作 - ref和reactvie如何使用,没有对错之分,只要遵循规范就好
ref VS reactive
ref和reactive的不同点
ref函数和reactive函数都是用来生命响应式状态的API。但还是有点差异的,主要不同点如下:
- 定义数据类型不同
- ref一般用来定义
基本数据类型
- reactive只能用来定义
引用数据类型
-(对象 + 数组)
- ref一般用来定义
ref也可以定义引用数据类型,内部也是通过reactive来实现的。
-
使用方式不同
- 操作ref的数据需要使用
.value
- 直接操作reactive的数据,无需.value
- 操作ref的数据需要使用
-
丢失响应性
- reactive重新分配一个新对象会失去响应; 将对象传入传入函数时,会失去响应
- ref重新分配一个新对象会不会失去响应;将对象传入传入函数时,不会失去响应
源码分析
class RefImpl<T> {
// ....
get value() {
trackRefValue(this)
return this._value
}
set value(newVal) {
const useDirectValue =
this.__v_isShallow || isShallow(newVal) || isReadonly(newVal)
newVal = useDirectValue ? newVal : toRaw(newVal)
if (hasChanged(newVal, this._rawValue)) {
this._rawValue = newVal
this._value = useDirectValue ? newVal : toReactive(newVal)
triggerRefValue(this, newVal)
}
}
通过源码我们可以看到: ref就是reactive的包了一层壳而已
如果值是基本数据类型,或者是浅层(shallow)或只读(readonly),则直接赋值,不需要进行响应式处理
如果值是引用数据类型,则使用 toReactive
转换为响应式
注意:ref和reactive
都是基于Proxy
实现的。很多人说ref定义基础数据类型使用的是Object.defineProperty这是错误的。
牢记:ref会创建一个包含
.value
属性的对象,这个对象的响应式功能是依赖于Proxy
reactive的局限性
官方文档进行了详细的说明,这里不做过多阐述,照搬官方的文档和案例
-
有限的值类型:reactive
只能声明对象和数组
-
不能替换整个对象:由于 Vue 的响应式跟踪是通过属性访问实现的,因此我们必须始终保持对响应式对象的相同引用
let state = reactive({ count: 0 })
// 上面的 ({ count: 0 }) 引用将不再被追踪
// (响应性连接已丢失!)
state = reactive({ count: 1 })
解决办法:
state = Object.assign(state , {count:1})
3. 对解构操作不友好:当我们将响应式对象的原始类型属性解构为本地变量时,或者将该属性传递给函数时,我们将丢失响应性连接
const state = reactive({ count: 0 })
// 当解构时,count 已经与 state.count 断开连接
let { count } = state
// 不会影响原始的 state
count++
// 该函数接收到的是一个普通的数字
// 并且无法追踪 state.count 的变化
// 我们必须传入整个对象以保持响应性
callSomeFunction(state.count)
ref能代替reactive吗?
先说答案:
不谈编码的优雅,只谈功能实现:reactive能做到的事情,ref都能做到,毕竟ref也是调用的reactive的方法
但是在项目中,很多人都不会完全舍弃reactive
.value
真的很烦- 表单操作,使用
reactive很直观,也非常方便
- 对于
vue2
转过来的程序员来说,无缝衔接
ref一把梭?
看到这,是不是更迷茫了。心里在低估,我用reactive到底对不对呀?以后的项目是不是ref一把梭到底呀?
其实不要迷茫,回归正题:ref和reactvie如何使用,没有对错之分,只要遵循规范就好。按照一种风格编码下来,遵循这种规范就好
既然是规范,首先要有制定规范的人,即有人去拍板
。规范制定了,剩下的人去遵守就好了
如何制定这个规范呢?
对于小团队,尤其是单人负责一个项目。我建议用reactive去声明复杂的对象,尤其是表单数据
。在computed
中去做一些数据格式处理及异常处理等等。
前提是对reactive丢失响应式有深刻的理解
对于多人协作,尤其是赶工期,要求快速迭代,快速上线的那种公司。我建议还是老老实实用ref比较好
。多人协作,很容易让reactive丢失响应式,产生莫名其妙的bug,排查问题,耽误时间。更有甚者,出了问题不去定位,然后toRefs, shallowRef, triggerRef
满天飞,把代码整得一团糟。就像打补丁一样,补丁摞补丁,最终失去了reactive本身的意义
.value小尾巴
很多人很烦.value
的小尾巴,认为每一次都要.value一下,其实很多VS Code可以帮我们解决这个问题,自动填充
比如: vue - official插件
用着确实方便,但不要忘记.value的意义是提醒我们这是一个响应式数据,至于是否启动,看各位自己的意愿了
总结
程序是写给人读的,只是偶尔让计算机执行一下。就执行而言,计算机只关心对错。因此无论使用什么方式,机器只是执行一个结果,不在意过程
因此,无论使用ref一把梭,还是正确且优雅的使用reactive,对于结果而言就是正确的。但如何平衡和取舍,需要我们自己把握,需要我们在实战中不断进步
最后再次提醒,如果不能充分了解reactive,就老老实实的使用ref一把梭,就代码的整体风格而言,更加优雅和可读。