溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

怎么在Vue中使localStorage具有響應(yīng)式

發(fā)布時間:2021-11-02 16:09:56 來源:億速云 閱讀:222 作者:iii 欄目:web開發(fā)

這篇文章主要介紹“怎么在Vue中使localStorage具有響應(yīng)式”,在日常操作中,相信很多人在怎么在Vue中使localStorage具有響應(yīng)式問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”怎么在Vue中使localStorage具有響應(yīng)式”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

響應(yīng)式是Vue.js的最大特色之一。如果你不知道幕后情況,它也是最神秘的地方之一。例如,為什么它不能用于對象和數(shù)組,而不能用于諸如  localStorage 之類的其他東西?

怎么在Vue中使localStorage具有響應(yīng)式

讓我們回答這個問題,在解決這個問題時,讓Vue響應(yīng)式與 localStorage 一起使用。

如果運(yùn)行以下代碼,則會看到計數(shù)器顯示為靜態(tài)值,并且不會像我們期望的那樣發(fā)生變化,這是因?yàn)閟etInterval在 localStorage  中更改了該值。

new Vue({   el: "#counter",   data: () => ({     counter: localStorage.getItem("counter")   }),   computed: {     even() {       return this.counter % 2 == 0;     }   },   template: `<div>     <div>Counter: {{ counter }}</div>     <div>Counter is {{ even ? 'even' : 'odd' }}</div>   </div>` });
// some-other-file.js setInterval(() => {   const counter = localStorage.getItem("counter");   localStorage.setItem("counter", +counter + 1); }, 1000);

盡管Vue.js實(shí)例中的 counter 屬性是響應(yīng)式的,但它不會因?yàn)槲覀兏牧怂?localStorage 中的來源而更改。

有多種解決方案,最好的也許是使用Vuex,并保持存儲值與 localStorage  同步。但如果我們需要像本例中那樣簡單的東西呢?我們要深入了解一下Vue.js的響應(yīng)式系統(tǒng)是如何工作的。

Vue 中的響應(yīng)式

當(dāng)Vue初始化組件實(shí)例時,它將觀察data選項(xiàng)。這意味著它將遍歷數(shù)據(jù)中的所有屬性,并使用 Object.defineProperty  將它們轉(zhuǎn)換為getter/setter。通過為每個屬性設(shè)置自定義設(shè)置器,Vue可以知道屬性何時發(fā)生更改,并且可以通知需要對更改做出反應(yīng)的依賴者。它如何知道哪些依賴者依賴于一個屬性?通過接入getters,它可以在計算的屬性、觀察者函數(shù)或渲染函數(shù)訪問數(shù)據(jù)屬性時進(jìn)行注冊。

// core/instance/state.js function initData () {   // ...   observe(data) }
// core/observer/index.js export function observe (value) {   // ...   new Observer(value)   // ... }  export class Observer {   // ...   constructor (value) {     // ...     this.walk(value)   }      walk (obj) {     const keys = Object.keys(obj)     for (let i = 0; i < keys.length; i++) {       defineReactive(obj, keys[i])     }   } }   export function defineReactive (obj, key, ...) {   const dep = new Dep()   // ...   Object.defineProperty(obj, key, {     // ...     get() {       // ...       dep.depend()       // ...     },     set(newVal) {       // ...       dep.notify()     }   }) }

所以,為什么 localStorage 不響應(yīng)?因?yàn)樗皇蔷哂袑傩缘膶ο蟆?/p>

但是等一下,我們也不能用數(shù)組定義getter和setter,但Vue中的數(shù)組仍然是反應(yīng)式的。這是因?yàn)閿?shù)組在Vue中是一種特殊情況。為了擁有響應(yīng)式的數(shù)組,Vue在后臺重寫了數(shù)組方法,并與Vue的響應(yīng)式系統(tǒng)進(jìn)行了修補(bǔ)。

我們可以對 localStorage 做類似的事情嗎?

覆蓋localStorage函數(shù)

首先嘗試通過覆蓋localStorage方法來修復(fù)最初的示例,以跟蹤哪些組件實(shí)例請求了localStorage項(xiàng)目。

// LocalStorage項(xiàng)目鍵與依賴它的Vue實(shí)例列表之間的映射。 const storeItemSubscribers = {};  const getItem = window.localStorage.getItem; localStorage.getItem = (key, target) => {   console.info("Getting", key);    // 收集依賴的Vue實(shí)例   if (!storeItemSubscribers[key]) storeItemSubscribers[key] = [];   if (target) storeItemSubscribers[key].push(target);    // 調(diào)用原始函數(shù)    return getItem.call(localStorage, key); };  const setItem = window.localStorage.setItem; localStorage.setItem = (key, value) => {   console.info("Setting", key, value);    // 更新相關(guān)Vue實(shí)例中的值   if (storeItemSubscribers[key]) {     storeItemSubscribers[key].forEach((dep) => {       if (dep.hasOwnProperty(key)) dep[key] = value;     });   }    // 調(diào)用原始函數(shù)    setItem.call(localStorage, key, value); };
new Vue({   el: "#counter",   data: function() {     return {       counter: localStorage.getItem("counter", this) // 我們現(xiàn)在需要傳遞“this”     }   },   computed: {     even() {       return this.counter % 2 == 0;     }   },   template: `<div>     <div>Counter: {{ counter }}</div>     <div>Counter is {{ even ? 'even' : 'odd' }}</div>   </div>` });
setInterval(() => {   const counter = localStorage.getItem("counter");   localStorage.setItem("counter", +counter + 1); }, 1000);

在這個例子中,我們重新定義了 getItem 和 setItem,以便收集和通知依賴 localStorage 項(xiàng)目的組件。在新的 getItem  中,我們注意到哪個組件請求了哪個項(xiàng)目,在 setItems 中,我們聯(lián)系所有請求該項(xiàng)目的組件,并重寫它們的數(shù)據(jù)屬性。

為了使上面的代碼工作,我們必須向 getItem 傳遞一個對組件實(shí)例的引用,這就改變了它的函數(shù)簽名。我們也不能再使用箭頭函數(shù)了,因?yàn)榉駝t我們就不會有正確的  this 值。

如果我們想做得更好,就必須更深入地挖掘。例如,我們?nèi)绾卧诓伙@式傳遞依賴者的情況下跟蹤它們?

怎么在Vue中使localStorage具有響應(yīng)式

Vue如何收集依賴關(guān)系

為了獲得啟發(fā),我們可以回到Vue的響應(yīng)式系統(tǒng)。我們之前曾看到,訪問數(shù)據(jù)屬性時,數(shù)據(jù)屬性的 getter  將使調(diào)用者訂閱該屬性的進(jìn)一步更改。但是它怎么知道是誰做的調(diào)用呢?當(dāng)我們得到一個數(shù)據(jù)屬性時,它的 getter  函數(shù)沒有任何關(guān)于調(diào)用者是誰的輸入。Getter函數(shù)沒有輸入,它怎么知道誰要注冊為依賴者呢?

每個數(shù)據(jù)屬性維護(hù)一個需要在Dep類中進(jìn)行響應(yīng)的依賴項(xiàng)列表。如果我們在此類中進(jìn)行更深入的研究,可以看到只要在注冊依賴項(xiàng)時就已經(jīng)在靜態(tài)目標(biāo)變量中定義了依賴項(xiàng)。這個目標(biāo)是由一個非常神秘的Watche類確定的。實(shí)際上,當(dāng)數(shù)據(jù)屬性更改時,將實(shí)際通知這些觀察程序,并且它們將啟動組件的重新渲染或計算屬性的重新計算。

但是,他們又是誰?

當(dāng)Vue使 data  選項(xiàng)可觀察時,它還會為每個計算出的屬性函數(shù)以及所有watch函數(shù)(不應(yīng)與Watcher類混為一談)以及每個組件實(shí)例的render函數(shù)創(chuàng)建watcher。觀察者就像這些函數(shù)的伴侶。他們主要做兩件事:

  • 當(dāng)它們被創(chuàng)建時,它們會評估函數(shù)。這將觸發(fā)依賴關(guān)系的集合。

  • 當(dāng)他們被通知他們所依賴的一個值發(fā)生變化時,他們會重新運(yùn)行他們的函數(shù)。這將最終重新計算一個計算出的屬性或重新渲染整個組件。

在觀察者調(diào)用其負(fù)責(zé)的函數(shù)之前,有一個重要的步驟發(fā)生了:他們將自己設(shè)置為Dep類中靜態(tài)變量的目標(biāo)。這樣可以確保在訪問響應(yīng)式數(shù)據(jù)屬性時將它們注冊為從屬。

追蹤誰調(diào)用了localStorage

我們無法完全做到這一點(diǎn),因?yàn)槲覀儫o法使用Vue的內(nèi)部機(jī)制。但是,我們可以使用Vue的想法,即觀察者可以在調(diào)用其負(fù)責(zé)的函數(shù)之前,將目標(biāo)設(shè)置為靜態(tài)屬性。我們能否在調(diào)用  localStorage 之前設(shè)置對組件實(shí)例的引用?

如果我們假設(shè)在設(shè)置 data 選項(xiàng)時調(diào)用了 localStorage,則可以將其插入 beforeCreate 和 created  中。這兩個掛鉤在初始化data選項(xiàng)之前和之后都會被觸發(fā),因此我們可以設(shè)置一個目標(biāo)變量,然后清除該變量,并引用當(dāng)前組件實(shí)例(我們可以在生命周期掛鉤中訪問該實(shí)例)。然后,在我們的自定義獲取器中,我們可以將該目標(biāo)注冊為依賴項(xiàng)。

我們要做的最后一點(diǎn)是使這些生命周期掛鉤成為我們所有組件的一部分,我們可以通過整個項(xiàng)目的全局混合來做到這一點(diǎn)。

// LocalStorage項(xiàng)目鍵與依賴它的Vue實(shí)例列表之間的映射 const storeItemSubscribers = {};  // 當(dāng)前正在初始化的Vue實(shí)例 let target = undefined;  const getItem = window.localStorage.getItem; localStorage.getItem = (key) => {   console.info("Getting", key);    // 收集依賴的Vue實(shí)例   if (!storeItemSubscribers[key]) storeItemSubscribers[key] = [];   if (target) storeItemSubscribers[key].push(target);    // 調(diào)用原始函數(shù)    return getItem.call(localStorage, key); };  const setItem = window.localStorage.setItem; localStorage.setItem = (key, value) => {   console.info("Setting", key, value);    // 更新相關(guān)Vue實(shí)例中的值   if (storeItemSubscribers[key]) {     storeItemSubscribers[key].forEach((dep) => {       if (dep.hasOwnProperty(key)) dep[key] = value;     });   }      // 調(diào)用原始函數(shù)    setItem.call(localStorage, key, value); };  Vue.mixin({   beforeCreate() {     console.log("beforeCreate", this._uid);     target = this;   },   created() {     console.log("created", this._uid);     target = undefined;   } });

現(xiàn)在,當(dāng)我們運(yùn)行第一個示例時,我們將獲得一個計數(shù)器,該計數(shù)器每秒增加一個數(shù)字。

new Vue({   el: "#counter",   data: () => ({     counter: localStorage.getItem("counter")   }),   computed: {     even() {       return this.counter % 2 == 0;     }   },   template: `<div class="component">     <div>Counter: {{ counter }}</div>     <div>Counter is {{ even ? 'even' : 'odd' }}</div>   </div>` });
setInterval(() => {   const counter = localStorage.getItem("counter");   localStorage.setItem("counter", +counter + 1); }, 1000);

到此,關(guān)于“怎么在Vue中使localStorage具有響應(yīng)式”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI