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

從vue源碼解析Vue.set()和this.$set()

 更新時(shí)間:2018年08月30日 15:27:49   作者:你假裝沒(méi)察覺(jué)  
這篇文章主要介紹了從vue源碼看Vue.set()和this.$set()的相關(guān)知識(shí),我們先來(lái)從Vue提供的Vue.set()和this.$set()這兩個(gè)api看看它內(nèi)部是怎么實(shí)現(xiàn)的。感興趣的朋友跟隨小編一起看看吧

前言

最近死磕了一段時(shí)間vue源碼,想想覺(jué)得還是要輸出點(diǎn)東西,我們先來(lái)從Vue提供的Vue.set()和this.$set()這兩個(gè)api看看它內(nèi)部是怎么實(shí)現(xiàn)的。

Vue.set()和this.$set()應(yīng)用的場(chǎng)景

平時(shí)做項(xiàng)目的時(shí)候難免不會(huì)對(duì) 數(shù)組或者對(duì)象 進(jìn)行這樣的騷操作操作,結(jié)果發(fā)現(xiàn),咦~~,他喵的,怎么頁(yè)面沒(méi)有重新渲染。

const vueInstance = new Vue({
 data: {
  arr: [1, 2],
  obj1: {
    a: 3
  }
 }
});
vueInstance.$data.arr[0] = 3; // 這種騷操作頁(yè)面不會(huì)重新渲染
vueInstance.$data.obj1.b = 3; // 這種騷操作頁(yè)面不會(huì)重新渲染

查了一下官方文檔,發(fā)現(xiàn)人家早就說(shuō)過(guò)這種情況

Vue.set()向響應(yīng)式對(duì)象中添加一個(gè)屬性,并確保這個(gè)新屬性同樣是響應(yīng)式的,且觸發(fā)視圖更新。它必須用于向響應(yīng)式對(duì)象上添加新屬性,因?yàn)?Vue 無(wú)法探測(cè)普通的新增屬性 (比如 this.myObject.newProperty = 'hi')

所以按照官網(wǎng)的寫法,我們應(yīng)該使用下面這種寫法:

Vue.set(vueInstance.$data.arr, 0, 3); // 這樣操作數(shù)組可以讓頁(yè)面重新渲染
vueInstance.$set(vueInstance.$data.arr, 0, 3); // 這樣操作數(shù)組也可以讓頁(yè)面重新渲染
Vue.set(vueInstance.$data.obj1, b, 3); // 這樣操作對(duì)象可以讓頁(yè)面重新渲染
vueInstance.$set(vueInstance.$data.obj1, b, 3); // 這樣操作對(duì)象也可以讓頁(yè)面重新渲染

Vue.set()和this.$set()實(shí)現(xiàn)原理

是時(shí)候看一波這兩個(gè)api的源碼了,我們先來(lái)看看Vue.set()的源碼:

import { set } from '../observer/index'
...
Vue.set = set
...

再來(lái)看看this.$set()的源碼:

import { set } from '../observer/index'
...
Vue.prototype.$set = set
...

結(jié)果我們發(fā)現(xiàn)Vue.set()和this.$set()這兩個(gè)api的實(shí)現(xiàn)原理基本一模一樣,都是使用了set函數(shù)。set函數(shù)是從 ../observer/index 文件中導(dǎo)出的,區(qū)別在于Vue.set()是將set函數(shù)綁定在Vue構(gòu)造函數(shù)上,this.$set()是將set函數(shù)綁定在Vue原型上。

接下來(lái)我們根據(jù) ../observer/index 中找出set函數(shù):

function set (target: Array<any> | Object, key: any, val: any): any {
 if (process.env.NODE_ENV !== 'production' &&
  (isUndef(target) || isPrimitive(target))
 ) {
  warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
 }
 if (Array.isArray(target) && isValidArrayIndex(key)) {
  target.length = Math.max(target.length, key)
  target.splice(key, 1, val)
  return val
 }
 if (key in target && !(key in Object.prototype)) {
  target[key] = val
  return val
 }
 const ob = (target: any).__ob__
 if (target._isVue || (ob && ob.vmCount)) {
  process.env.NODE_ENV !== 'production' && warn(
   'Avoid adding reactive properties to a Vue instance or its root $data ' +
   'at runtime - declare it upfront in the data option.'
  )
  return val
 }
 if (!ob) {
  target[key] = val
  return val
 }
 defineReactive(ob.value, key, val)
 ob.dep.notify()
 return val
}

我們發(fā)現(xiàn)set函數(shù)接收三個(gè)參數(shù)分別為 target、key、val,其中target的值為數(shù)組或者對(duì)象,這正好和官網(wǎng)給出的調(diào)用Vue.set()方法時(shí)傳入的參數(shù)參數(shù)對(duì)應(yīng)上。如下圖所示:

我們接著往下看:

if (process.env.NODE_ENV !== 'production' &&
  (isUndef(target) || isPrimitive(target))
 ) {
  warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
 }

我們先看isUndef和isPrimitive方法,從名字就可以看出,isUndef是判斷target是不是等于undefined或者null。isPrimitive是判斷target的數(shù)據(jù)類型是不是string、number、symbol、boolean中的一種。所以這里的意思是如果當(dāng)前環(huán)境不是生產(chǎn)環(huán)境并且 isUndef(target) || isPrimitive(target) 為真的時(shí)候,那么就拋出錯(cuò)誤警告。

數(shù)組的實(shí)現(xiàn)原理

接著向下看:

if (Array.isArray(target) && isValidArrayIndex(key)) {
  target.length = Math.max(target.length, key)
  target.splice(key, 1, val)
  return val
 }

這里實(shí)際就是修改數(shù)組時(shí)調(diào)用set方法時(shí)讓我們能夠觸發(fā)響應(yīng)的代碼,不過(guò)在分析這段代碼之前我們來(lái)看看vue中的數(shù)組實(shí)際上是長(zhǎng)什么樣的。 下圖分別是vue中的數(shù)組和普通的js數(shù)組:

vue中的數(shù)組我們命名為arrVue,js中的普通數(shù)組命名為arrJs。其實(shí)我們平時(shí)調(diào)用普通數(shù)組的push、pop等方法是調(diào)用的Array原型上面定義的方法, 從圖中我們可以看出arrJs的原型是指向Array.prototype,也就是說(shuō) arrJs.__proto__ == Array.prototype 。

但是在vue的數(shù)組中,我們發(fā)現(xiàn)arrVue的原型其實(shí)不是指向的Array.prototype,而是指向的一個(gè)對(duì)象(我們這里給這個(gè)對(duì)象命名為arrayMethods)。arrayMethods上面只有7個(gè)push、pop等方法,并且arrayMethods的原型才是指向的Array.prototype。所以我們?cè)趘ue中調(diào)用數(shù)組的push、pop等方法時(shí)其實(shí)不是直接調(diào)用的數(shù)組原型給我們提供的push、pop等方法,而是調(diào)用的arrayMethods給我們提供的push、pop等方法。vue為什么要給數(shù)組的原型鏈上面加上這個(gè)arrayMethods呢?這里涉及到了vue的數(shù)據(jù)響應(yīng)的原理,我們這篇文章暫時(shí)不談?wù)摂?shù)據(jù)響應(yīng)原理的具體實(shí)現(xiàn)。這里你可以理解成vue在arrayMethods對(duì)象中做過(guò)了特殊處理,如果你調(diào)用了arrayMethods提供的push、pop等7個(gè)方法,那么它會(huì)觸發(fā)當(dāng)前收集的依賴(這里收集的依賴可以暫時(shí)理解成渲染函數(shù)),導(dǎo)致頁(yè)面重新渲染。換句話說(shuō),對(duì)于數(shù)組的操作,我們只有使用arrayMethods提供的那7個(gè)方法才會(huì)導(dǎo)致頁(yè)面渲染,這也就解釋了為什么我們使用 vueInstance.$data.arr[0] = 3; 時(shí)不會(huì)導(dǎo)致頁(yè)面出現(xiàn)渲染。

搞清楚vue中的數(shù)組具體是怎么實(shí)現(xiàn)了之后,我們?cè)賮?lái)看上面的代碼:

if (Array.isArray(target) && isValidArrayIndex(key)) {
  target.length = Math.max(target.length, key)
  target.splice(key, 1, val)
  return val
 }

首先if判斷當(dāng)前target是不是數(shù)組,并且key的值是有效的數(shù)組索引。然后將target數(shù)組的長(zhǎng)度設(shè)置為target.length和key中的最大值,這里為什么要這樣做呢?是因?yàn)槲覀兛赡軙?huì)進(jìn)行下面這種騷操作:

arr1 = [1,3];
Vue.set(arr1,10,1) // 如果不那樣做,這種情況就會(huì)出問(wèn)題

接著向下看,我們發(fā)現(xiàn)這里直接調(diào)用了target.splice(key, 1, val),在前面我們說(shuō)過(guò)調(diào)用arrayMethods提供的push、pop等7個(gè)方法可以導(dǎo)致頁(yè)面重新渲染,剛好splice也是屬性arrayMethods提供的7個(gè)方法中的一種。

總結(jié)一下Vue.set數(shù)組實(shí)現(xiàn)的原理:其實(shí)Vue.set()對(duì)于數(shù)組的處理其實(shí)就是調(diào)用了splice方法,是不是發(fā)現(xiàn)其實(shí)很簡(jiǎn)單~~

對(duì)象的實(shí)現(xiàn)原理

我們接著向下看代碼:

if (key in target && !(key in Object.prototype)) {
  target[key] = val
  return val
 }

這里先判斷如果key本來(lái)就是對(duì)象中的一個(gè)屬性,并且key不是Object原型上的屬性。說(shuō)明這個(gè)key本來(lái)就在對(duì)象上面已經(jīng)定義過(guò)了的,直接修改值就可以了,可以自動(dòng)觸發(fā)響應(yīng)。

關(guān)于對(duì)象的依賴收集和觸發(fā)原理我們本文也不做詳細(xì)解釋,你可以暫時(shí)先這樣理解。vue是使用的Object.defineProperty給對(duì)象做了一層攔截,當(dāng)觸發(fā)get的時(shí)候就會(huì)進(jìn)行依賴收集(這里收集的依賴還是像數(shù)組那樣,理解成渲染函數(shù)),當(dāng)觸發(fā)set的時(shí)候就會(huì)觸發(fā)依賴,導(dǎo)致渲染函數(shù)執(zhí)行頁(yè)面重新渲染。那么第一次是在哪里觸發(fā)get的呢?其實(shí)是在首次加載頁(yè)面渲染的時(shí)候觸發(fā)的,這里會(huì)進(jìn)行遞歸將對(duì)象的屬性都依賴收集,所以我們修改對(duì)象已有屬性值得時(shí)候會(huì)導(dǎo)致頁(yè)面重新渲染。這也剛好解釋了我們使用 vueInstance.$data.obj1.b = 3; 的時(shí)候?yàn)槭裁错?yè)面不會(huì)重新渲染,因?yàn)檫@里的屬性b不是對(duì)象的已有屬性,也就是說(shuō)屬性b沒(méi)有進(jìn)行過(guò)依賴收集,所以才會(huì)導(dǎo)致修改屬性b的值頁(yè)面不會(huì)重新渲染。

我們接著向下看代碼:

const ob = (target: any).__ob__
 if (target._isVue || (ob && ob.vmCount)) {
  process.env.NODE_ENV !== 'production' && warn(
   'Avoid adding reactive properties to a Vue instance or its root $data ' +
   'at runtime - declare it upfront in the data option.'
  )
  return val
 }
 if (!ob) {
  target[key] = val
  return val
 }

首先定義變量ob的值為 target.__ob__ ,這個(gè) __ob__ 屬性到底是什么對(duì)象呢?vue給響應(yīng)式對(duì)象都加了一個(gè) __ob__ 屬性,如果一個(gè)對(duì)象有這個(gè) __ob__ 屬性,那么就說(shuō)明這個(gè)對(duì)象是響應(yīng)式對(duì)象,我們修改對(duì)象已有屬性的時(shí)候就會(huì)觸發(fā)頁(yè)面渲染。

target._isVue || (ob && ob.vmCount) 的意思是:當(dāng)前的target對(duì)象是vue實(shí)例對(duì)象或者是根數(shù)據(jù)對(duì)象,那么就會(huì)拋出錯(cuò)誤警告。

if (!ob) 為真說(shuō)明當(dāng)前的target對(duì)象不是響應(yīng)式對(duì)象,那么直接賦值返回即可。

接著向下看:

defineReactive(ob.value, key, val)
 ob.dep.notify()
 return val

這里其實(shí)才是vue.set()真正處理對(duì)象的地方。 defineReactive(ob.value, key, val) 的意思是給新加的屬性添加依賴,以后再直接修改這個(gè)新的屬性的時(shí)候就會(huì)觸發(fā)頁(yè)面渲染。

ob.dep.notify() 這句代碼的意思是觸發(fā)當(dāng)前的依賴(這里的依賴依然可以理解成渲染函數(shù)),所以頁(yè)面就會(huì)進(jìn)行重新渲染。

總結(jié)

從源碼層次看vue提供的vue.set()和this.$set()這兩個(gè)api還是很簡(jiǎn)單的,由于本文沒(méi)有詳細(xì)解釋vue依賴收集和觸發(fā),所以有的地方說(shuō)的還是很模糊。

以上所述是小編給大家介紹的vue源碼解析Vue.set()和this.$set(),希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!

相關(guān)文章

  • Vue2?響應(yīng)式系統(tǒng)之深度響應(yīng)

    Vue2?響應(yīng)式系統(tǒng)之深度響應(yīng)

    這篇文章主要介紹了Vue2?響應(yīng)式系統(tǒng)之深度響應(yīng),文章基于Vue2?響應(yīng)式系統(tǒng)的相關(guān)資料展開(kāi)對(duì)Vue2?深度響應(yīng)的介紹,需要的小伙伴可以參考一下
    2022-04-04
  • vue?this.$toast?失效問(wèn)題解決方案

    vue?this.$toast?失效問(wèn)題解決方案

    這篇文章主要介紹了vue?this.$toast?失效問(wèn)題匯總,本文給大家分享完美解決方案,感興趣的朋友跟隨小編一起看看吧
    2024-03-03
  • vue可滑動(dòng)的tab組件使用詳解

    vue可滑動(dòng)的tab組件使用詳解

    這篇文章主要為大家詳細(xì)介紹了vue可滑動(dòng)的tab組件使用,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • Vue3使用全局函數(shù)或變量的2種常用方式代碼

    Vue3使用全局函數(shù)或變量的2種常用方式代碼

    在Vue3項(xiàng)目中需要頻繁使用某一個(gè)方法,配置到全局感覺(jué)會(huì)方便很多,這篇文章主要給大家介紹了關(guān)于Vue3使用全局函數(shù)或變量的2種常用方式,需要的朋友可以參考下
    2023-09-09
  • Vue-CLI與Vuex使用方法實(shí)例分析

    Vue-CLI與Vuex使用方法實(shí)例分析

    這篇文章主要介紹了Vue-CLI與Vuex使用方法,結(jié)合實(shí)例形式分析了Vue-CLI創(chuàng)建項(xiàng)目與Vuex相關(guān)概念、使用方法及操作注意事項(xiàng),需要的朋友可以參考下
    2020-01-01
  • vue中?render?函數(shù)功能與原理分析

    vue中?render?函數(shù)功能與原理分析

    這篇文章主要介紹了vue中?render?函數(shù)功能與原理,結(jié)合實(shí)例形式分析了vue中render函數(shù)的基本功能、原理及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下
    2023-06-06
  • Vue路由實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)的示例代碼

    Vue路由實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)的示例代碼

    根據(jù)登錄的角色不一樣,實(shí)現(xiàn)不同的路由展示,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-04-04
  • 解決vue3+vite配置unplugin-vue-component找不到Vant組件

    解決vue3+vite配置unplugin-vue-component找不到Vant組件

    這篇文章主要為大家介紹了vue3+vite配置unplugin-vue-component找不到Vant組件問(wèn)題解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • vue.js前端網(wǎng)頁(yè)彈框異步行為示例分析

    vue.js前端網(wǎng)頁(yè)彈框異步行為示例分析

    這篇文章主要為大家介紹了vue.js前端網(wǎng)頁(yè)彈框異步的行為示例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助祝大家多多進(jìn)步,早日升職加薪
    2021-11-11
  • Vue 3集成??礧eb插件實(shí)現(xiàn)視頻監(jiān)控功能

    Vue 3集成??礧eb插件實(shí)現(xiàn)視頻監(jiān)控功能

    本文詳細(xì)介紹了如何使用 Vue 3 框架集成??礧eb插件實(shí)現(xiàn)視頻監(jiān)控功能,通過(guò)定義屬性、事件、變量,以及編寫初始化、播放視頻、處理節(jié)點(diǎn)點(diǎn)擊事件等方法,我們成功實(shí)現(xiàn)了視頻監(jiān)控系統(tǒng)的前端部分,感興趣的朋友一起看看吧
    2024-11-11

最新評(píng)論

开封市| 池州市| 惠水县| 延寿县| 茂名市| 永仁县| 贵溪市| 灵宝市| 山西省| 株洲市| 中阳县| 曲阜市| 手游| 阿拉善右旗| 册亨县| 泾源县| 襄城县| 田阳县| 富顺县| 东至县| 莆田市| 金乡县| 离岛区| 辉南县| 岑溪市| 大姚县| 宾阳县| 柳林县| 广饶县| 荆州市| 海盐县| 镇宁| 右玉县| 古蔺县| 白沙| 蓬溪县| 略阳县| 武乡县| 汝城县| 贵定县| 长白|