首頁 >web前端 >Vue.js >vue2&vue3資料響應式原理分析及手動實作(實例詳解)

vue2&vue3資料響應式原理分析及手動實作(實例詳解)

WBOY
WBOY轉載
2021-12-22 18:25:282278瀏覽

本篇文章為大家帶來了vue2&vue3數據響應式原理分析及手動實現的相關知識,數據響應式視圖跟數據是自動更新的,數據更新的時候視圖是自動的更新的追踪數據的變化,希望對大家有幫助。

vue2&vue3資料響應式原理分析及手動實作(實例詳解)

資料響應式

  • 檢視跟資料是自動更新的,資料更新的時候檢視是自動的更新的
  • #追蹤資料的變化,在讀取資料或設定資料的時候能夠做一些劫持的一些動作
  • vue2 使用defineProperty
  • vue3 改用Proxy

使用defineProperty

如何追蹤變化

var obj = {}var age 
Object.defineProperty(obj, 'age', {
    get: function() {
        consoel.log('get age ...')
        return age    },
    set: function(val) {
        console.log('set age ...')
        age = val    }})obj.age =100 //set age ...console.log(obj.age)//get age ...

物件obj在取age屬性的時候會呼叫資料劫持的get方法
在給age屬性賦值的時候會呼叫set方法

那怎麼使用Object.defineProperty實作一個資料回應式呢

function defineReactive(data) {
  if (!data || Object.prototype.toString.call(data) !== '[object Object]')
    return;
  for (let key in data) {
    let val = data[key];
    Object.defineProperty(data, key, {
      enumerable: true, //可枚举
      configurable: true, //可配置
      get: function() {
        track(data, key);
        return val;
      },
      set: function() {
        trigger(val, key);
      },
    });
    if (typeof val === "object") {
      defineReactive(val);
    }
  }}function trigger(val, key) {
  console.log("sue set", val, key);}function track(val, key) {
  console.log("sue set", val, key);}const data = {
  name:'better',
  firends:['1','2']}defineReactive(data)console.log(data.name)console.log(data.firends[1])console.log(data.firends[0])console.log(Object.prototype.toString.call(data))

這個函數defineReactve用來對Object.defineProperty進行封裝,從函數名稱可以看出,運作就是定義一個響應式資料,封裝後只需要傳遞data,key和val就行
每當從data讀取key的時候觸發track函數,往data的key設定資料時,set函數中的trigger函數觸發

陣列的響應式

我們透過Array原型上的方法來改變陣列的內容不會觸發getter和setter
整理發現Array原型中可以改變陣列本身內容的方法有7個,分別push pop#shift unshift splice sort reverse
vue2 改寫了這7種方法
實作方式:
以Array.propertype為原型建立一個arrayMethods物件,再使用Object.setPropertypeOf(o, arryMethods)將o的__proto__指向arrayMethods

vue2&vue3資料響應式原理分析及手動實作(實例詳解)

如何收集依賴

使用

<template><p>{{name}}</p></template>

該範本中使用資料name, 我們要觀察資料, 當資料的屬性改變的時候, 可以通知哪些使用的地方,
這就是我們要先收集依賴,即把用到資料name的地方收集起來,然後等資料變化的時候,把之前收集好的依賴循環觸發一遍,總結來說就是getter中收集依賴,在setter中觸發依賴

使用proxy

Proxy物件用於建立一個物件的代理, 從而實現基本操作的攔截和定義(如屬性查找、賦值、枚舉、函數掉用等)

const p = new Proxy(target, handler)
  • target

  • 要使用 Proxy 包裝的目標物件(可以是任何類型的對象,包括原生數組,函數,甚至另一個代理) 。

  • handler

  • 一個通常以函數為屬性的對象,各屬性中的函數分別定義了在執行各種操作時代理 p 的行為。
    reflect是內建對象, 他提供攔截javascript操作的方法, 這些方法和Proxy handlers相同

Reflect.set將值分配給屬性的函數。傳回一個Boolean 如果更新成功則回傳true

Reflect.get取得物件身上某個屬性的值,類似target[name]

如何實作劫持

const dinner = {
  meal:'111'}const handler = {
  get(target, prop) {
    console.log('get...', prop)
    return Reflect.get(...arguments)
  },
  set(target, key, value) {
    console.log('get...', prop)
    console.log('set',key,value)
    return Reflect.set(...arguments)
  }}const proxy = new Proxy(dinner, handler)console.log(proxy.meal)console.log(proxy.meal)

程式碼中dinner 物件代理到handler上
defineProperty區別
defineProperty的屬性需要遍歷才能監管所有屬性

使用proxy可以將物件所有屬性進行代理

用proxy實作一個模擬響應式

function reactive(obj) {
  const handler = {
    get(target, prop, receiver) {
      track(target, prop);
      const value =  Reflect.get(...arguments);
      if(typeof value === 'Object') {
        reactive(value)
      }else {
        return value      }
    },
    set(target,key, value, receiver) {
      trigger(target,key, value);
      return Reflect.set(...arguments);
    },
  };
  return new Proxy(obj,handler)}function track(data, key) {
  console.log("sue set", data, key);}function trigger(data, key,value) {
  console.log("sue set", key,':',value);}const dinner = {
  name:'haochi1'}const proxy  =reactive(dinner)proxy.name
proxy.list = []proxy.list.push(1)

執行後自動列印

vue2&vue3資料響應式原理分析及手動實作(實例詳解)

#思考:為啥只在get中使用遞歸,set不使用呢?

賦值也需要先get

#簡單總結:

  1. #vue2 (淺響應式)
  • 遍歷data,使用defineProperty攔截所有屬性
  • 當使用者操作檢視,會觸發set攔截器
  • set先改變目前資料, 再通知wartch, 讓watch去通知檢視更新
  • 檢視重繪, 再次從get中取得對應的資料
  1. vue3 (深度回應式) :
  • 使用proxy 進行代理;攔截data任意屬性的任意操作(13種), 包括屬性的讀寫, 屬性的添加, 屬性的刪除等等

  • 使用Reflect進行反射;動態對被代理的物件的對應屬性進行特定的操作

  • 代理物件(proxy)的反射物件(reflect)必須相互配合才能實現響應式

兩者的不同

Proxy能劫持整個物件,而Object.defineProperty只能劫持物件的屬性; 前者遞歸回傳屬性對應的值的代理即可實現響應式,後者需要深度遍歷每個屬性,後者對數組的操作很不友好.

更多編程相關知識,請訪問:編程入門! !

以上是vue2&vue3資料響應式原理分析及手動實作(實例詳解)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:csdn.net。如有侵權,請聯絡admin@php.cn刪除