最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深入理解Vue響應式原理及其實現(xiàn)方式

 更新時間:2023年05月11日 14:58:08   作者:世界和平  
Vue的響應式原理是Vue最核心的特性之一,也是Vue能夠為開發(fā)者提供高效便捷的開發(fā)體驗的重要原因之一,這篇文章主要介紹了響應式的原理及其實現(xiàn)方式,需要詳細了解可以參考下文

Vue的響應式

用過Vue這個框架的人應該都知道,數(shù)據(jù)驅(qū)動是Vue框架的核心,數(shù)據(jù)雙向綁定是它的一大特色,根據(jù)官方的解釋,我們可以比較清晰地去知道響應式的簡單原理。

Vue2的響應式原理

當你把一個普通的 JavaScript 對象傳入 Vue 實例作為 data 選項,Vue 將遍歷此對象所有的 property,并使用 Object.defineProperty 把這些 property 全部轉(zhuǎn)為 getter/setter。Object.defineProperty 是 ES5 中一個無法 shim 的特性,這也就是 Vue 不支持 IE8 以及更低版本瀏覽器的原因。

這些 getter/setter 對用戶來說是不可見的,但是在內(nèi)部它們讓 Vue 能夠追蹤依賴,在 property 被訪問和修改時通知變更。這里需要注意的是不同瀏覽器在控制臺打印數(shù)據(jù)對象時對 getter/setter 的格式化并不同,所以建議安裝 vue-devtools 來獲取對檢查數(shù)據(jù)更加友好的用戶界面。

每個組件實例都對應一個 watcher 實例,它會在組件渲染的過程中把“接觸”過的數(shù)據(jù) property 記錄為依賴。之后當依賴項的 setter 觸發(fā)時,會通知 watcher,從而使它關(guān)聯(lián)的組件重新渲染。

Vue3的響應式原理

實現(xiàn)原理:

通過Proxy(代理): 攔截對象中任意屬性的變化,包括:屬性值的讀寫,屬性的增加,屬性的刪除等。

通過Reffect(反射): 對源對象的屬性進行操作

new Proxy(data,{
  //攔截讀取屬性值
  get(target, prop){
    return Reflect.get(target, prop)
  },
  //攔截設置屬性值或添加新屬性
  set(target, prop, value){
    return Reflect.set(target, prop, value)
  },
  //攔截刪除屬性
  deleteProperty(target, prop){
    return Reflect.deleteProperty(target, prop)
  }
})

Vue2和Vue3的響應式原理其實有異曲同工之妙,但是Vue3的proxy封裝性和獨立性相對更強更靈活一些,但是我們看到的這些只是最簡單的,也是最基礎的一個響應式原理,如果要更深入地去了解Vue是如何利用這一原理去實現(xiàn)框架中的各種雙向綁定和數(shù)據(jù)渲染操作,我們可以對它的源碼進行分析。

深入理解響應式

1.數(shù)據(jù)初始化

new Vue({
  el: "#app",
  router,
  store,
  render: (h) => h(App),
});

這段代碼,大家一定非常熟悉。這就是 Vue 實例化的過程 從 new 操作符,咱們可以看出 Vue 其實就是一個構(gòu)造函數(shù),沒啥特別的,傳入的參數(shù)就是一個對象,我們叫做 options(選項)。

// src/index.js
import { initMixin } from "./init.js";
// Vue就是一個構(gòu)造函數(shù) 通過new關(guān)鍵字進行實例化
function Vue(options) {
  // 這里開始進行Vue初始化工作
  this._init(options);
}
// _init方法是掛載在Vue原型的方法 通過引入文件的方式進行原型掛載需要傳入Vue
// 此做法有利于代碼分割
initMixin(Vue);
export default Vue;

因為在 Vue 初始化可能會處理很多事情,比如數(shù)據(jù)處理,事件處理,生命周期處理等等,所以劃分不同文件引入利于代碼分割。

// src/init.js
import { initState } from "./state";
export function initMixin(Vue) {
  Vue.prototype._init = function (options) {
    const vm = this;
    // 這里的this代表調(diào)用_init方法的對象(實例對象)
    //  this.$options就是用戶new Vue的時候傳入的屬性
    vm.$options = options;
    // 初始化狀態(tài)
    initState(vm);
  };
}

initMixin 把_init 方法掛載在 Vue 原型 供 Vue 實例調(diào)用。

// src/state.js
import { observe } from "./observer/index.js";
export function initState(vm) {
  // 獲取傳入的數(shù)據(jù)對象
  const opts = vm.$options;
  if (opts.props) {
    initProps(vm);
  }
  if (opts.methods) {
    initMethod(vm);
  }
  if (opts.data) {
    // 初始化data
    initData(vm);
  }
  if (opts.computed) {
    initComputed(vm);
  }
  if (opts.watch) {
    initWatch(vm);
  }
}
// 初始化data數(shù)據(jù)
function initData(vm) {
  let data = vm.$options.data;
  //   實例的_data屬性就是傳入的data
  // vue組件data推薦使用函數(shù) 防止數(shù)據(jù)在組件之間共享
  data = vm._data = typeof data === "function" ? data.call(vm) : data || {};
  // 把data數(shù)據(jù)代理到vm 也就是Vue實例上面 我們可以使用this.a來訪問this._data.a
  for (let key in data) {
    proxy(vm, `_data`, key);
  }
  // 對數(shù)據(jù)進行觀測 --響應式數(shù)據(jù)核心
  observe(data);
}
// 數(shù)據(jù)代理
function proxy(object, sourceKey, key) {
  Object.defineProperty(object, key, {
    get() {
      return object[sourceKey][key];
    },
    set(newValue) {
      object[sourceKey][key] = newValue;
    },
  });
}

①通過這段代碼,就可以得到一個平時開發(fā)Vue項目的時候?qū)τ谖覀冇泻艽髱椭男畔ⅲ搓P(guān)于數(shù)據(jù)初始化的順序依次是 prop>methods>data>computed>watch。關(guān)于我們能否在data里面去調(diào)用prop的值的問題,如果知道數(shù)據(jù)渲染的順序,就迎刃而解了。

②另外通過這段源碼,我們還可以獲得一個信息,data是用了函數(shù)function封裝,而不是對象Object,就是為了避免數(shù)據(jù)在組件間共享,這樣我們每個組件才能有獨立的變量作用域。

2.對象的數(shù)據(jù)劫持

對象數(shù)據(jù)的劫持,其實很好理解,代碼中通過遞歸的方式,把對象中的每個參數(shù)都添加了對應的監(jiān)聽器,所以當對象數(shù)據(jù)發(fā)生變化的時候自然就會觸發(fā)監(jiān)聽器。

這里我們可以得到一個信息,對象只有在初始化階段的時候進行了監(jiān)聽標記,當我們后續(xù)為對象新增參數(shù)的時候,必須通過Vue提供的內(nèi)置函數(shù) s e t 和 set和 set和delete才能對對象參數(shù)進行動態(tài)操作,不然直接通過Object.xxx去新增參數(shù),這個時候是不具備雙向綁定的效果的。

// src/obserber/index.js
class Observer {
  // 觀測值
  constructor(value) {
    this.walk(value);
  }
  walk(data) {
    // 對象上的所有屬性依次進行觀測
    let keys = Object.keys(data);
    for (let i = 0; i < keys.length; i++) {
      let key = keys[i];
      let value = data[key];
      defineReactive(data, key, value);
    }
  }
}
// Object.defineProperty數(shù)據(jù)劫持核心 兼容性在ie9以及以上
function defineReactive(data, key, value) {
  observe(value); // 遞歸關(guān)鍵
  // --如果value還是一個對象會繼續(xù)走一遍odefineReactive 層層遍歷一直到value不是對象才停止
  //   思考?如果Vue數(shù)據(jù)嵌套層級過深 >>性能會受影響
  Object.defineProperty(data, key, {
    get() {
      console.log("獲取值");
      return value;
    },
    set(newValue) {
      if (newValue === value) return;
      console.log("設置值");
      value = newValue;
    },
  });
}
export function observe(value) {
  // 如果傳過來的是對象或者數(shù)組 進行屬性劫持
  if (
    Object.prototype.toString.call(value) === "[object Object]" ||
    Array.isArray(value)
  ) {
    return new Observer(value);
  }
}

數(shù)組的監(jiān)聽

// src/obserber/index.js
import { arrayMethods } from "./array";
class Observer {
  constructor(value) {
    if (Array.isArray(value)) {
      // 這里對數(shù)組做了額外判斷
      // 通過重寫數(shù)組原型方法來對數(shù)組的七種方法進行攔截
      value.__proto__ = arrayMethods;
      // 如果數(shù)組里面還包含數(shù)組 需要遞歸判斷
      this.observeArray(value);
    } else {
      this.walk(value);
    }
  }
  observeArray(items) {
    for (let i = 0; i < items.length; i++) {
      observe(items[i]);
    }
  }
}

數(shù)組的監(jiān)聽,是對數(shù)組的每個元素進行判斷,如果數(shù)組中還包含數(shù)組則需要遞歸進行監(jiān)聽,如果非數(shù)組元素則直接對數(shù)組進行監(jiān)聽設置的操作。

因為對數(shù)組下標的攔截太浪費性能 對 Observer 構(gòu)造函數(shù)傳入的數(shù)據(jù)參數(shù)增加了數(shù)組的判斷。

// src/obserber/index.js
class Observer {
  // 觀測值
  constructor(value) {
    Object.defineProperty(value, "__ob__", {
      //  值指代的就是Observer的實例
      value: this,
      //  不可枚舉
      enumerable: false,
      writable: true,
      configurable: true,
    });
  }
}

最后為了方便我們對數(shù)組的操作,Vue對數(shù)組的一些常用方法進行了重寫,當我們調(diào)用這些方法,Vue底層會為我們自動添加對應的監(jiān)聽器,不用讓我們再次去對元素進行數(shù)據(jù)渲染和綁定。

// src/obserber/array.js
// 先保留數(shù)組原型
const arrayProto = Array.prototype;
// 然后將arrayMethods繼承自數(shù)組原型
// 這里是面向切片編程思想(AOP)--不破壞封裝的前提下,動態(tài)的擴展功能
export const arrayMethods = Object.create(arrayProto);
let methodsToPatch = [
  "push",
  "pop",
  "shift",
  "unshift",
  "splice",
  "reverse",
  "sort",
];
methodsToPatch.forEach((method) => {
  arrayMethods[method] = function (...args) {
    //   這里保留原型方法的執(zhí)行結(jié)果
    const result = arrayProto[method].apply(this, args);
    // 這句話是關(guān)鍵
    // this代表的就是數(shù)據(jù)本身 比如數(shù)據(jù)是{a:[1,2,3]} 那么我們使用a.push(4)  this就是a  ob就是a.__ob__ 這個屬性就是上段代碼增加的 代表的是該數(shù)據(jù)已經(jīng)被響應式觀察過了指向Observer實例
    const ob = this.__ob__;
    // 這里的標志就是代表數(shù)組有新增操作
    let inserted;
    switch (method) {
      case "push":
      case "unshift":
        inserted = args;
        break;
      case "splice":
        inserted = args.slice(2);
      default:
        break;
    }
    // 如果有新增的元素 inserted是一個數(shù)組 調(diào)用Observer實例的observeArray對數(shù)組每一項進行觀測
    if (inserted) ob.observeArray(inserted);
    // 之后咱們還可以在這里檢測到數(shù)組改變了之后從而觸發(fā)視圖更新的操作--后續(xù)源碼會揭曉
    return result;
  };
});

到此這篇關(guān)于深入理解Vue響應式原理及其實現(xiàn)方式的文章就介紹到這了,更多相關(guān)Vue響應式原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解Vue單元測試Karma+Mocha學習筆記

    詳解Vue單元測試Karma+Mocha學習筆記

    本篇文章主要介紹了詳解Vue單元測試Karma+Mocha學習筆記,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-01-01
  • 關(guān)于在vscode使用webpack指令顯示

    關(guān)于在vscode使用webpack指令顯示"因為在此系統(tǒng)中禁止運行腳本"問題(完美解決)

    這篇文章主要介紹了解決在vscode使用webpack指令顯示"因為在此系統(tǒng)中禁止運行腳本"問題,本文給大家分享完美解決方法,需要的朋友可以參考下
    2021-07-07
  • vue中v-model和.sync修飾符的區(qū)別

    vue中v-model和.sync修飾符的區(qū)別

    在平時開發(fā)是經(jīng)常用到一些父子組件通信,經(jīng)常用到props、vuex等等,下面這篇文章主要給大家介紹了關(guān)于vue中v-model和.sync修飾符區(qū)別的相關(guān)資料,需要的朋友可以參考下
    2022-06-06
  • Vue3.0結(jié)合bootstrap創(chuàng)建多頁面應用

    Vue3.0結(jié)合bootstrap創(chuàng)建多頁面應用

    這篇文章主要介紹了Vue3.0結(jié)合bootstrap創(chuàng)建多頁面應用,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-05-05
  • Vue使用富文本編輯器Vue-Quill-Editor(含圖片自定義上傳服務、清除復制粘貼樣式等)

    Vue使用富文本編輯器Vue-Quill-Editor(含圖片自定義上傳服務、清除復制粘貼樣式等)

    這篇文章主要介紹了Vue項目中使用富文本編輯器Vue-Quill-Editor(含圖片自定義上傳服務、清除復制粘貼樣式等)的相關(guān)知識,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-05-05
  • Vue解決echart在element的tab切換時顯示不正確問題

    Vue解決echart在element的tab切換時顯示不正確問題

    這篇文章主要介紹了Vue解決echart在element的tab切換時顯示不正確問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • 詳解vue中在父組件點擊按鈕觸發(fā)子組件的事件

    詳解vue中在父組件點擊按鈕觸發(fā)子組件的事件

    這篇文章主要介紹了詳解vue中在父組件點擊按鈕觸發(fā)子組件的事件,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-11-11
  • 配置vue全局方法的兩種方式實例

    配置vue全局方法的兩種方式實例

    vue項目中有一些方法需要在多個頁面調(diào)用,但為了避免在每個頁面都import進來,可以把方法加到原型上去,這樣在每個組件中都能使用了,下面這篇文章主要給大家介紹了關(guān)于配置vue全局方法的兩種方式,需要的朋友可以參考下
    2021-09-09
  • Vue中EpicEditor和vue-quill-editor的使用詳解

    Vue中EpicEditor和vue-quill-editor的使用詳解

    EpicEditor和Vue-quill-editor都是基于Quill.js的富文本編輯器,并且都提供了許多強大的功能,下面我們就來介紹一下二者的具體使用,感興趣的小伙伴可以了解一下
    2023-11-11
  • Vue編寫自定義Plugin詳解

    Vue編寫自定義Plugin詳解

    這篇文章主要介紹了Vue編寫自定義Plugin詳解,在Vue開發(fā)中,我們經(jīng)常需要使用一些第三方庫或功能性模塊,Vue插件就是一種將這些庫或模塊集成到Vue應用中的方式,插件是Vue.js提供的一種機制,用于擴展Vue的功能,需要的朋友可以參考下
    2023-08-08

最新評論

平湖市| 六枝特区| 久治县| 克东县| 澄城县| 且末县| 临邑县| 镇巴县| 扎赉特旗| 江油市| 商河县| 平罗县| 江安县| 保山市| 陆良县| 陆丰市| 沅江市| 普兰店市| 台南市| 咸宁市| 年辖:市辖区| 甘肃省| 武陟县| 长治县| 山阳县| 杭锦旗| 岳普湖县| 礼泉县| 富裕县| 会东县| 太白县| 宣城市| 黄冈市| 南通市| 宾阳县| 尉犁县| 常熟市| 遵化市| 巴彦县| 沾益县| 福安市|