0


vue3响应式

vue3响应式

vue3

实现响应式的方法有两种:

第一种运用组合式API中的

reactive

直接构建响应式,组合式API的出现让我们可以直接用

setup

函数来处理之前的大部分逻辑,同时也避免了

this

的使用,像

data

watch

computed

,生命周期函数都声明在

setup

函数中,这样就像

react-hooks

一样提升代码的复用率,逻辑性更强。

第二种就是使用传统的

data(){ return{} }

的形式,

vue3

并没有放弃对

vue2

写法的支持,而是对

vue2

的写法完全兼容。

响应式基础API

首先,让我们来看看

vue3

为响应式提供的一些基础

API

.

reactive
reactive

用于将数据变成响应式数据。调用

reactive

后返回的对象是响应式副本而非原始对象。其原理就是将传入的数据包装成一个

Proxy

对象。

import{ reactive, watchEffect }from'vue';const data ={count:0};const obj =reactive(data);
data === obj // falsewatchEffect(()=>{// 用于响应性追踪
  console.log(obj.count);});setTimeout(()=>{
  obj.count++;},2000);

响应式转换是“深层”的——它影响所有嵌套

property

。在基于

ES2015 Proxy

的实现中,返回的

proxy

是不等于原始对象的。建议只使用响应式

proxy

,避免依赖原始对象。

reactive

用于复杂数据类型,比如对象和数组等,当传入基础数据类型时,默认情况下修改数据,界面不会自动更新,如果想更新,可以通过重新赋值的方式。

readonly

接受一个对象 (响应式或纯对象) 或

ref

并返回原始对象的只读代理。只读代理是深层的:任何被访问的嵌套

property

也是只读的,不能改变值,否则是报错。

import{ reactive, watchEffect, readonly }from'vue';const data ={count:0};const obj =reactive(data);const copy =readonly(obj);watchEffect(()=>{// 用于响应性追踪
  console.log(obj.count)});setTimeout(()=>{
  obj.count++;
  copy.count++;},2000);

isProxy

检查对象是否是由

reactive

readonly

创建的

proxy

import{ reactive, readonly, isProxy }from'vue';const data ={count:0};const obj =reactive(data);const copy =readonly(obj);

console.log(isProxy(obj));// true
console.log(isProxy(copy));// true
console.log(isProxy(data));// false

isReactive

检查对象是否是由

reactive

创建的响应式代理。如果该代理是

readonly

创建的,但包裹了由

reactive

创建的另一个代理,它也会返回

true

import{ reactive, readonly, isReactive }from'vue';const data ={count:0};const obj =reactive(data);const copy =readonly(obj);const immute =readonly(data);

console.log(isReactive(obj));// true
console.log(isReactive(copy));// true
console.log(isReactive(data));// false
console.log(isReactive(immute))// false

isReadonly

检查对象是否是由

readonly

创建的只读代理。

import{ reactive, readonly, isReadonly }from'vue';const data ={count:0};const obj =reactive(data);const copy =readonly(obj);const immute =readonly(data);

console.log(isReadonly(obj));// false
console.log(isReadonly(copy));// true
console.log(isReadonly(data));// false
console.log(isReadonly(immute))// true

toRaw

返回

reactive

readonly

代理的原始对象。这是一个“逃生舱”,可用于临时读取数据而无需承担代理访问/跟踪的开销,也可用于写入数据而避免触发更改。不建议保留对原始对象的持久引用。请谨慎使用。

import{ reactive, readonly, toRaw }from'vue';const data ={count:0};const obj =reactive(data);const immute =readonly(data);

console.log(toRaw(obj)=== data);// true
console.log(toRaw(immute)=== data);// true

markRaw

标记一个对象,使其永远不会转换为

proxy

。返回对象本身。

import{ reactive, readonly, isReactive, markRaw }from'vue';const data =markRaw({count:0});const obj =reactive(data);const immute =readonly(data);

console.log(isReactive(obj));// false
console.log(obj === data);// true
console.log(immute === data);// true// 即使嵌套在其他响应式队中也是非响应式的const wrap =reactive({data});
console.log(isReactive(wrap.data));// false

但是如果对标记对象内的属性进行响应化,是可以的。

const count =reactive({// data被标记,data.count未被标记count: data.count
})

console.log(isReactive(count));

shallowReactive

创建一个响应式代理,它跟踪其自身

property

的响应性,但不执行嵌套对象的深层响应式转换。

import{ shallowReactive, isReactive, watchEffect }from'vue';const data ={count:0,nested:{count:0}};const obj =shallowReactive(data);watchEffect(()=>{// 追踪obj.count的变化
  console.log(obj.count);});watchEffect(()=>{// 追踪obj.nested.count的变化
  console.log(obj.nested.count);});

obj.count++;
obj.nested.count++;
console.log(data.count);// 0
console.log(data.nested.count);// 1
console.log(isReactive(obj.nested));// false

shallowReadonly

创建一个

proxy

,使其自身的

property

为只读,但不执行嵌套对象的深度只读转换。

import{ shallowReadonly, isReadonly }from'vue';const data ={count:0,nested:{count:0}};const obj =shallowReadonly(data);// 报错
obj.count++;

obj.nested.count++;
console.log(isReadonly(obj.nested));// false

ref

接受一个内部值并返回一个响应式且可变的

ref

对象。

ref

对象仅有一个

.value

值,指向该内部值。跟

reactive

类似,也是将数据变成响应式。

import{ ref, watchEffect }from'vue';const count =ref(0);const obj =ref({count:0})
console.log(obj)watchEffect(()=>{
  console.log(count.value);})watchEffect(()=>{
  console.log('obj.value.count: ', obj.value.count);})

count.value++;
obj.value.count++;

ref和reactive的区别

  • ref是把值类型添加一层包装,使其变成响应式的引用类型的值。ref(0) --> reactive( { value:0 })
  • reactive 则是引用类型的值变成响应式的值。

两者的区别只是在于是否需要添加一层引用包装,**对于对象而言,添加一层包装后会被

reactive

处理为深层的响应式对象,在调用

unref

后就能看到其实对象是一个

Reactive

对象**。

像上面的例子,使用

ref

同样可以将对象响应化,不过访问的时候需要调用

value.

去访问内部属性。所以对于对象而言,最好使用

reative

去响应化处理。

unref

如果参数是一个

ref

,则返回内部值,否则返回参数本身。这是

val = isRef(val) ? val.value : val

的语法糖函数。

import{ ref, unref }from'vue';const count =ref(0);const obj =ref({count:0})
console.log(unref(count));// 0
console.log(unref(obj))// Reactive对象

toRef

可以用来为源响应式对象上的某个属性新创建一个

ref

。然后,

ref

可以被传递,它会保持对其源属性的响应式连接。

import{ reactive, toRef }from'vue';const obj =reactive({count:0})const countRef =toRef(obj,'count');

countRef.value++;
console.log(obj.count)// 1

obj.count++;
console.log(countRef)// 2

toRefs

将响应式对象转换为普通对象,其中结果对象的每个属性都是指向原始对象相应属性的

ref

import{ reactive, toRefs }from'vue';const obj =reactive({count:0})const countRef =toRefs(obj);

countRef.value++;
console.log(obj.count)// 1

obj.count++;
console.log(countRef.count.value)// 2
toRefs

只会为源对象中包含的属性生成

ref

。如果要为特定的属性创建

ref

,则应当使用

toRef

isRef

检查值是否为一个

ref

对象。

import{ isRef, ref }from'vue';const count =ref(0);
console.log(isRef(count));// true

customRef

创建一个自定义的

ref

,并对其依赖项跟踪和更新触发进行显式控制。它需要一个工厂函数,该函数接收

track

trigger

函数作为参数,并且应该返回一个带有

get

set

的对象。

使用自定义

ref

通过

v-model

实现

debounce

的示例:

<inputv-model="text"/>
functionuseDebouncedRef(value, delay =200){let timeout
  returncustomRef((track, trigger)=>{return{get(){track()return value
      },set(newValue){clearTimeout(timeout)
        timeout =setTimeout(()=>{
          value = newValue
          trigger()}, delay)}}})}exportdefault{setup(){return{text:useDebouncedRef('hello')}}}

shallowRef

创建一个跟踪自身

.value

变化的

ref

,但不会使其值也变成响应式的。

const obj =shallowRef({})// 改变 ref 的值是响应式的
obj.value ={}// 但是这个值不会被转换。isReactive(obj.value)// false

triggerRef

手动执行与

shallowRef

关联的任何作用。

const shallow =shallowRef({greet:'Hello, world'})// 第一次运行时记录一次 "Hello, world"watchEffect(()=>{
  console.log(shallow.value.greet)})// 这不会触发作用 (effect),因为 ref 是浅层的
shallow.value.greet ='Hello, universe'// 触发副作用 "Hello, universe"triggerRef(shallow)

computed

接受一个

getter

函数,并根据

getter

的返回值返回一个不可变的响应式

ref

对象。

const count =ref(1)const plusOne =computed(()=> count.value +1)

console.log(plusOne.value)// 2

plusOne.value++// 错误

或者,接受一个具有

get

set

函数的对象,用来创建可写的

ref

对象。

const count =ref(1)const plusOne =computed({get:()=> count.value +1,set:val=>{
    count.value = val -1}})

plusOne.value =1
console.log(count.value)// 0

watch
vue3

中组合式

api
watch

vue2

中选项式的

watch

完全等效。

watch

需要监听特定的数据源,并在单独的回调函数中执行副作用。默认情况下,它也是惰性的——即回调仅在监听源发生变化时被调用。

watchEffect

相比,

watch

允许我们:

  • 惰性地执行副作用;
  • 更具体地说明应触发监听器重新运行的状态;
  • 访问被监听状态的先前值和当前值。

监听单一源

源数据可以是一个

reactive

,也可以直接是一个

ref

语法

watch( name , callback, options ) ;
  • name: 需要监听的属性或者返回值的getter函数
  • callback: 属性改变后执行的方法,接受两个参数
    • newVal: 新值
    • oldVal: 旧值
  • options: 配置项,可配置如下
    • deep: Boolean, 是否深度监听
    • immediate: Boolean,是否立即执行
// 侦听reactiveconst state =reactive({count:0,value:1})// 只监听对象中的countwatch(// 使用getter函数保证只监听了state中的count()=> state.count,(count, prevCount)=>{/* ... */})// 侦听state中所有属性watch(state,(newVal, oldVal)=>{// ...})// 直接侦听一个 refconst count =ref(0)watch(count,(count, prevCount)=>{/* ... */})

监听多个源

使用数组来同时侦听多个源数据,当任一源数据发生改变都会触发

watch
const state =reactive({count:0,value:1});const count =ref(0)watch([state, count],([newState, newCount],[oldState, oldCount])=>{
  console.log(newState);
  console.log(newCount);})

state.count++;
count.value++;

深度监听

watch

的第三个参数中传入

deep: true
const state =reactive({count:0,value:{status:false}});watch(()=> state,(newVal, oldVal)=>{
  console.log('不会触发')})watch(()=> state,(newVal, oldVal)=>{
  console.log('触发深度监听')},{deep:true})

state.value.status =true;

由于

getter

方法只返回

state

,对没有对内部的对象进行监听,因此内部对象的属性发生改变不会触发

watch

当没有使用

getter

方法而是传入

state

这个

reactive

数据,则不需要设置

deep: true

都会进行深度监听。

立即执行

watch

的第三个参数中传入

immediate: true

,当传入数据就会执行一次。

watchEffect

立即执行传入的一个函数,响应式追踪其依赖,在其依赖变更时重新运行该函数

它会监听引用数据类型的所有属性,不需要具体到某个属性,一旦运行就会立即监听,组件卸载的时候会停止监听

const count =ref(0)watchEffect(()=> console.log(count.value))// -> logs 0setTimeout(()=>{
  count.value++// -> logs 1},100)

停止监听

watchEffect

在组件的

setup()

函数或生命周期钩子被调用时,侦听器会被链接到该组件的生命周期,并在组件卸载时自动停止。

在一些情况下,也可以显式调用返回值以停止侦听

const stop =watchEffect(()=>{/* ... */})// laterstop()

清除副作用

有时副作用函数会执行一些异步的副作用,这些响应需要在其失效时清除 (即完成之前状态已改变了) 。所以侦听副作用传入的函数可以接收一个

onInvalidate

函数作入参,用来注册清理失效时的回调。当以下情况发生时,这个失效回调会被触发:

  • 副作用即将重新执行时
  • 侦听器被停止 (如果在 setup() 或生命周期钩子函数中使用了 watchEffect,则在组件卸载时)
watchEffect(onInvalidate=>{const token =performAsyncOperation(id.value)onInvalidate(()=>{// id has changed or watcher is stopped.// invalidate previously pending async operation
    token.cancel()})})

本文转载自: https://blog.csdn.net/qq_42880714/article/details/127819812
版权归原作者 dralexsanderl 所有, 如有侵权,请联系我们删除。

“vue3响应式”的评论:

还没有评论