Rumah > Artikel > hujung hadapan web > Penjelasan terperinci tentang dua alat responsif utama Vue3: ref dan reaktif
Berbanding dengan tindak balas data yang dilaksanakan oleh defineProperty Vue2, Vue3 mempunyai pembahagian kerja yang lebih jelas dalam memproses respons data Data dibungkus melalui dua fungsi ref dan reaktif yang terdedah kepada pembangun dalam gabungan API, dengan itu merealisasikan tindak balas data. formula, jadi apakah perbezaan antara mereka? Mari belajar bersama-sama dengan contoh!
ref:定义基本数据类型、引用数据类型的响应式
. Dalam erti kata lain, ref(value), jenis nilai ini boleh menjadi jenis data asas atau jenis data rujukan, tetapi ia mesti digunakan dalam format 属性.value
apabila digunakan dalam js dan data boleh dipanggil terus dalam templat .
<template> <div> <div><button @click="changeValue">修改</button></div> <div> <p>当前strRef:{{ strRef }}</p> <p>当前objRef:姓名:{{ objRef.name }} 爱好:{{ objRef.hobboy }}</p> <p>当前arrRef:{{ arrRef }}</p> </div> </div> </template> <script> import { defineComponent, ref, shallowRef } from 'vue' export default defineComponent({ setup () { const strRef = ref('sapper');// 基本数据类型 const arrRef = ref([1, 3, 2]);// 数组类型 const objRef = ref({ // 对象类型 name: 'sapper', hobboy: ['吉他', '原神'] }) const changeValue = () => { strRef.value = '工兵'; arrRef.value[1] = 4; objRef.value.hobboy[1] = '滑冰'; } return {strRef,objRef,arrRef,changeValue} } }) </script>
reaktif: 定义引用类型数据的响应式,不支持基本数据类型
, jika anda perlu menulis jenis data asas, anda hanya boleh memasukkannya ke dalam objek, iaitu reaktif(nilai), ini jenis nilai mestilah jenis rujukan . [Cadangan berkaitan: tutorial video vuejs, pembangunan bahagian hadapan web]
<template> <div> <div><button @click="changeValue">修改</button></div> <div> <div>当前objReactive: <br/> 姓名:{{ objReactive.name }}<br/> 爱好:{{ objReactive.hobboy }} </div> <div>当前arrReactive:{{ arrReactive }}</div> </div> </div> </template> <script> import { defineComponent, reactive } from 'vue' export default defineComponent({ setup () { const arrReactive = reactive([1, 3, 2]);// 数组类型 const objReactive = reactive({ // 对象类型 name: 'sapper', hobboy: ['吉他', '原神'] }) const changeValue = () => { arrReactive[1] = 4; objReactive.name = '工兵'; objReactive.hobboy[1] = '滑冰'; } return {objReactive,arrReactive,changeValue} } }) </script>
Daripada dua contoh di atas kita dapat melihat bahawa tidak kira apa jenis data , ref Anda perlu menggunakan .value
untuk memanggil data yang ditakrifkan oleh ref Untuk jenis data rujukan, kita dapat melihat bahawa kod itu tidak cantik, jadi secara amnya untuk data jenis rujukan, adalah disyorkan untuk menggunakan reaktif untuk menentukan. ; Untuk jenis data asas, anda boleh menggunakan ref atau reaktif untuk mentakrifkan . Sekarang setelah kita semua memahami perbezaan antara penggunaan ref dan reaktif di sini, mari teruskan meneroka perbezaan dalam prinsip tindak balas mereka?
Daripada contoh di atas, kita mula-mula mencetak dan melihat struktur pembungkusan dalaman ref jenis data asas (strRef) dan data rujukan taip (arrRef, ObjRef) Macam mana? Seperti yang ditunjukkan dalam tiga gambar berikut
Seperti yang dapat dilihat dari gambar di atas, tidak kira apa jenis datanya ialah, untuk data berkapsul ref Kedua-duanya ialah objek RefImpl, singkatan reference implement
bermaksud pelaksanaan rujukan Setiap objek RefImpl mempunyai 6 atribut:
. dep : Ia ialah data jenis Set, digunakan untuk menyimpan kebergantungan yang dikumpul oleh nilai rujukan semasa.
_ v _ isRef: Tandakan bit, selagi ia ditakrifkan oleh ref, ia akan menandakan data semasa sebagai Ref, iaitu nilainya ditanda sebagai benar.
_ v _ isShallow: Tentukan sama ada ia adalah data yang ditakrifkan oleh shallowRef.
Berbeza daripada ref, apabila menggunakan shallowRef untuk mencipta responsif bagi jenis rujukan, atribut dalam diubah suai dan tiada responsif. Dipecat hanya untuk rujukan kepada .value.
const state = shallowRef({ count: 1 }) // 不会触发更改 state.value.count = 2 // 会触发更改 state.value = { count: 2 }
_ rawValue: untuk 保存当前ref值对应的原始值
, jika parameter yang diluluskan ialah 对象
, ia ialah 用于保存转化前的原始值
, jika tidak_ nilai ialah sama seperti _ rawValue.
_ nilai: untuk 保存ref当前值
, jika parameter yang diluluskan ialah 对象
, ia adalah 用于保存经过reactive函数转化后的值
, jika tidak _ nilai adalah sama dengan _ rawValue . Daripada contoh di atas, kita dapati bahawa untuk data jenis rujukan, nilainya ialah objek proksi, yang sebenarnya adalah objek reaktif selepas merangkum data (akan dibincangkan kemudian). Mari kita lihat gambar di bawah dahulu dan mendapati bahawa _ rawValue adalah nilai asal tanpa pemprosesan responsif Kemudian kita melihat nilai _ iaitu objek proksi yang telah diproses dengan tindak balas reaktif.
_ rawValue dan _ value adalah untuk membezakan sama ada data jenis rujukan perlu diproses secara responsif.
nilai: Menyimpan nilai semasa.
Sekarang kita tahu apakah atribut ref merangkum data, mari kita mula meneroka cara kod sumber memberikan nilai kepada enam atribut di atas:
fungsi ref: Vue3 mendedahkan fungsi ref kepada pembangun.
export function ref(value?: unknown) { return createRef(value, false) }
fungsi createRef : Terdapat dua parameter, satu ialah 做响应处理的数据
dan satu lagi ialah 判断数据是否为shallowRef定义的数据
. Perkara yang dilakukan terutamanya adalah untuk menentukan sama ada rawValue semasa (data yang belum diproses untuk respons) ialah data jenis ref dan mencipta objek contoh RefImpl.
function createRef(rawValue: unknown, shallow: boolean) { if (isRef(rawValue)) { return rawValue } return new RefImpl(rawValue, shallow) }
RefImpl类:创建RefImpl类给_ rawValue和_ value属性赋值,判断当前定义的ref数据是否为shallowRef定义的数据,然后获取响应性值时对数据依赖进行收集并返回_ value,修改响应式值时修改并通知依赖更新。
ref定义的数据为什么需要带.value调用数据? 就是
因为RefImpl类暴露给实例对象的get、set方法是value
,所以在调用的时候,需要带上。
其实,RefImpl实例关键就在于trackRefValue(this)
和triggerRefValue(this, newVal)
的两个函数的处理,我们大概也知道它们就是依赖收集、依赖更新。这里就不一一探讨。
上面也说了reactive封装数据的用法,它只支持传入引用类型数据(数组、对象),如果需要在reactive中使用基础类型只能放在对象中。既然这样我们来探讨一下reactive函数究竟做了什么?
const arrReactive = reactive([1, 3, 2]);// 数组类型 const objReactive = reactive({ // 对象类型 name: 'sapper', hobboy: ['吉他', '原神'] }) const changeValue = () => { arrReactive[1] = 4; objReactive.name = '工兵'; objReactive.hobboy[1] = '滑冰'; console.log('arrReactive',arrReactive); console.log('objReactive',objReactive); }
从上图可以看出,使用reactive封装的数据返回的都是一个proxy对象,proxy就是代理,如把一个对象代理到另一个对象,好比如房子所有者,代理房子给二手房东销售,二手房东就可以拥有房子销售权利。从上图我们可以看到Proxy对象有三个属性:
[[Handler]]: 创建Proxy对象传入的第二个参数,是对当前需要代理的目标target进行一些相关配置处理。
[[Target]]:需要代理的目标target,也就是被代理的目标。
[[IsRevoked]]:表示是否可撤销,生成可撤销的proxy对象用Proxy.revocable()方法。 那么Proxy对象可以做什么?我们看看下面例子:
const houseOwner = {home:'房源',price:1200,type:'一房一厅'}; const proxyOwner = new Proxy(houseOwner,{ get:function (target,key){ console.log(`${key}属性被访问!`) return target[key]; }, set:function(target,key,value){ if(target[key]===value){ return; } target[key] = value; return target[key]; }, }) console.log(proxyOwner); proxyOwner.price = 1300;// 对被代理对象的修改 proxyOwner.remark = '采光点好!'; console.log(proxyOwner.price);// price属性被访问
从这个例子,可以看出Proxy对象的第二个参数给代理目标带上相关属性:set方法、get方法,再回到reactive封装的数据中,数据的Handler属性给数据带上了五个属性:deleteProperty
、get
、set
、has
、ownKeys
。这五个属性怎么来的?我们一起探讨一下Vue3的源码实现:
// 源码位置:core-main/packages/reactivity/src/reactive.ts // Vue3中暴露给开发者的是reactive方法 export function reactive(target: object) { // 判断target是否只读,是就不做处理 if (isReadonly(target)) { return target } return createReactiveObject( target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap ) }
createReactiveObject函数主要为了创建Proxy实例对象,参数传了五个属性: target
(目标数据)、isReadonly
(target是否只读)、mutableHandlers
(ProxyHandler)、mutableCollectionHandlers
(ProxyHandler类型)、proxyMap
(数据集合)。我们先了解一波createReactiveObject函数:
ReactiveFlags:响应式数据标记。
export const enum ReactiveFlags { SKIP = '__v_skip',// 标记对象不可进行代理 IS_REACTIVE = '__v_isReactive',// 是否是Reactive封装的 IS_READONLY = '__v_isReadonly',// 是否只读 IS_SHALLOW = '__v_isShallow',// 是否是shallowRef封装的 RAW = '__v_raw'// 是否是proxy原始的target }
TargetType:target的数据类型。
const enum TargetType { INVALID = 0, COMMON = 1,// Array、Object类型 COLLECTION = 2 // Set、Map、WaekMap、WeakSet类型 }
baseHandlers:对于Array、Object类型数据,Proxy实例的第二个参数。传入的baseHandlers就是mutableHandlers
。这个函数主要是为了给Proxy对象带上五个属性。
// 源码位置:core-main/packages/reactivity/src/baseHandlers.ts export const mutableHandlers: ProxyHandler<object> = { // createGetter() 主要实现依赖收集和Reflect.set(target, key, value, receiver) get, // createSetter() 主要实现通知依赖更新和Reflect.get(target, key, receiver) set, // deleteProperty() 主要是删除target的指定key的属性Reflect.deleteProperty(target, key) deleteProperty, // has() 主要是判断target是否存在指定key的属性,Reflect.has(target, key) has, // ownKeys() 主要是获取target的key数组,Reflect.ownKeys(target) ownKeys }
collectionHandlers:对于Set、Map、WaekMap、WeakSet类型数据,Proxy实例的第二个参数。传入的baseHandlers就是mutableCollectionHandlers
。mutableCollectionHandlers主要是对 set、map、weakSet、weakMap 四种类型的对象进行劫持。
// 源码位置:core-main/packages/reactivity/src/mutableCollectionHandlers.ts export const mutableCollectionHandlers: ProxyHandler<CollectionTypes> = { get: /*#__PURE__*/ createInstrumentationGetter(false, false) } function createInstrumentationGetter(isReadonly: boolean, shallow: boolean) { const instrumentations = shallow? isReadonly? shallowReadonlyInstrumentations: shallowInstrumentations : isReadonly? readonlyInstrumentations: mutableInstrumentations return (target: CollectionTypes,key: string | symbol,receiver: CollectionTypes) => { ... return Reflect.get( hasOwn(instrumentations, key) && key in target? instrumentations:target, key, receiver ) } }
总结:
创建了RefImpl实例对象
。创建了Proxy实例对象
,通过Reflect实现数据的获取与修改。Atas ialah kandungan terperinci Penjelasan terperinci tentang dua alat responsif utama Vue3: ref dan reaktif. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!