您好,登錄后才能下訂單哦!
這篇文章主要介紹“Vue3中的響應式機制是什么”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“Vue3中的響應式機制是什么”文章能幫助大家解決問題。
響應式一直都是 Vue 的特色功能之一;與之相比,JavaScript 里面的變量,是沒有響應式這個概念的;你在學習 JavaScript 的時候首先被灌輸的概念,就是代碼是自上而下執行的;
我們看下面的代碼,代碼在執行后,打印輸出的兩次 double 的結果也都是 2;即使 我們修改了代碼中 count 的值后,double 的值也不會有任何改變
let count = 1 let double = count * 2 count = 2
double 的值是根據 count 的值乘以二計算而得到的,如果現在我們想讓 doube 能夠跟著 count 的變化而變化,那么我們就需要在每次 count 的值修改后,重新計算 double
比如,在下面的代碼,我們先把計算 doube 的邏輯封裝成函數,然后在修改完 count 之 后,再執行一遍,你就會得到最新的 double 值
let count = 1 // 計算過程封裝成函數 let getDouble = n=>n*2 //箭頭函數 let double = getDouble(count) count = 2 // 重新計算double ,這里我們不能自動執行對double的計算 double = getDouble(count)
實際開發中的計算邏輯會比計算 doube 復雜的多,但是都可以封裝成一個函數去執行;下 一步,我們要考慮的是,如何讓 double 的值得到自動計算
如果我們能讓 getDouble 函數自動執行,也就是如下圖所示,我們使用 JavaScript 的某種機制,把 count 包裹一層,每當對 count 進行修改時,就去同步更新 double 的值,那 么就有一種 double 自動跟著 count 的變化而變化的感覺,這就算是響應式的雛形了
響應式原理是什么呢?Vue 中用過三種響應式解決方案,分別是 defineProperty、Proxy 和 value setter我們首先來看 Vue 2 的 defineProperty API
這里我結合一個例子來說明,在下面的代碼中,我們定義個一個對象 obj,使用 defineProperty 代理了 count 屬性;這樣我們就對 obj 對象的 value 屬性實現了攔截,讀取 count 屬性的時候執行 get 函數,修改 count 屬性的時候執行 set 函數,并在 set 函數內部重新計算了 double
let getDouble = n=>n*2 let obj = {} let count = 1 let double = getDouble(count) Object.defineProperty(obj,'count',{ get(){ return count }, set(val){ count = val double = getDouble(val) } }) console.log(double) // 打印2 obj.count = 2 console.log(double) // 打印4 有種自動變化的感覺
這樣我們就實現了簡易的響應式功能,在課程的第四部分,我還會帶著你寫一個更完善的響應式系統
但 defineProperty API 作為 Vue 2 實現響應式的原理,它的語法中也有一些缺陷;比如在下面代碼中,我們刪除 obj.count 屬性,set 函數就不會執行,double 還是之前的數值;這也是為什么在 Vue 2 中,我們需要 $delete
一個專門的函數去刪除數據
delete obj.count console.log(double) // doube還是4
Vue 3 的響應式機制是基于 Proxy 實現的;就 Proxy 這個名字來說,你也能看出來這是代理的意思,Proxy 的重要意義在于它解決了 Vue 2 響應式的缺陷
我們看下面的代碼,在其中我們通過 new Proxy 代理了 obj 這個對象,然后通過 get、set 和 deleteProperty 函數代理了對象的讀取、修改和刪除操作,從而實現了響應式的功能
let proxy = new Proxy(obj,{ get : function (target,prop) { return target[prop] }, set : function (target,prop,value) { target[prop] = value; if(prop==='count'){ double = getDouble(value) } }, deleteProperty(target,prop){ delete target[prop] if(prop==='count'){ double = NaN } } }) console.log(obj.count,double) proxy.count = 2 console.log(obj.count,double) delete proxy.count // 刪除屬性后,我們打印log時,輸出的結果就會是 undefined NaN console.log(obj.count,double)
我們從這里可以看出 Proxy 實現的功能和 Vue 2 的 definePropery 類似,它們都能夠在用戶修改數據的時候觸發 set 函數,從而實現自動更新 double 的功能。而且 Proxy 還完善了幾個 definePropery 的缺陷,比如說可以監聽到屬性的刪除
Proxy 是針對對象來監聽,而不是針對某個具體屬性,所以不僅可以代理那些定義時不存在的屬性,還可以代理更豐富的數據結構,比如 Map、Set 等,并且我們也能通過 deleteProperty 實現對刪除操作的代理
當然,為了幫助你理解 Proxy,我們還可以把 double 相關的代碼都寫在 set 和 deleteProperty 函數里進行實現,在課程的后半程我會帶你做好更完善的封裝;比如下面代碼中,Vue 3 的 reactive 函數可以把一個對象變成響應式數據,而 reactive 就是基于 Proxy 實現的;我們還可以通過 watchEffect,在 obj.count 修改之后,執行數據的打印
import {reactive,computed,watchEffect} from 'vue' let obj = reactive({ count:1 }) let double = computed(()=>obj.count*2) obj.count = 2 watchEffect(()=>{ console.log('數據被修改了',obj.count,double.value) })
有了 Proxy 后,響應式機制就比較完備了;但是在 Vue 3 中還有另一個響應式實現的邏輯,就是利用對象的 get 和 set 函數來進行監聽,這種響應式的實現方式,只能攔截某一個屬性的修改,這也是 Vue 3 中 ref 這個 API 的實現
在下面的代碼中,我們攔截了 count 的 value 屬性,并且攔截了 set 操作,也能實現類似的功能
let getDouble = n => n * 2 let _value = 1 double = getDouble(_value) let count = { get value() { return _value }, set value(val) { _value = val double = getDouble(_value) } } console.log(count.value,double) count.value = 2 console.log(count.value,double)
三種實現原理的對比表格如下,幫助你理解三種響應式的區別
實現原理 | defineProperty | Proxy | value setter |
---|---|---|---|
實際場景 | Vue 2 響應式 | Vue 3 reactive | Vue 3 ref |
優勢 | 兼容性 | 基于proxy實現真正的攔截 | 實現簡單 |
劣勢 | 數組和屬性刪除等攔截不了 | 兼容不了 IE11 | 只攔截了 value 屬性 |
實際應用 | Vue 2 | Vue 3 復雜數據結構 | Vue 3 簡單數據結構 |
簡單入門響應式的原理后,接下來我們學習一下響應式數據在使用的時候的進階方式;我們看下使用 <script setup>
重構之后的 todolist 的代碼;這段代碼使用 watchEffect,數據變化之后會把數據同步到 localStorage 之上,這樣我們就實現了 todolist 和本地存儲的同步
import { ref, watchEffect, computed } from "vue"; let title = ref(""); let todos = ref(JSON.parse(localStorage.getItem('todos')||'[]')); watchEffect(()=>{ localStorage.setItem('todos',JSON.stringify(todos.value)) }) function addTodo() { todos.value.push({ title: title.value, done: false, }); title.value = ""; }
更進一步,我們可以直接抽離一個 useStorage 函數,在響應式的基礎之上,把任意數據響應式的變化同步到本地存儲;我們先看下面的這段代碼,ref 從本地存儲中獲取數據,封裝成響應式并且返回,watchEffect 中做本地存儲的同步,useStorage 這個函數可以抽離成一個文件,放在工具函數文件夾中
function useStorage(name, value=[]){ let data = ref(JSON.parse(localStorage.getItem(name)||'[]')) watchEffect(()=>{ localStorage.setItem(name,JSON.stringify(data.value)) }) return data }
在項目中我們使用下面代碼的寫法,把 ref 變成 useStorage,這也是 Composition API 最大的優點,也就是可以任意拆分出獨立的功能
let todos = useStorage('todos',[]) function addTodo() { ...code }
現在,你應該已經學會了在 Vue 內部進階地使用響應式機制,去封裝獨立的函數;在后續的實戰應用中,我們也會經常對通用功能進行封裝;如下圖所示,我們可以把日常開發中用到的數據,無論是瀏覽器的本地存儲,還是網絡數據,都封裝成響應式數據,統一使用響應式數據開發的模式;這樣,我們開發項目的時候,只需要修改對應的數據就可以了
基于響應式的開發模式,我們還可以按照類似的原理,把我們需要修改的數據,都變成響應式;比如,我們可以在 loading 狀態下,去修改瀏覽器的小圖標 favicon;和本地存儲類似,修改 favicon 時,我們需要找到 head 中有 icon 屬性的標簽
在下面的代碼中,我們把對圖標的對應修改的操作封裝成了 useFavicon 函數,并且通過 ref 和 watch 的包裹,我們還把小圖標變成了響應式數據
import {ref,watch} from 'vue' export default function useFavicon( newIcon ) { const favicon = ref(newIcon) const updateIcon = (icon) => { document.head .querySelectorAll(`link[rel*="icon"]`) .forEach(el => el.href = `${icon}`) } watch( favicon, (i) => { updateIcon(i) } ) return {favicon,reset} }
這樣在組件中,我們就可以通過響應式的方式去修改和使用小圖標,通過對 faivcon.value 的修改就可以隨時更換網站小圖標;下面的代碼,就實現了在點擊按鈕之后,修改了網頁的圖標為 geek.png 的操作
<script setup> import useFavicon from './utils/favicon' let {favicon} = useFavicon() function loading(){ favicon.value = '/geek.png' } </script> <template> <button @click="loading">123</button> </template>
我們自己封裝的 useStorage,算是把 localStorage 簡單地變成了響應式對象,實現數據的更新和localStorage 的同步;同理,我們還可以封裝更多的類似 useStorage 函數的其他 use 類型的函數,把實際開發中你用到的任何數據或者瀏覽器屬性,都封裝成響應式數據,這樣就可以極大地提高我們的開發效率
Vue 社區中其實已經有一個類似的工具集合,也就是 VueUse,它把開發中常見的屬性都封裝成為響應式函數
VueUse 趁著這一波 Vue 3 的更新,跟上了響應式 API 的潮流;VueUse 的官方的介紹說這是一個 Composition API 的工具集合,適用于 Vue 2.x 或者 Vue 3.x,用起來和 React Hooks 還挺像的
在項目目錄下打開命令行里,我們輸入如下命令,來進行 VueUse 插件的安裝:
npm install @vueuse/core
然后,我們就先來使用一下 VueUse;在下面這段代碼中,我們使用 useFullscreen 來返回全屏的狀態和切換全屏的函數;這樣,我們就不需要考慮瀏覽器全屏的 API,而是直接使用 VueUse 響應式數據和函數就可以很輕松地在項目中實現全屏功能
<template> <h2 @click="toggle">click</h2> </template> <script setup> import { useFullscreen } from '@vueuse/core' const { isFullscreen, enter, exit, toggle } = useFullscreen() </script>
useFullscreen 的封裝邏輯和 useStorage 類似,都是屏蔽了瀏覽器的操作,把所有我們需要用到的狀態和數據都用響應式的方式統一管理,VueUse 中包含了很多我們常用的工具函數,我們可以把網絡狀態、異步請求的數據、動畫和事件等功能,都看成是響應式的數據去管理。
關于“Vue3中的響應式機制是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。