Vue響應式系統如何操作運用?本文詳解

来源:https://www.cnblogs.com/chengxuyuanaa/archive/2020/06/10/13089130.html
-Advertisement-
Play Games

前言 之前學習 vue 的時候,一直沒刨根問底過。在看到網上這類文章比較多,參差不齊的質量有時候看的一頭霧水。當然也有不錯的文章,但是終究是別人的理解。於是寫一篇關於自己的理解記錄下來,親身實踐才能收穫更多! 初階:響應式原理 在說明之前,我們先瞭解一個 Object.defineProperty( ...


前言

之前學習 vue 的時候,一直沒刨根問底過。在看到網上這類文章比較多,參差不齊的質量有時候看的一頭霧水。當然也有不錯的文章,但是終究是別人的理解。於是寫一篇關於自己的理解記錄下來,親身實踐才能收穫更多!

初階:響應式原理

在說明之前,我們先瞭解一個 Object.defineProperty() 。引用 MDN 上的權威介紹 developer.mozilla.org/zh-CN/docs/… :

Object.defineProperty() 方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性,並返回此對象。

語法

Object.defineProperty(obj, prop, descriptor)
複製代碼

參數

  • obj

    要定義屬性的對象。

  • prop

    要定義或修改的屬性的名稱或 Symbol 。

  • descriptor

    要定義或修改的屬性描述符。

返回值

被傳遞給函數的對象。

註意:除了本文項目。我還結合多年開發經驗整理出2020最新企業級實戰視頻教程, 包括 Vue3.0/Js/ES6/TS/React/node等,有興趣的進扣扣裙 519293536 免費獲取,小白勿進哦!接下來我們繼續
在瞭解了這個之後,我們就可以用它來實現一個響應式的初級樣子。

const TestObject = {
  name:"",
  age:10
}
let tempValue = '';
Object.defineProperty(TestObject,'name',{
  
  get:function (){
    console.log('我被獲取了,我可以在這裡搞點事情!')
    return tempValue;
  },
  set:function (newValue){
    console.log('我被寫入了,我可以在這裡搞點事情!')
    tempValue = newValue
  }
})
複製代碼

此時使用 TestObject.name 方法可以讓 get 和 set 裡面的對應生效。

我想看到這裡,聰明的同學可能會有一個疑問,為啥要搞一個 tempValue ?我直接 TestObject.name 在 get 和 set 裡面賦值不行麽?就像這樣:

const TestObject = {
  name:"",
  age:10
}
Object.defineProperty(TestObject,'name',{
  get:function (){
    console.log('我被獲取了,我可以在這裡搞點事情!')
    return TestObject.name;
  },
  set:function (newValue){
    console.log('我被寫入了,我可以在這裡搞點事情!')
    TestObject.name = newValue
  }
})
複製代碼

真正運行的時候,其實會發現,陷入了死迴圈,這裡大家切忌要避免坑!

我們重讀MDN上的文檔可以發現,get 和 set 本身就是在獲取和設置的時候觸發的函數,在裡面寫了 TestObject.name ,那麼就會繼續調用 set ,然後繼續 TestObject.name ,繼續 set ,繼續.....無限迴圈。所以使用一個臨時變數在外面,是比較安全的做法。

中階:接管對象

在前面基礎上,我們現在可以開始接管整個對象了,邏輯非常簡單,套個迴圈,上代碼:

function ProxyObj(obj){
  Object.keys(obj).forEach(key=>{
    DefineObj(obj,key,obj[key])
  })
}
function DefineObj(obj,key,value){
  Object.defineProperty(obj,key,{
    get:function (){
      console.log('我被獲取了,我可以在這裡搞點事情!')
      return value;
    },
    set:function (newValue){
      console.log('我被寫入了,我可以在這裡搞點事情!')
      value = newValue
    }
  })
}
const TestObject = {
  name:"",
  age:10
}
ProxyObj(TestObject)
複製代碼

這時,聰明的同學又會有疑問了?為什麼要創建兩個 function ,ProxyObj 和 DefineObj 不能堆在一個裡面麽?就像這樣

function ProxyObj(obj){
  Object.keys(obj).forEach(key=>{
    Object.defineProperty(obj,key,{
    get:function (){
      console.log('我被獲取了,我可以在這裡搞點事情!')
      return obj[key];
    },
    set:function (newValue){
      console.log('我被寫入了,我可以在這裡搞點事情!')
      obj[key] = newValue
    }
  })
  })
}
const TestObject = {
  name:"",
  age:10
}
ProxyObj(TestObject)
複製代碼

不能,其實原因跟之前提到的問題一樣,存在死迴圈的問題。那為什麼我們拆分開來就不存在呢?因為這裡的 DefineObj 傳入的形參。用我們瞭解到的 js 基礎知識來解釋,一個函數內的形參,相當於是函數內預設的變數,一般情況下(也會有二般情況)這個變數的生命周期僅在函數內。所以利用了這個特性,案例巧妙的將形參 value 拿出來傳遞和賦值,就不存在無限死迴圈的問題了。

劃重點:使用 Object.defineProperty 切忌不要陷入到死迴圈當中!

高階:收集依賴

理解需求

終於來到了收集依賴環節,這塊也是我之前一直沒有想通的地方。直到第二天醒來朦朧中看著屏幕前的代碼,突然若有所思了,話不多說,直接來看看需求,對於需求都沒有非常清晰的概念,直接上代碼有點暈。

const TestObject = {
  name:"",
  age:10
}

watcher(TestObject,'type',()=>{
 return TestObject.age>18?'成年人':'未成年人';
})
複製代碼

簡單實現

我們需要實現這個watcher函數,裡面可以填入對象,並且設置關註的對象 type 屬性,此時該屬性還沒有在對象身上,我們需要賦予一下,當 age 變化以後,type 也要對應著改變一下。這就是我們所說的依賴收集需求。

我們這樣簡單實現一下:

function watcher(obj,key,cb){
  Object.defineProperty(obj,key,{
    get:function (){
      const val = cb();
      return val;
    },
    set:function(newValue){
      console.log('該屬性是被用於去自動計算的哦~不要人工賦值!')
    }  
  })
}

const TestObject = {
  name:"",
  age:10
}

watcher(TestObject,'type',()=>{
  return TestObject.age>18?'成年人':'未成年人';
})
console.log(TestObject.type)//未成年人
TestObject.age=19;
console.log(TestObject.type)//成年人
複製代碼

可以看到,我們通過這樣的方式,就簡單的實現了 TestObject 屬性上的依賴計算屬性 。

完善需求

但是問題又來了,我如果不去讀取 type 它是不會主動更新的。如何做到 age 變化以後,type 自動更新呢?

在前面的基礎上,我們先定義一個依賴更新時候的函數 updateTodo 此時代碼變這樣:

let target = '';

function watcher(obj,key,cb){
  function updateTodo(){
    const val = cb();
    console.log('更新啦',val)
    }
  Object.defineProperty(obj,key,{
    get:function (){
      target = updateTodo;//重點代碼
      const val = cb();//重點代碼
      target = '';//重點代碼
      return val;
    },
    set:function(newValue){
      console.log('該屬性是被用於去自動計算的哦~不要人工賦值!')
    }  
  })
}

const TestObject = {
  name:"",
  age:10
}

watcher(TestObject,'type',()=>{
  return TestObject.age>18?'成年人':'未成年人';//重點代碼
})
console.log(TestObject.type)
TestObject.age = 19;
console.log(TestObject.type)
複製代碼

會發現我們順便在全局還定義一個target,儲存當前 callback,這裡是我覺得最重要的部分,一定要認真看裡面的這段,思考一下,我單獨拿出來:

target = updateTodo;
const val = cb();
target = '';
複製代碼

callback函數長這樣:

()=>{
  return TestObject.age>18?'成年人':'未成年人';
}
複製代碼

可能大家會有疑問,target 賦一下有重置是什麼騷操作?有何意義?

註意, callback 裡面有一個 TestObject.age !這個一旦被訪問,它的 get 函數就被調用了!那麼此刻,前面target裡面儲存的 updateTodo 函數,是不是就可以在 get 裡面取到了呢?

所以在 cb() 執行完之後,實際上裡面就有機會收集依賴了,target 就是這個作用,作為一個臨時的 callback 緩存著,那麼我們的需求也很好解決了,只需要在這裡進行一次依賴的收集和釋放即可!

這裡的疑問解決了以後,就開始直接上代碼吧!後面就比較好理解,跟我們前面的接管對象章節做一個合併,直接展示完整代碼,細細品味,非常有意思:

function ProxyObj(obj){
  Object.keys(obj).forEach(key=>{
    DefineObj(obj,key,obj[key])
  })
}

function DefineObj(obj,key,value){
  let deps = [];
  Object.defineProperty(obj,key,{
    get:function (){
      console.log('我被獲取了,我可以在這裡搞點事情!')
      //依賴收集
      if(target && !deps.includes(target)){//判斷是否存在或重覆依賴
        deps.push(target)
      }
      return value;
    },
    set:function (newValue){
      console.log('我被寫入了,我可以在這裡搞點事情!')
      value = newValue;
      deps.forEach(fn=>{//依賴釋放
        fn()
      })
    }
  })
}

function watcher(obj,key,cb){
  function updateTodo(){
    const val = cb();
    console.log('更新啦',val)
    }
  Object.defineProperty(obj,key,{
    get:function (){
      target = updateTodo;
      const val = cb();
      target='';
      return val;
    },
    set:function(newValue){
      console.log('該屬性是被用於去自動計算的哦~不要人工賦值!')
    }  
  })
}


let target = '';
const TestObject = {
  name:"",
  age:10
}

ProxyObj(TestObject)
watcher(TestObject,'type',()=>{
  return TestObject.age>18?'成年人':'未成年人';
})
console.log(TestObject.type)
TestObject.age=19;
console.log(TestObject.type)
複製代碼

當然,如果你對設計模式和一些代碼整潔有要求,看到這裡你可能會覺得非常不爽,沒有關係,嘗試著自己來封裝下吧,或者參看 Vue 內的源碼部分。

數組類型檢測原理

1.關於數組方面,可以參看 vue 源碼部分。
主要邏輯是通過新建一個 Array 對象來代理實現原生 Array 對象的7大操作。當我們在 vue 裡面使用數組方法的時候,實際上使用的是 vue 替我們實現的一些方法,而不是原生的數組操作。
2.這也很好解釋了,為什麼 vue 不推薦大家使用 [] 方式修改數組。因為受限於 js 語言本身,無法實現對 [] 修改的檢測。
3.註意:除了本文項目。我還結合多年開發經驗整理出2020最新企業級實戰視頻教程, 包括 Vue3.0/Js/ES6/TS/React/node等,有興趣的進扣扣裙 519293536 免費獲取,小白勿進哦!

本文的文字及圖片來源於網路加上自己的想法,僅供學習、交流使用,不具有任何商業用途,版權歸原作者所有,如有問題請及時聯繫我們以作處理




您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.)說明. 本項目是來自github上的一個項目roottools (https://github.com/Stericson/RootTools ),這裡只是想本地編譯後輸出下jar包供自己進行使用。 2.)操作步驟. 步驟1)按之前你熟悉的方式進行開發待輸出為jar的項目. 步驟2) 一般的g ...
  • 根據本人工作遇到的一些情況,總結如下: 1.)安裝包的最低版本要求高於待安裝的手機的android版本。2.)目標手機上已經存在相同包名的app,且簽名不一樣.3.)android studio下打包輸出app時, 只選擇了v2, 並且遇到的手機是低版本的。會出現安裝失敗.4.)目標手機可用記憶體太低 ...
  • .android文件夾主要是用來存放模擬器的,是占用空間最大的一個,如果你沒有使用它的模擬器,可以直接把這個文件夾刪除。因為我建立了兩個x86的模擬器,吃掉了我20G的空間。果斷搬走。.gradle這個目錄主要是項目編譯時所用的,更改比較簡單,AndroidStudio 有給我們留下介面。.Andr... ...
  • 插件 flutter-carousel-slider A flutter carousel widget, support infinite scroll, and custom child widget. code-builder A fluent API for generating valid ...
  • Js--字元串拼接/連接 博客說明 文章所涉及的資料來自互聯網整理和個人總結,意在於個人學習和經驗彙總,如有什麼地方侵權,請聯繫本人刪除,謝謝! 說明 在開發的過程中很容易遇到字元串連接的問題,下麵列舉三種方法 使用加號運算符 連接字元串最簡便的方法是使用加號運算符。 使用加號運算符連接兩個字元串 ...
  • 本文只是複習HTML筆記 html 骨架: DTD:文檔類型定義,規定了使用哪個版本的html規範 html 標簽:雙標簽,表示整個網頁 head 標簽: 配置HTML頁面 title: 網頁標題 meta :設置字元集 charset body : 書寫標簽組成的網頁 語法: 標簽之間對空格,縮進 ...
  • 前言 就在前段時間,vue官方發佈了3.0.0-beta.1 版本,趁著五一假期有時間,就把之前的一個電商商城的項目,用最新的Composition API拿來改造一下! 👉GitHub地址請訪問🔗:github.com/GitHubGanKa… 項目介紹 vue-jd-h5是一個電商H5頁面前 ...
  • 相信只要你去面試vue,都會被問到vue的雙向數據綁定,你要是就說個mvvm就是視圖模型模型視圖,只要數據改變視圖也會同時更新!那你離被pass就不遠了! 視頻已錄製,地址(www.bilibili.com/video/BV1qJ…) 幾種實現雙向綁定的做法 目前幾種主流的mvc(vm)框架都實現了 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...