ref 函数
语法:
const xxx=ref(initValue)
接受的数据类型:基本类型,对象类型
作用:
把参数加工成一个响应式对象,全称为reference
对象(我们下面一律简称为 ref 对象)
核心原理:
响应式依赖Object.defineProperty()
的get()
和set()
。
Ref 函数对于基本数据类型的参数
基本使用:
<template> <h2>{{Str}}</h2> </template> <script> import {ref} from 'vue' export default { setup(){ const Str =ref('hello') return{ Str } } }
上面我们定义了一个Str
变量来接收ref
加工生成的ref
对象,我们都知道对象的数据都是以键值对的形式存储的,但是在这里为什么直接把Str
给return
出去, 并且模板里可以直接写Str
这个对象呢,我们的源数据呢?带着这个疑问我们来输出一下 Str:
setup(){ const Str =ref('hello') console.log(Str) return{ Str } }
打印结果如下:
RefImpl __v_isRef: true _rawValue: "hello" _shallow: false _value: "hello" value: "hello" [[Prototype]]: Object
在这里我们看到了ref
对象身上的一个属性 value
,没错,我们存入的值就对应在value
属性上,至于为什么直接return
出是Str
而不是Str.value
。是因为 Vue 帮了我们一个忙,我们在这里可以简写,return
出去的Str
就相当于Str.value
,当然,非要写.value
的形式也不是不可以。不过这里需要注意的是,在setup
函数中操作Str
中的数据不可以简写。
响应式原理:
对于基本数据类型说完了它的基本用法,下面我们来说说他的响应式原理,也就是我们为什么非要用一个ref
对象来接受数据,而不是直接就定义好一个字符串'hello'
,然后去直接操作这个字符串?
对于ref
函数,数据参数和它加工生成的ref
对象二者存在着某种“契约”,我们把加工好的参数也就是ref
对象交出去之后,我们直接操作修改的就是ref
对象上的value
属性,因为这个契约的存在,以至于在修改属性值的时候对应的源数据参数也会连同被修改,但在原数据被修改之前 vue 就会监听到我们修改了数据,立马进行解析模板更新页面,这就是ref
对象的响应式原理。这里的契约其实就是Object.defineProperty
的get()
和set()
,篇幅有限我们不对底层核心代码做示范.
我们知道了ref
函数定义基本类型,下面会讲解reactive
函数,该函数参数是对象类型和ref
的对象类型参数有着千丝万缕的联系,所以学会reactive
函数学会了 ref 函数存储的对象数据类型也就自然理解了。
reactive 函数
语法:
const xxx=ref(源对象)
接受的数据类型:对象类型
作用:
把参数加工成一个代理对象,全称为proxy
对象
核心原理:
基于 Es6 的 Proxy 实现,通过代理操作源对象,相比于reactive
定义的浅层次响应式数据对象,reactive
定义的是更深层次的响应式数据对象。
reactive 对于对象类型的参数
基本使用:
<template> <h2>姓名:{{Person.name}}</h2> <h2>薪水:{{Person.job.salary}}</h2> </template> <script> import {reactive} from 'vue' export default { setup(){ const p ={ name:'Ben', job:{ salary:'30k' } } const Person =reactive(p) return{ Person } } }
这里不同于上面的ref
函数,参数是一个对象类型的数据,那么对于接受返回值的Person
对象也会和上面的ref
对象一样都是把数据对应到value
属性上吗?带着这个疑问打印一下Person
对象:
Proxy [[Handler]]: Object [[Target]]: Object job: {salary: "30k"} name: "Ben" [[Prototype]]: Object [[IsRevoked]]: false
我们发现Person
对象身上并没有value
属性并且Person
对象是一个proxy
类型的对象,这里我们可以理解为Person
是一个加强版的p
对象,对于p
来说 Person 是一个代理对象,所以我们可以直接访问到自身的属性,而不是和ref
函数一样把数据对应到value
属性上,所以我们return
出去的Person
对象可以直接放在模板里读取属性。
响应式原理:
reactive
的响应式是更加“深层次”的,底层依赖于 ES6 中的Proxy
实现,用reactive
函数加工出来的对象都是Proxy
对象,无论原数据对象里面有多少层对象他们都会被加工成Proxy
类型对象(深层次)。它的响应式核心思想和ref
是大同小异的,这里我们定义的 Person 对象来代理 p 对象,二者也会建立“契约”,我们在把Person
对象return
出去后,在我们操作修改Person
对象的数据时,在原对象数据改变之前Vue
都会监听到这一举动并且同时解析模板、更新页面。
Ref 函数对于对象类型的参数
说到这里大家会有个疑问,ref
定义对象类型的数据和这个reactive
函数有什么关系呢?
ref
对象只能去操作浅层次的数据,把基本数据类型当做自己的属性值,如果ref
函数的参数是对象这种深层次的数据类型时它会求助一个人,这个人不是别人正是reactive
函数,在底层 Vue 会把对象参数通过reactive
函数加工成一个Proxy
代理对象放到ref
的value
属性上。
<template> <h2>姓名:{{Person.name}}</h2> <h2>薪水:{{Person.job.salary}}</h2> </template> <script> import {reactive,ref} from 'vue' export default { setup(){ const p ={ name:'Ben', job:{ salary:'30k' } } const Person =ref(p) console.log(Person) return{ Person } } }
打印 Person 对象的结果:
RefImpl {_shallow: false, __v_isRef: true, _rawValue: {…}, _value: Proxy} __v_isRef: true _rawValue: {name: "Ben", job: {…}} _shallow: false _value: Proxy {name: "Ben", job: {…}} value: Proxy [[Handler]]: Object [[Target]]: Object job: {salary: "30k"} name: "Ben" [[Prototype]]: Object [[IsRevoked]]: false [[Prototype]]: Object
我们可以很清楚的看到,value 属性下对应的正是给 p 原数据对象加工生成 Proxy 对象。
区别
- 存储类型:
ref
对象可以接受基本数据类型得数据也可以接受对象类型的数据,而reactive
只可以接受对象类型的数据; - 响应式:相比于
ref
对象reactive
函数是更深层次的,ref
函数参数为对象类型时依赖的也是 reactive 函数; - 用法:
Proxy
代理对象的数据键值对是和原数据对象的键值对一一对应的,在使用时可以直接使用 对象(接受返回值的对象).键名 的形式,ref
对象会把数据参数对应到自己; value
属性上,在模板里我们可以直接省略.value
的形式,这看起来好像是“把源数据赋值给了ref
类型的变量,可以直接在模板上使用这个ref
类型的变量”;- 响应式原理:
ref
的响应式原理是依赖于Object.defineProperty()
的get()
和set()
而reactive
的响应式原理是依赖于 ES6 中的Proxy
。