我們知道在 Vue3 中有兩個(gè)非常常用的響應(yīng)式 API:reactive 和 ref。這樣就可以變成我們想要追蹤的數(shù)據(jù)變成響應(yīng)式。
知道嗎?在使用時(shí)一直被告知 ref 用于創(chuàng)建基礎(chǔ)類型的響應(yīng)式,也可以創(chuàng)建引用類型的響應(yīng)式。而對于引用類型,底層也是轉(zhuǎn)換為 reactive 來進(jìn)行響應(yīng)式處理。那既然這樣為撒還需要 reactive ,全部使用 ref 不就行了嗎?
雖然 ref 創(chuàng)建的響應(yīng)式數(shù)據(jù)在腳本中需要通過 .value 才能訪問到呀!但是這里肯定影響不大。并且在模板中會自動添加上 .value,所以模板中不需要通過 .value 訪問。
既然這二者基本沒撒差別,但是還是暴露了 reactive 這個(gè) API,難道有什么場景是 reactive 能做而 ref 做不了的?
簡單了解 ref & reactive
現(xiàn)在我們簡單簡述下 API。
reactive
返回對象的響應(yīng)式副本,響應(yīng)式轉(zhuǎn)換是“深層”的——它影響所有嵌套 property。這是常規(guī)寫法。
const obj = reactive({ count: 0 })
并且可以直接使用。
const count = obj.count
ref
接受一個(gè)內(nèi)部值并返回一個(gè)響應(yīng)式且可變的 ref 對象。ref 對象僅有一個(gè).valueproperty,指向該內(nèi)部值。 這是常規(guī)寫法。
const data = ref(xxx)
引用的時(shí)候,一般會通過data.value的方式引用。
const dataValue = data.value
上述代碼中跟蹤 Vue3 的源代碼表明,在調(diào)用 ref 方法來定義響應(yīng)式數(shù)據(jù)時(shí),當(dāng)參數(shù)為對象類型時(shí),只是用到 reactive 方法。也就是說上面的 data.value ,事實(shí)上是 reactive 方法創(chuàng)造出來的。
reactive 能做的 ref 也能做,并且還是用 reactive 做的
我們通過源碼來看看 ref 的源碼實(shí)現(xiàn)。
源碼分析版本:3.2.36
function ref(value) { return createRef(value, false); }
ref 函數(shù)跳轉(zhuǎn)到 createRef 函數(shù)。
function createRef(rawValue, shallow) { ... return new RefImpl(rawValue, shallow); }
createRef 函數(shù)返回的是 RefImpl 類的實(shí)例,換句話說,ref 創(chuàng)建出來的響應(yīng)式就是 RefImpl 實(shí)例對象。
const count = ref(1); console.log(count);
重點(diǎn)講解 RefImpl 類。
class RefImpl { constructor(value, __v_isShallow) { ... this._value = __v_isShallow ? value : toReactive(value); } get value() { trackRefValue(this); return this._value; } set value(newVal) { newVal = this.__v_isShallow ? newVal : toRaw(newVal); if (hasChanged(newVal, this._rawValue)) { this._rawValue = newVal; this._value = this.__v_isShallow ? newVal : toReactive(newVal); triggerRefValue(this, newVal); } } }
__v_isShallow參數(shù)在這里默認(rèn)是 false,記住在使用shallowRef時(shí),返回參數(shù)為 true。
function shallowRef(value) { return createRef(value, true); }
Ref 與 Reactive 創(chuàng)建的都是遞歸響應(yīng)的,將每一層的 json 數(shù)據(jù)解析成一個(gè) proxy 對象,shallowRef 與 shallowReactive 創(chuàng)建的是非遞歸的響應(yīng)對象,shallowReactive 創(chuàng)建的數(shù)據(jù)第一層數(shù)據(jù)改變會重新渲染 dom。
var state = shallowReactive({ a:'a', gf:{ b:'b', f:{ c:'c', s:{d:'d'} } } }); // 改變第一層的數(shù)據(jù)會導(dǎo)致頁面重新渲染 state.a = '1' // 如果不改變第一層,只改變其他的數(shù)據(jù)頁面不會重新渲染 state.gf.b = 2
通過 shallowRef 創(chuàng)建的響應(yīng)式對象,需要修改整個(gè) value 才能重新渲染 dom。
var state = shallowRef({ a:'a', gf:{ b:'b', f:{ c:'c', s:{d:'d'} } } }); // 不會重新渲染 state.value.a = 1 // 要修改整個(gè) value 才能重新渲染 state.value = { a:'1', gf:{ b:'2', f:{ c:'3', s:{d:'d'} } } }
如果想更新 shallowRef 的某一層數(shù)據(jù),并且想觸發(fā)渲染,可以使用 triggerRef。
var state = shallowRef({ a:'a', gf:{ b:'b', f:{ c:'c', s:{d:'d'} } } }) state.value.gf.f.s.d = 4 triggerRef(state)
現(xiàn)在到了toReactive(value)函數(shù)。
const isObject = (val) => val !== null && typeof val === 'object'; const toReactive = (value) => isObject(value) ? reactive(value) : value;
假如傳入的參數(shù)是一個(gè)對象的話,返回值將會繼續(xù)調(diào)用 reactive 方法來進(jìn)行包裹,reactive 最終會通過 Proxy 來進(jìn)行實(shí)現(xiàn)響應(yīng)攔截,返回的也是一個(gè) Proxy 對象,但在這里不重要,我們只需要知道當(dāng) ref 的參數(shù)為對象時(shí),用的就是 reactive 方法。
const data = reactive({ count: 1, }); console.log(data); const data_ref = ref({ count: 1, }); console.log(data_ref);
上圖顯而易見,讓對 ref 傳入對象作為參數(shù)時(shí)和傳入基本類型作為參數(shù)返回反饋結(jié)果不一樣。
基本類型返回值value就是具體的值,對象類型返回值value是 reactive 方法創(chuàng)建的 proxy 對象。
通過源碼來看,其實(shí)也證明了,在 Vue3 中,如果是把對象類型的數(shù)據(jù)弄成響應(yīng)式,reactive 和 ref 都可以,且ref 內(nèi)部是通過r eactive 來支持的。
也就是說,你 reactive 能做的,我 ref 也能做。
ref 能做,但是 reactive 不能做
其實(shí)通過上面的例子就能知道有什么是 reactive 不能做的呢?很明顯,reactive 不支持對基本類型數(shù)據(jù)響應(yīng)式,也就是說基本類型數(shù)據(jù)不能直接作為 reactive 的參數(shù)來使用。
簡單看看源碼。
function reactive(target) { ... return createReactiveObject(...); }
reactive 函數(shù)跳轉(zhuǎn)到 createReactiveObject 函數(shù)。
const isObject = (val) => val !== null && typeof val === 'object'; function createReactiveObject(...) { if (!isObject(target)) { { console.warn(`value cannot be made reactive: ${String(target)}`); } return target; } ... const proxy = new Proxy(...); proxyMap.set(target, proxy); return proxy; }
createReactiveObject 一開始就會判斷target是否是對象,如果不是對象就會直接??提示返回。如果是對象就會把 target 用 Proxy 變成響應(yīng)式對象。
const data = reactive(10);
通過源碼來分析了兩個(gè)響應(yīng)式 API,發(fā)現(xiàn) Vue3 中有沒有 reactive 能做而 ref 做不了的場景?
結(jié)論是:沒有
總結(jié)來說就是ref 在 reactive 上在進(jìn)行了封裝進(jìn)行了增強(qiáng),因此在 Vue3 中 reactive 能做的,ref 也能做,reactive 不能做的,ref 也能做。希望大家以后多多交流,學(xué)習(xí)到更多。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/128010.html
上一節(jié)我們簡單的介紹了一下vue3 項(xiàng)目中的計(jì)算屬性,這一節(jié)我們繼續(xù) vue3 的基礎(chǔ)知識講解。 這一節(jié)我們來說 vue3 的偵聽器。 學(xué)過 vue2 的小伙伴們肯定學(xué)習(xí)過偵聽器,主要是用來監(jiān)聽頁面數(shù)據(jù)或者是路由的變化,來執(zhí)行相應(yīng)的操作,在 vue3里面呢,也有偵聽器的用法,功能基本一樣,換湯不換藥的東西。 偵聽器是常用的 Vue API 之一,它用于監(jiān)聽一個(gè)數(shù)據(jù)并在數(shù)據(jù)變動時(shí)做一些自定義...
在近期的工作中有些知識總結(jié)分享就是使用 uniapp 的 Vue3 版進(jìn)行開發(fā)。這樣可以在開發(fā)中遇到業(yè)務(wù)場景相同的,就分裝了一個(gè)hook 來減少代碼,易于維護(hù)。 hook的場景 上圖中已經(jīng)很詳細(xì)為我們展示3處使用到了獲取列表的功能。分別是: 我的收藏、已投遞崗位、未投遞崗位。現(xiàn)在我們就來詳細(xì)說說。 假如: 我的收藏、已投遞崗位、未投遞崗位 都各自獲取列表,就會出現(xiàn)重復(fù)性的定義以下代碼 ...
00:先下載 yarn add @vitejs/plugin-vue-jsx -D 01:引入 vite.config.ts import{defineConfig}from'vite' importvuefrom'@vitejs/plugin-vue' importvueJsxfrom'@vitejs/plugin-vue-jsx...
閱讀 561·2023-03-27 18:33
閱讀 750·2023-03-26 17:27
閱讀 647·2023-03-26 17:14
閱讀 603·2023-03-17 21:13
閱讀 537·2023-03-17 08:28
閱讀 1823·2023-02-27 22:32
閱讀 1315·2023-02-27 22:27
閱讀 2199·2023-01-20 08:28