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

Vue數(shù)據(jù)劫持詳情介紹

 更新時(shí)間:2022年08月15日 09:29:26   作者:夏日  
這篇文章主要介紹了Vue數(shù)據(jù)劫持詳情介紹,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,感興趣的小伙伴可以參考一下,希望對(duì)你的學(xué)習(xí)有所幫助

前言

Vue會(huì)對(duì)我們?cè)?code>data中傳入的數(shù)據(jù)進(jìn)行攔截:

  • 對(duì)象:遞歸的為對(duì)象的每個(gè)屬性都設(shè)置get/set方法
  • 數(shù)組:修改數(shù)組的原型方法,對(duì)于會(huì)修改原數(shù)組的方法進(jìn)行了重寫

在用戶為data中的對(duì)象設(shè)置值、修改值以及調(diào)用修改原數(shù)組的方法時(shí),都可以添加一些邏輯來進(jìn)行處理,實(shí)現(xiàn)數(shù)據(jù)更新頁面也同時(shí)更新。

Vue中的響應(yīng)式(reactive): 對(duì)對(duì)象屬性或數(shù)組方法進(jìn)行了攔截,在屬性或數(shù)組更新時(shí)可以同時(shí)自動(dòng)地更新視圖。在代碼中被觀測過的數(shù)據(jù)具有響應(yīng)性

創(chuàng)建Vue實(shí)例

我們先讓代碼實(shí)現(xiàn)下面的功能:

<body>
<script>
  const vm = new Vue({
    el: '#app',
    data () {
      return {
        age: 18
      };
    }
  });
  // 會(huì)觸發(fā)age屬性對(duì)應(yīng)的set方法
  vm.age = 20;
  // 會(huì)觸發(fā)age屬性對(duì)應(yīng)的get方法
  console.log(vm.age);
</script>
</body>

src/index.js中,定義Vue的構(gòu)造函數(shù)。用戶用到的Vue就是在這里導(dǎo)出的Vue:

import initMixin from './init';

function Vue (options) {
  this._init(options);
}

// 進(jìn)行原型方法擴(kuò)展
initMixin(Vue);
export default Vue;

init中,會(huì)定義原型上的_init方法,并進(jìn)行狀態(tài)的初始化:

import initState from './state';

function initMixin (Vue) {
  Vue.prototype._init = function (options) {
    const vm = this;
    // 將用戶傳入的選項(xiàng)放到vm.$options上,之后可以很方便的通過實(shí)例vm來訪問所有實(shí)例化時(shí)傳入的選項(xiàng)
    vm.$options = options;
    initState(vm);
  };
}
export default initMixin;

_init方法中,所有的options被放到了vm.$options中,這不僅讓之后代碼中可以更方便的來獲取用戶傳入的配置項(xiàng),也可以讓用戶通過這個(gè)api來獲取實(shí)例化時(shí)傳入的一些自定義選選項(xiàng)。比如在Vuex 和Vue-Router中,實(shí)例化時(shí)傳入的routerstore屬性便可以通過$options獲取到。

除了設(shè)置vm.$options,_init中還執(zhí)行了initState方法。該方法中會(huì)判斷選項(xiàng)中傳入的屬性,來分別進(jìn)行props、methodsdata、watchcomputed 等配置項(xiàng)的初始化操作,這里我們主要處理data選項(xiàng):

import { observe } from './observer';
import { proxy } from './shared/utils';

function initState (vm) {
  const options = vm.$options;
  if (options.props) {
    initProps(vm);
  }
  if (options.methods) {
    initMethods(vm);
  }
  if (options.data) {
    initData(vm);
  }
  if (options.computed) {
    initComputed(vm)
  }
  if (options.watch) {
    initWatch(vm)
  }
}

function initData (vm) {
  let data = vm.$options.data;
  vm._data = data = typeof data === 'function' ? data.call(vm) : data;
  // 對(duì)data中的數(shù)據(jù)進(jìn)行攔截
  observe(data);
  // 將data中的屬性代理到vm上
  for (const key in data) {
    if (data.hasOwnProperty(key)) {
      // 為vm代理所有data中的屬性,可以直接通過vm.xxx來進(jìn)行獲取
      proxy(vm, key, data);
    }
  }
}
export default initState;

initData中進(jìn)行了如下操作:

  • data可能是對(duì)象或函數(shù),這里將data統(tǒng)一處理為對(duì)象
  • 觀測data中的數(shù)據(jù),為所有對(duì)象屬性添加set/get方法,重寫數(shù)組的原型鏈方法
  • data中的屬性代理到vm上,方便用戶直接通過實(shí)例vm來訪問對(duì)應(yīng)的值,而不是通過vm._data來訪問

新建src/observer/index.js,在這里書寫observe函數(shù)的邏輯:

function observe (data) {
  // 如果是對(duì)象,會(huì)遍歷對(duì)象中的每一個(gè)元素
  if (typeof data === 'object' && data !== null) {
    // 已經(jīng)觀測過的值不再處理
    if (data.__ob__) {
      return;
    }
    new Observer(data);
  }
}
export { observe };

observe函數(shù)中會(huì)過濾data中的數(shù)據(jù),只對(duì)對(duì)象和數(shù)組進(jìn)行處理,真正的處理邏輯在Observer中:

/**
 * 為data中的所有對(duì)象設(shè)置`set/get`方法
 */
class Observer {
  constructor (value) {
    this.value = value;
    // 為data中的每一個(gè)對(duì)象和數(shù)組都添加__ob__屬性,方便直接可以通過data中的屬性來直接調(diào)用Observer實(shí)例上的屬性和方法
    defineProperty(this.value, '__ob__', this);
    // 這里會(huì)對(duì)數(shù)組和對(duì)象進(jìn)行單獨(dú)處理,因?yàn)闉閿?shù)組中的每一個(gè)索引都設(shè)置get/set方法性能消耗比較大
    if (Array.isArray(value)) {
      Object.setPrototypeOf(value, arrayProtoCopy);
      this.observeArray(value);
    } else {
      this.walk();
    }
  }

  walk () {
    for (const key in this.value) {
      if (this.value.hasOwnProperty(key)) {
        defineReactive(this.value, key);
      }
    }
  }

  observeArray (value) {
    for (let i = 0; i < value.length; i++) {
      observe(value[i]);
    }
  }
}

需要注意的是,__ob__屬性要設(shè)置為不可枚舉,否則之后在對(duì)象遍歷時(shí)可能會(huì)引發(fā)死循環(huán)

Observer類中會(huì)為對(duì)象和數(shù)組都添加__ob__屬性,之后便可以直接通過data中的對(duì)象和數(shù)組vm.value.__ob__來獲取到Observer實(shí)例。

當(dāng)傳入的value為數(shù)組時(shí),由于觀測數(shù)組的每一個(gè)索引會(huì)耗費(fèi)比較大的性能,并且在實(shí)際使用中,我們可能只會(huì)操作數(shù)組的第一項(xiàng)和最后一項(xiàng),即arr[0],arr[arr.length-1],很少會(huì)寫出arr[23] = xxx的代碼。

所以我們選擇對(duì)數(shù)組的方法進(jìn)行重寫,將數(shù)組的原型指向繼承Array.prototype新創(chuàng)建的對(duì)象arrayProtoCopy,對(duì)數(shù)組中的每一項(xiàng)繼續(xù)進(jìn)行觀測。

創(chuàng)建data中數(shù)組原型的邏輯在src/observer/array.js中:

// if (Array.isArray(value)) {
//    Object.setPrototypeOf(value, arrayProtoCopy);
//    this.observeArray();
// }
const arrayProto = Array.prototype;
export const arrayProtoCopy = Object.create(arrayProto);

const methods = ['push', 'pop', 'unshift', 'shift', 'splice', 'reverse', 'sort'];

methods.forEach(method => {
  arrayProtoCopy[method] = function (...args) {
    const result = arrayProto[method].apply(this, args);
    console.log('change array value');
    // data中的數(shù)組會(huì)調(diào)用這里定義的方法,this指向該數(shù)組
    const ob = this.__ob__;
    let inserted;
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args;
        break;
      case 'splice': // splice(index,deleteCount,item1,item2)
        inserted = args.slice(2);
        break;
    }
    if (inserted) {ob.observeArray(inserted);}
    return result;
  };
});

通過Object.create方法,可以創(chuàng)建一個(gè)原型為Array.prototype的新對(duì)象arrayProtoCopy。修改原數(shù)組的7個(gè)方法會(huì)設(shè)置為新對(duì)象的私有屬性,并且在執(zhí)行時(shí)會(huì)調(diào)用arrayProto 上對(duì)應(yīng)的方法。

在這樣處理之后,便可以在arrayProto中的方法執(zhí)行前后添加自己的邏輯,而除了這7個(gè)方法外的其它方法,會(huì)根據(jù)原型鏈,使用arrayProto上的對(duì)應(yīng)方法,并不會(huì)有任何額外的處理。

在修改原數(shù)組的方法中,添加了如下的額外邏輯:

const ob = this.__ob__;
let inserted;
switch (method) {
  case 'push':
  case 'unshift':
    inserted = args;
    break;
  case 'splice': // splice(index,deleteCount,item1,item2)
    inserted = args.slice(2);
    break;
}
if (inserted) {ob.observeArray(inserted);}

push、unshift、splice會(huì)為數(shù)組新增元素,對(duì)于新增的元素,也要對(duì)其進(jìn)行觀測。這里利用到了Observer中為數(shù)組添加的__ob__屬性,來直接調(diào)用ob.observeArray ,對(duì)數(shù)組中新增的元素繼續(xù)進(jìn)行觀測。

對(duì)于對(duì)象,要遍歷對(duì)象的每一個(gè)屬性,來為其添加set/get方法。如果對(duì)象的屬性依舊是對(duì)象,會(huì)對(duì)其進(jìn)行遞歸處理

function defineReactive (target, key) {
  let value = target[key];
  // 繼續(xù)對(duì)value進(jìn)行監(jiān)聽,如果value還是對(duì)象的話,會(huì)繼續(xù)new Observer,執(zhí)行defineProperty來為其設(shè)置get/set方法
  // 否則會(huì)在observe方法中什么都不做
  observe(value);
  Object.defineProperty(target, key, {
    get () {
      console.log('get value');
      return value;
    },
    set (newValue) {
      if (newValue !== value) {
        // 新加的元素也可能是對(duì)象,繼續(xù)為新加對(duì)象的屬性設(shè)置get/set方法
        observe(newValue);
        // 這樣寫會(huì)新將value指向一個(gè)新的值,而不會(huì)影響target[key]
        console.log('set value');
        value = newValue;
      }
    }
  });
}

class Observer {
  constructor (value) {
    // some code ...
    if (Array.isArray(value)) {
      // some code ...
    } else {
      this.walk();
    }
  }

  walk () {
    for (const key in this.value) {
      if (this.value.hasOwnProperty(key)) {
        defineReactive(this.value, key);
      }
    }
  }

  // some code ...  
}

數(shù)據(jù)觀測存在的問題

檢測變化的注意事項(xiàng)

我們先創(chuàng)建一個(gè)簡單的例子:

const mv = new Vue({
  data () {
    return {
      arr: [1, 2, 3],
      person: {
        name: 'zs',
        age: 20
      }
    }
  }
})

對(duì)于對(duì)象,我們只是攔截了它的取值和賦值操作,添加值和刪除值并不會(huì)進(jìn)行攔截:

vm.person.school = '北大'
delete vm.person.age

而對(duì)于數(shù)組,用索引修改值以及修改數(shù)組長度不會(huì)被觀測到:

vm.arr[0] = 0
vm.arr.length--

為了能處理上述的情況,Vue為用戶提供了$set$delete方法:

  • $set: 為響應(yīng)式對(duì)象添加一個(gè)屬性,確保新屬性也是響應(yīng)式的,因此會(huì)觸發(fā)視圖更新
  • $delete: 刪除對(duì)象上的一個(gè)屬性。如果對(duì)象是響應(yīng)式的,確保刪除觸發(fā)視圖更新。

結(jié)語

通過實(shí)現(xiàn)Vue的數(shù)據(jù)劫持,將會(huì)對(duì)Vue的數(shù)據(jù)初始化和響應(yīng)式有更深的認(rèn)識(shí)。

在工作中,我們可能總是會(huì)疑惑,為什么我更新了值,但是頁面沒有發(fā)生變化?現(xiàn)在我們可以從源碼的角度進(jìn)行理解,從而更清楚的知道代碼中存在的問題以及如何解決和避免這些問題。

源代碼: 傳送門

到此這篇關(guān)于Vue數(shù)據(jù)劫持詳情介紹的文章就介紹到這了,更多相關(guān)Vue數(shù)據(jù)劫持內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vue封裝jquery修改自身及兄弟元素的方法

    vue封裝jquery修改自身及兄弟元素的方法

    本文主要介紹了vue封裝jquery修改自身及兄弟元素的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • Vue3使用Vuex之mapState與mapGetters詳解

    Vue3使用Vuex之mapState與mapGetters詳解

    這篇文章主要為大家介紹了Vue3使用Vuex之mapState與mapGetters詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03
  • Vue將el-table導(dǎo)出為excel文件的實(shí)現(xiàn)方法

    Vue將el-table導(dǎo)出為excel文件的實(shí)現(xiàn)方法

    在 Vue + Element UI 中,el-table 數(shù)據(jù)導(dǎo)出 Excel 文件,可以使用 xlsx(SheetJS)庫進(jìn)行處理,以下是詳細(xì)的實(shí)現(xiàn)方法,包括安裝依賴、代碼示例和優(yōu)化建議,需要的朋友可以參考下
    2025-02-02
  • Vue打包部署到Nginx時(shí),css樣式不生效的解決方式

    Vue打包部署到Nginx時(shí),css樣式不生效的解決方式

    這篇文章主要介紹了Vue打包部署到Nginx時(shí),css樣式不生效的解決方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • vue實(shí)現(xiàn)員工信息錄入功能

    vue實(shí)現(xiàn)員工信息錄入功能

    這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)員工信息錄入功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-06-06
  • vsCode中vue文件無法提示html標(biāo)簽的操作方法

    vsCode中vue文件無法提示html標(biāo)簽的操作方法

    在vsCode中書寫Vue頁面時(shí)無法提示,那真是很郁悶的事情,下面這篇文章主要給大家介紹了關(guān)于vsCode中vue文件無法提示html標(biāo)簽的操作方法,需要的朋友可以參考下
    2023-03-03
  • Vue前端路由hash與history差異深入了解

    Vue前端路由hash與history差異深入了解

    這篇文章主要為大家介紹了Vue前端路由hash與history差異的深入了解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • nginx部署多個(gè)vue項(xiàng)目的方法示例

    nginx部署多個(gè)vue項(xiàng)目的方法示例

    這篇文章主要介紹了nginx部署多個(gè)vue項(xiàng)目的方法示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Vue Transition實(shí)現(xiàn)類原生組件跳轉(zhuǎn)過渡動(dòng)畫的示例

    Vue Transition實(shí)現(xiàn)類原生組件跳轉(zhuǎn)過渡動(dòng)畫的示例

    本篇文章主要介紹了Vue Transition實(shí)現(xiàn)類原生組件跳轉(zhuǎn)過渡動(dòng)畫的示例,具有一定的參考價(jià)值,有興趣的可以了解一下
    2017-08-08
  • vue中node_modules中第三方模塊的修改使用詳解

    vue中node_modules中第三方模塊的修改使用詳解

    這篇文章主要介紹了vue中node_modules中第三方模塊的修改使用,本文圖文并茂給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-05-05

最新評(píng)論

会昌县| 宽甸| 依安县| 山东省| 德化县| 徐闻县| 镇安县| 蒲江县| 上饶县| 陕西省| 昌吉市| 富民县| 金溪县| 尤溪县| 宁化县| 河间市| 资阳市| 静安区| 张家川| 安溪县| 邵东县| 扬州市| 鹤庆县| 湖口县| 大田县| 西峡县| 柳江县| 健康| 宜兴市| 桂阳县| 泰和县| 崇礼县| 兴宁市| 阿坝县| 遂昌县| 天津市| 铜鼓县| 长垣县| 夏津县| 惠州市| 正宁县|