日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

Vue為什么要謹(jǐn)慎使用$attrs與$listeners

瀏覽:252日期:2022-12-03 13:56:29

前言

在 Vue 開(kāi)發(fā)過(guò)程中,如遇到祖先組件需要傳值到孫子組件時(shí),需要在兒子組件接收 props ,然后再傳遞給孫子組件,通過(guò)使用 v-bind='$attrs' 則會(huì)帶來(lái)極大的便利,但同時(shí)也會(huì)有一些隱患在其中。

隱患

先來(lái)看一個(gè)例子:

Vue為什么要謹(jǐn)慎使用$attrs與$listeners

父組件:

{ template: ` <div> <input type='text' v-model='input' placeholder='please input'> <test :test='test' /> </div> `, data() { return { input: ’’, test: ’1111’, }; },}

子組件:

{ template: ’<div v-bind='$attrs'></div>’, updated() { console.log(’Why should I update?’); },}

可以看到,當(dāng)我們?cè)谳斎肟蜉斎胫档臅r(shí)候,只有修改到 input 字段,從而更新父組件,而子組件的 props test 則是沒(méi)有修改的,按照 誰(shuí)更新,更新誰(shuí) 的標(biāo)準(zhǔn)來(lái)看,子組件是不應(yīng)該更新觸發(fā) updated 方法的,那這是為什么呢?

于是我發(fā)現(xiàn)這個(gè)“bug”,并迅速打開(kāi) gayhub 提了個(gè) issue ,想著我也是參與過(guò)重大開(kāi)源項(xiàng)目的人了,還不免一陣竊喜。事實(shí)很殘酷,這么明顯的問(wèn)題怎么可能還沒(méi)被發(fā)現(xiàn)...

Vue為什么要謹(jǐn)慎使用$attrs與$listeners

無(wú)情……,于是我打開(kāi)看了看,尤大說(shuō)了這么一番話我就好像明白了:

Vue為什么要謹(jǐn)慎使用$attrs與$listeners

那既然不是“bug”,那來(lái)看看是為什么吧。

前因

首先介紹一個(gè)前提,就是 Vue 在更新組件的時(shí)候是更新對(duì)應(yīng)的 data 和 props 觸發(fā) Watcher 通知來(lái)更新渲染的。

每一個(gè)組件都有一個(gè)唯一對(duì)應(yīng)的 Watcher ,所以在子組件上的 props 沒(méi)有更新的時(shí)候,是不會(huì)觸發(fā)子組件的更新的。當(dāng)我們?nèi)サ糇咏M件上的 v-bind='$attrs' 時(shí)可以發(fā)現(xiàn), updated 鉤子不會(huì)再執(zhí)行,所以可以發(fā)現(xiàn)問(wèn)題就出現(xiàn)在這里。

原因分析

Vue 源碼中搜索 $attrs ,找到 src/core/instance/render.js 文件:

export function initRender (vm: Component) { // ... defineReactive(vm, ’$attrs’, parentData && parentData.attrs || emptyObject, null, true) defineReactive(vm, ’$listeners’, options._parentListeners || emptyObject, null, true)}

噢,amazing!就是它。可以看到在 initRender 方法中,將 $attrs 屬性綁定到了 this 上,并且設(shè)置成響應(yīng)式對(duì)象,離發(fā)現(xiàn)奧秘又近了一步。

依賴收集

我們知道 Vue 會(huì)通過(guò) Object.defineProperty 方法來(lái)進(jìn)行依賴收集,由于這部分內(nèi)容也比較多,這里只進(jìn)行一個(gè)簡(jiǎn)單了解。

Object.defineProperty(obj, key, { get: function reactiveGetter () { const value = getter ? getter.call(obj) : val if (Dep.target) { dep.depend() // 依賴收集 -- Dep.target.addDep(dep) if (childOb) { childOb.dep.depend() if (Array.isArray(value)) { dependArray(value) } } } return value } })

通過(guò)對(duì) get 的劫持,使得我們?cè)谠L問(wèn) $attrs 時(shí)它( dep )會(huì)將 $attrs 所在的 Watcher 收集到 dep 的 subs 里面,從而在設(shè)置時(shí)進(jìn)行派發(fā)更新( notify() ),通知視圖渲染。

派發(fā)更新

下面是在改變響應(yīng)式數(shù)據(jù)時(shí)派發(fā)更新的核心邏輯:

Object.defineProperty(obj, key, { set: function reactiveSetter (newVal) { const value = getter ? getter.call(obj) : val /* eslint-disable no-self-compare */ if (newVal === value || (newVal !== newVal && value !== value)) { return } /* eslint-enable no-self-compare */ if (process.env.NODE_ENV !== ’production’ && customSetter) { customSetter() } if (setter) { setter.call(obj, newVal) } else { val = newVal } childOb = !shallow && observe(newVal) dep.notify() } })

很簡(jiǎn)單的一部分代碼,就是在響應(yīng)式數(shù)據(jù)被 set 時(shí),調(diào)用 dep 的 notify 方法,遍歷每一個(gè) Watcher 進(jìn)行更新。

notify () { // stabilize the subscriber list first const subs = this.subs.slice() for (let i = 0, l = subs.length; i < l; i++) { subs[i].update() } }

了解到這些基礎(chǔ)后,我們?cè)倩仡^看看 $attrs 是如何觸發(fā)子組件的 updated 方法的。

要知道子組件會(huì)被更新,肯定是在某個(gè)地方訪問(wèn)到了 $attrs ,依賴被收集到 subs 里了,才會(huì)在派發(fā)時(shí)被通知需要更新。我們對(duì)比添加 v-bind='$attrs' 和不添加 v-bind='$attrs' 調(diào)試一下源碼可以看到:

get: function reactiveGetter () { var value = getter ? getter.call(obj) : val; if (Dep.target) { dep.depend(); if (childOb) { childOb.dep.depend(); if (Array.isArray(value)) { dependArray(value); } } } var a = dep; // 看看當(dāng)前 dep 是啥 debugger; // debugger 斷點(diǎn) return value }

當(dāng)綁定了 v-bind='$attrs' 時(shí),會(huì)多收集到一個(gè)依賴。

Vue為什么要謹(jǐn)慎使用$attrs與$listeners

會(huì)有一個(gè) id 為 8 的 dep 里面收集了 $attrs 所在的 Watcher ,我們?cè)賹?duì)比一下有無(wú) v-bind='$attrs' 時(shí)的 set

派發(fā)更新?tīng)顟B(tài):

set: function reactiveSetter (newVal) { var value = getter ? getter.call(obj) : val; /* eslint-disable no-self-compare */ if (newVal === value || (newVal !== newVal && value !== value)) { return } /* eslint-enable no-self-compare */ if (process.env.NODE_ENV !== ’production’ && customSetter) { customSetter(); } if (setter) { setter.call(obj, newVal); } else { val = newVal; } childOb = !shallow && observe(newVal); var a = dep; // 查看當(dāng)前 dep debugger; // debugger 斷點(diǎn) dep.notify(); }

Vue為什么要謹(jǐn)慎使用$attrs與$listeners

這里可以明顯看到也是 id 為 8 的 dep 正準(zhǔn)備遍歷 subs 通知 Watcher 來(lái)更新,也能看到 newVal 與 value

其實(shí)值并沒(méi)有改變而進(jìn)行了更新這個(gè)問(wèn)題。

問(wèn)題:$attrs 的依賴是如何被收集的呢?

我們知道依賴收集是在 get 中完成的,但是我們初始化的時(shí)候并沒(méi)有訪問(wèn)數(shù)據(jù),那這是怎么實(shí)現(xiàn)的呢?

答案就在 vm._render() 這個(gè)方法會(huì)生成 Vnode 并在這個(gè)過(guò)程中會(huì)訪問(wèn)到數(shù)據(jù),從而收集到了依賴。

那還是沒(méi)有解答出這個(gè)問(wèn)題呀,別急,這還是一個(gè)鋪墊,因?yàn)槟阍?vm._render() 里也找不到在哪訪問(wèn)到了 $attrs ...

柳暗花明

我們的代碼里和 vm._render() 都沒(méi)有對(duì) $attrs 訪問(wèn),原因只可能出現(xiàn)在 v-bind 上了,我們使用 vue-template-compiler 對(duì)模板進(jìn)行編譯看看:

const compiler = require(’vue-template-compiler’);const result = compiler.compile( // ` // <div :test='test'> // <p>測(cè)試內(nèi)容</p> // </div> // ` ` <div v-bind='$attrs'> <p>測(cè)試內(nèi)容</p> </div>`);console.log(result.render);// with (this) {// return _c(// ’div’,// { attrs: { test: test } },// [// _c(’p’, [_v(’測(cè)試內(nèi)容’)])// ]// );// }// with (this) {// return _c(// ’div’,// _b({}, ’div’, $attrs, false),// [// _c(’p’, [_v(’測(cè)試內(nèi)容’)])// ]// );// }

這就是最終訪問(wèn) $attrs 的地方了,所以 $attrs 會(huì)被收集到依賴中,當(dāng) input 中 v-model 的值更新時(shí),觸發(fā) set 通知更新,而在更新組件時(shí)調(diào)用的 updateChildComponent 方法中會(huì)對(duì) $attrs 進(jìn)行賦值:

// update $attrs and $listeners hash // these are also reactive so they may trigger child update if the child // used them during render vm.$attrs = parentVnode.data.attrs || emptyObject; vm.$listeners = listeners || emptyObject;

所以會(huì)觸發(fā) $attrs 的 set ,導(dǎo)致它所在的 Watcher 進(jìn)行更新,也就會(huì)導(dǎo)致子組件更新了。而如果沒(méi)有綁定 v-bind='$attrs' ,則雖然也會(huì)到這一步,但是沒(méi)有依賴收集的過(guò)程,就無(wú)法去更新子組件了。

奇淫技巧

如果又想圖人家身子,啊呸,圖人家方便,又想要好點(diǎn)的性能怎么辦呢?這里有一個(gè)曲線救國(guó)的方法:

<template> <Child v-bind='attrsCopy' /></template><script>import _ from ’lodash’;import Child from ’./Child’;export default { name: ’Child’, components: { Child, }, data() { return { attrsCopy: {}, }; }, watch: { $attrs: { handler(newVal, value) { if (!_.isEqual(newVal, value)) { this.attrsCopy = _.cloneDeep(newVal); } }, immediate: true, }, },};</script>

總結(jié)

到此為止,我們就已經(jīng)分析完了 $attrs 數(shù)據(jù)沒(méi)有變化,卻讓子組件更新的原因,源碼中有這樣一段話:

// $attrs & $listeners are exposed for easier HOC creation. // they need to be reactive so that HOCs using them are always updated

一開(kāi)始這樣設(shè)計(jì)目的是為了 HOC 高階組件更好的創(chuàng)建使用,便于 HOC 組件總能對(duì)數(shù)據(jù)變化做出反應(yīng),但是在實(shí)際過(guò)程中與 v-model 產(chǎn)生了一些副作用,對(duì)于這兩者的使用,建議在沒(méi)有數(shù)據(jù)頻繁變化時(shí)可以使用,或者使用上面的奇淫技巧,以及……把產(chǎn)生頻繁變化的部分扔到一個(gè)單獨(dú)的組件中讓他自己自?shī)首詷?lè)去吧。

到此這篇關(guān)于Vue為什么要謹(jǐn)慎使用$attrs與$listeners的文章就介紹到這了,更多相關(guān)Vue $attrs與$listeners內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

標(biāo)簽: Vue
相關(guān)文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
国产66精品| 亚洲欧美久久| 亚洲欧洲av| 亚洲精品三级| 欧美日韩一区二区高清| 免费在线播放第一区高清av| 国产成人在线中文字幕| 亚洲www啪成人一区二区| 欧美成人久久| 日本亚洲不卡| 久久久久久亚洲精品美女| 蜜臀久久精品| 在线看片日韩| 国产精品成人自拍| 国产精品一在线观看| 一区二区三区四区日本视频| 亚洲欧美日韩国产一区| 国产欧美啪啪| 久久国产影院| 亚洲精品乱码日韩| 国产精品igao视频网网址不卡日韩 | 国产一区二区视频在线看| 午夜av成人| 日本aⅴ亚洲精品中文乱码 | 999久久久91| 日韩在线网址| 色在线视频观看| 日本不卡高清视频| 美女视频网站久久| 国产精品99一区二区| 四虎国产精品免费久久| 福利在线一区| 综合色一区二区| 成人在线免费观看网站| 欧美日韩视频| 久久在线91| 免费在线欧美视频| 色网在线免费观看| 综合国产在线| 国产传媒av在线| 日韩欧美久久| 亚洲精品88| 欧美日韩午夜电影网| 欧美亚洲精品在线| 精品视频网站| 日韩精品亚洲专区在线观看| 电影亚洲精品噜噜在线观看 | 欧美aa一级| 日韩区欧美区| 午夜电影亚洲| 久久精品国产999大香线蕉| 国产免费成人| 日韩精品诱惑一区?区三区| 日韩高清成人在线| 日韩一级精品| 日本欧美不卡| 久久影院一区二区三区| 亚洲狼人精品一区二区三区| 日韩在线免费| 精品国产中文字幕第一页 | 五月激激激综合网色播| 午夜久久影院| 99精品在线| 岛国av免费在线观看| 欧美私人啪啪vps| 亚洲深夜福利在线观看| 欧美日韩免费观看一区=区三区| 国产aⅴ精品一区二区三区久久| 亚洲va久久| 美女日韩在线中文字幕| 国户精品久久久久久久久久久不卡| 国产成人黄色| 成人精品动漫一区二区三区| 欧美1区二区| 国产精品一区二区精品视频观看 | 亚洲一区二区小说| 日韩一级精品| 亚洲欧洲一区| 婷婷综合激情| 精品欧美久久| 欧美1区免费| 国产综合激情| 免费观看久久av| 午夜精品久久久久久久久久蜜桃| 麻豆精品在线播放| 欧美a一区二区| 国产精品亚洲人成在99www| 日韩av网站免费在线| 日本精品国产| 久久国产乱子精品免费女| 日韩精品视频在线看| 亚洲人成亚洲精品| 日韩精品五月天| 国产午夜精品一区在线观看| 久久精品99国产精品日本| 国产精品二区影院| 精品国产一级| 日韩啪啪电影网| 久久免费国产| 在线日韩中文| 亚洲一区二区三区免费在线观看| 久久伦理在线| 一区三区视频| 日本欧美在线看| 三级久久三级久久久| 欧美日韩a区| 麻豆精品视频在线| 国产99在线| 91成人精品视频| 亚洲人成毛片在线播放女女| 日韩av中文字幕一区二区三区| 人人精品久久| 精品国产精品久久一区免费式| 98精品久久久久久久| 成人午夜精品| 先锋亚洲精品| 国产视频一区二区在线播放| 国产一级成人av| 成人一区而且| 日韩一级网站| 97成人在线| 日韩av免费大片| 99亚洲视频| 日韩中文字幕一区二区高清99| 久久99久久久精品欧美| 日韩和的一区二在线| 免费观看不卡av| 日韩精品亚洲专区在线观看| 麻豆中文一区二区| 欧美精品一二| 日韩二区三区在线观看| 日韩精品dvd| 久久国产精品毛片| 欧美国产另类| 中文一区在线| 国产精品v一区二区三区| 精品免费av在线| 日韩av在线免费观看不卡| 伊伊综合在线| 亚洲一级大片| 成人片免费看| 日韩影片在线观看| 精品免费av在线| 欧美精品影院| 国产精品88久久久久久| 国产区精品区| 亚洲二区三区不卡| 国产美女亚洲精品7777| 亚洲国产不卡| 国产成人调教视频在线观看| 午夜一级久久| 高潮一区二区| 日本天堂一区| 亚洲精品2区| 成人在线黄色| 97se亚洲| 国产毛片一区| 国产+成+人+亚洲欧洲在线| 中文字幕一区二区精品区| 日韩免费高清| 久久久久亚洲精品中文字幕| 亚洲人成高清| 91精品国产福利在线观看麻豆| 国产日韩一区二区三区在线 | 在线亚洲国产精品网站| 精品久久网站| 欧美天堂一区| 久久亚洲欧洲| 欧美丝袜一区| 麻豆视频在线看| 国产乱人伦丫前精品视频| 亚洲激情偷拍| 亚洲1234区| 老司机精品视频在线播放| 亚洲三级网站| 1024精品久久久久久久久| 国产精品久久久久久久免费观看| 日韩动漫一区| 亚洲欧美在线专区| 亚洲欧洲一区| 久久五月天小说| 91日韩在线| 久久不卡日韩美女| 奇米亚洲欧美| 日韩一区二区三区高清在线观看| 免费精品国产的网站免费观看| 亚洲欧洲高清| 91亚洲国产| 精品久久电影| 精品国产一区二区三区av片| 国产欧美自拍一区| 亚欧洲精品视频在线观看| 国产亚洲亚洲| 欧美1级日本1级| 特黄毛片在线观看| 国产一区二区亚洲| 国内在线观看一区二区三区| 欧美国产亚洲精品| 国产精品成人国产| 久久av导航|