溫馨提示×

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

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

vue2和vue3數(shù)據(jù)響應(yīng)式原理分析及如何實(shí)現(xiàn)

發(fā)布時(shí)間:2021-12-22 20:22:07 來(lái)源:億速云 閱讀:168 作者:柒染 欄目:編程語(yǔ)言

今天就跟大家聊聊有關(guān)vue2和vue3數(shù)據(jù)響應(yīng)式原理分析及如何實(shí)現(xiàn),可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。

數(shù)據(jù)響應(yīng)式

  • 視圖跟數(shù)據(jù)是自動(dòng)更新的,數(shù)據(jù)更新的時(shí)候視圖是自動(dòng)的更新的

  • 追蹤數(shù)據(jù)的變化,在讀取數(shù)據(jù)或者設(shè)置數(shù)據(jù)的時(shí)候能夠做一些劫持的一些操作

  • 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 ...

對(duì)象obj在取age屬性的時(shí)候會(huì)調(diào)用數(shù)據(jù)劫持的get方法
在給age屬性賦值的時(shí)候會(huì)調(diào)用set方法

那怎么使用Object.defineProperty實(shí)現(xiàn)一個(gè)數(shù)據(jù)響應(yīng)式呢

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))

這個(gè)函數(shù)defineReactve用來(lái)對(duì)Object.defineProperty進(jìn)行封裝,從函數(shù)名可以看出,起作用就是定義一個(gè)響應(yīng)式數(shù)據(jù),封裝后只需要傳遞data,key和val就行
每當(dāng)從data中讀取key的時(shí)候觸發(fā)track函數(shù),往data的key中設(shè)置數(shù)據(jù)時(shí),set函數(shù)中的trigger函數(shù)觸發(fā)

數(shù)組的響應(yīng)式

我們通過(guò)Array原型上的方法來(lái)改變數(shù)組的內(nèi)容不會(huì)觸發(fā)getter和setter
整理發(fā)現(xiàn)Array原型中可以改變數(shù)組自身內(nèi)容的方法有7個(gè),分別push pop shift unshift splice sort reverse
vue2 改寫(xiě)了這這7種方法
實(shí)現(xiàn)方式:
以Array.propertype為原型創(chuàng)建一個(gè)arrayMethods對(duì)象,再使用Object.setPropertypeOf(o, arryMethods)將o的__proto__指向arrayMethods

vue2和vue3數(shù)據(jù)響應(yīng)式原理分析及如何實(shí)現(xiàn)

如何收集依賴

使用

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

該模板中使用數(shù)據(jù) name, 我們要觀察數(shù)據(jù), 當(dāng)數(shù)據(jù)的屬性發(fā)生變化的時(shí)候, 可以通知哪些使用的地方,
這就是我們要先收集依賴,即把用到數(shù)據(jù)name的地方收集起來(lái),然后等數(shù)據(jù)變化的時(shí)候,把之前收集好的依賴循環(huán)觸發(fā)一遍,總結(jié)來(lái)說(shuō)就是getter中收集依賴,在setter中觸發(fā)依賴

使用proxy

Proxy對(duì)象用于創(chuàng)建一個(gè)對(duì)象的代理, 從而實(shí)現(xiàn)基本操作的攔截和定義(如屬性查找、賦值、枚舉、函數(shù)掉用等)

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

  • 要使用 Proxy 包裝的目標(biāo)對(duì)象(可以是任何類型的對(duì)象,包括原生數(shù)組,函數(shù),甚至另一個(gè)代理)。

  • handler

  • 一個(gè)通常以函數(shù)作為屬性的對(duì)象,各屬性中的函數(shù)分別定義了在執(zhí)行各種操作時(shí)代理 p 的行為。
    reflect是一個(gè)內(nèi)置對(duì)象, 他提供攔截javascript操作的方法, 這些方法和Proxy handlers相同

Reflect.set將值分配給屬性的函數(shù)。返回一個(gè)Boolean 如果更新成功則返回true

Reflect.get獲取對(duì)象身上某個(gè)屬性的值,類似target[name]

如何實(shí)現(xiàn)劫持

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 對(duì)象代理到handler上
defineProperty區(qū)別
defineProperty的屬性需要遍歷才能監(jiān)管所有屬性

使用proxy可以將對(duì)象所有屬性進(jìn)行代理

用proxy實(shí)現(xiàn)一個(gè)模擬響應(yīng)式

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)

執(zhí)行后自動(dòng)打印

vue2和vue3數(shù)據(jù)響應(yīng)式原理分析及如何實(shí)現(xiàn)

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

賦值也需要先get

簡(jiǎn)單總結(jié):

  1. vue2 (淺響應(yīng)式)

  • 遍歷data,使用defineProperty攔截所有屬性

  • 當(dāng)用戶操作視圖,會(huì)觸發(fā)set攔截器

  • set先改變當(dāng)前數(shù)據(jù), 再通知wartch, 讓watch去通知視圖更新

  • 視圖重繪, 再次從get中獲取對(duì)應(yīng)的數(shù)據(jù)

  1. vue3 (深度響應(yīng)式) :

  • 使用proxy 進(jìn)行代理;攔截data任意屬性的任意操作(13種), 包括屬性的讀寫(xiě), 屬性的添加, 屬性的刪除等等

  • 使用Reflect進(jìn)行反射; 動(dòng)態(tài)對(duì)被代理的對(duì)象的相應(yīng)屬性進(jìn)行特定的操作

  • 代理對(duì)象(proxy)的反射對(duì)象(reflect)必須相互配合才能實(shí)現(xiàn)響應(yīng)式

兩者的不同

Proxy能劫持整個(gè)對(duì)象,而Object.defineProperty只能劫持對(duì)象的屬性; 前者遞歸返回屬性對(duì)應(yīng)的值的代理即可實(shí)現(xiàn)響應(yīng)式,后者需要深度遍歷每個(gè)屬性,后者對(duì)數(shù)組的操作很不友好.

看完上述內(nèi)容,你們對(duì)vue2和vue3數(shù)據(jù)響應(yīng)式原理分析及如何實(shí)現(xiàn)有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝大家的支持。

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

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

vue
AI