日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
思想實(shí)驗(yàn):如何在Vue中使localStorage具有響應(yīng)式?

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

成都創(chuàng)新互聯(lián)專注于耀州網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠(chéng)為您提供耀州營(yíng)銷型網(wǎng)站建設(shè),耀州網(wǎng)站制作、耀州網(wǎng)頁(yè)設(shè)計(jì)、耀州網(wǎng)站官網(wǎng)定制、小程序開(kāi)發(fā)服務(wù),打造耀州網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供耀州網(wǎng)站排名全網(wǎng)營(yíng)銷落地服務(wù)。

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

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

 
 
 
 
  1. new Vue({
  2.   el: "#counter",
  3.   data: () => ({
  4.     counter: localStorage.getItem("counter")
  5.   }),
  6.   computed: {
  7.     even() {
  8.       return this.counter % 2 == 0;
  9.     }
  10.   },
  11.   template: `
  12.     
    Counter: {{ counter }}
  13.     
    Counter is {{ even ? 'even' : 'odd' }}
  14.   
`
  • });
  •  
     
     
     
    1. // some-other-file.js
    2. setInterval(() => {
    3.   const counter = localStorage.getItem("counter");
    4.   localStorage.setItem("counter", +counter + 1);
    5. }, 1000);

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

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

    Vue 中的響應(yīng)式

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

     
     
     
     
    1. // core/instance/state.js
    2. function initData () {
    3.   // ...
    4.   observe(data)
    5. }
     
     
     
     
    1. // core/observer/index.js
    2. export function observe (value) {
    3.   // ...
    4.   new Observer(value)
    5.   // ...
    6. }
    7. export class Observer {
    8.   // ...
    9.   constructor (value) {
    10.     // ...
    11.     this.walk(value)
    12.   }
    13.   
    14.   walk (obj) {
    15.     const keys = Object.keys(obj)
    16.     for (let i = 0; i < keys.length; i++) {
    17.       defineReactive(obj, keys[i])
    18.     }
    19.   }
    20. export function defineReactive (obj, key, ...) {
    21.   const dep = new Dep()
    22.   // ...
    23.   Object.defineProperty(obj, key, {
    24.     // ...
    25.     get() {
    26.       // ...
    27.       dep.depend()
    28.       // ...
    29.     },
    30.     set(newVal) {
    31.       // ...
    32.       dep.notify()
    33.     }
    34.   })
    35. }

    所以,為什么 localStorage 不響應(yīng)?因?yàn)樗皇蔷哂袑傩缘膶?duì)象。

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

    我們可以對(duì) localStorage 做類似的事情嗎?

    覆蓋localStorage函數(shù)

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

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

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

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

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

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

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

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

    但是,他們又是誰(shuí)?

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

    • 當(dāng)它們被創(chuàng)建時(shí),它們會(huì)評(píng)估函數(shù)。這將觸發(fā)依賴關(guān)系的集合。
    • 當(dāng)他們被通知他們所依賴的一個(gè)值發(fā)生變化時(shí),他們會(huì)重新運(yùn)行他們的函數(shù)。這將最終重新計(jì)算一個(gè)計(jì)算出的屬性或重新渲染整個(gè)組件。

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

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

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

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

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

     
     
     
     
    1. // LocalStorage項(xiàng)目鍵與依賴它的Vue實(shí)例列表之間的映射
    2. const storeItemSubscribers = {};
    3. // 當(dāng)前正在初始化的Vue實(shí)例
    4. let target = undefined;
    5. const getItem = window.localStorage.getItem;
    6. localStorage.getItem = (key) => {
    7.   console.info("Getting", key);
    8.   // 收集依賴的Vue實(shí)例
    9.   if (!storeItemSubscribers[key]) storeItemSubscribers[key] = [];
    10.   if (target) storeItemSubscribers[key].push(target);
    11.   // 調(diào)用原始函數(shù) 
    12.   return getItem.call(localStorage, key);
    13. };
    14. const setItem = window.localStorage.setItem;
    15. localStorage.setItem = (key, value) => {
    16.   console.info("Setting", key, value);
    17.   // 更新相關(guān)Vue實(shí)例中的值
    18.   if (storeItemSubscribers[key]) {
    19.     storeItemSubscribers[key].forEach((dep) => {
    20.       if (dep.hasOwnProperty(key)) dep[key] = value;
    21.     });
    22.   }
    23.   
    24.   // 調(diào)用原始函數(shù) 
    25.   setItem.call(localStorage, key, value);
    26. };
    27. Vue.mixin({
    28.   beforeCreate() {
    29.     console.log("beforeCreate", this._uid);
    30.     target = this;
    31.   },
    32.   created() {
    33.     console.log("created", this._uid);
    34.     target = undefined;
    35.   }
    36. });

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

     
     
     
     
    1. new Vue({
    2.   el: "#counter",
    3.   data: () => ({
    4.     counter: localStorage.getItem("counter")
    5.   }),
    6.   computed: {
    7.     even() {
    8.       return this.counter % 2 == 0;
    9.     }
    10.   },
    11.   template: `
    12.     
      Counter: {{ counter }}
    13.     
      Counter is {{ even ? 'even' : 'odd' }}
    14.   
    `
  • });
  •  
     
     
     
    1. setInterval(() => {
    2.   const counter = localStorage.getItem("counter");
    3.   localStorage.setItem("counter", +counter + 1);
    4. }, 1000);

    我們的思想實(shí)驗(yàn)結(jié)束

    當(dāng)我們解決了最初的問(wèn)題時(shí),請(qǐng)記住這主要是一個(gè)思想實(shí)驗(yàn)。它缺少一些功能,例如處理已刪除的項(xiàng)目和未安裝的組件實(shí)例。它還具有一些限制,例如組件實(shí)例的屬性名稱需要與存儲(chǔ)在 localStorage 中的項(xiàng)目相同的名稱。就是說(shuō),主要目標(biāo)是更好地了解Vue響應(yīng)式在幕后的工作方式并充分利用這一點(diǎn),因此,我希望你能從所有這些事情中受益。


    分享名稱:思想實(shí)驗(yàn):如何在Vue中使localStorage具有響應(yīng)式?
    網(wǎng)頁(yè)路徑:http://www.5511xx.com/article/dhjcgsg.html