成人在线亚洲_国产日韩视频一区二区三区_久久久国产精品_99国内精品久久久久久久

您的位置:首頁技術文章
文章詳情頁

Vue為什么要謹慎使用$attrs與$listeners

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

前言

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

隱患

先來看一個例子:

Vue為什么要謹慎使用$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?’); },}

可以看到,當我們在輸入框輸入值的時候,只有修改到 input 字段,從而更新父組件,而子組件的 props test 則是沒有修改的,按照 誰更新,更新誰 的標準來看,子組件是不應該更新觸發 updated 方法的,那這是為什么呢?

于是我發現這個“bug”,并迅速打開 gayhub 提了個 issue ,想著我也是參與過重大開源項目的人了,還不免一陣竊喜。事實很殘酷,這么明顯的問題怎么可能還沒被發現...

Vue為什么要謹慎使用$attrs與$listeners

無情……,于是我打開看了看,尤大說了這么一番話我就好像明白了:

Vue為什么要謹慎使用$attrs與$listeners

那既然不是“bug”,那來看看是為什么吧。

前因

首先介紹一個前提,就是 Vue 在更新組件的時候是更新對應的 data 和 props 觸發 Watcher 通知來更新渲染的。

每一個組件都有一個唯一對應的 Watcher ,所以在子組件上的 props 沒有更新的時候,是不會觸發子組件的更新的。當我們去掉子組件上的 v-bind='$attrs' 時可以發現, updated 鉤子不會再執行,所以可以發現問題就出現在這里。

原因分析

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 上,并且設置成響應式對象,離發現奧秘又近了一步。

依賴收集

我們知道 Vue 會通過 Object.defineProperty 方法來進行依賴收集,由于這部分內容也比較多,這里只進行一個簡單了解。

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

通過對 get 的劫持,使得我們在訪問 $attrs 時它( dep )會將 $attrs 所在的 Watcher 收集到 dep 的 subs 里面,從而在設置時進行派發更新( notify() ),通知視圖渲染。

派發更新

下面是在改變響應式數據時派發更新的核心邏輯:

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

很簡單的一部分代碼,就是在響應式數據被 set 時,調用 dep 的 notify 方法,遍歷每一個 Watcher 進行更新。

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

了解到這些基礎后,我們再回頭看看 $attrs 是如何觸發子組件的 updated 方法的。

要知道子組件會被更新,肯定是在某個地方訪問到了 $attrs ,依賴被收集到 subs 里了,才會在派發時被通知需要更新。我們對比添加 v-bind='$attrs' 和不添加 v-bind='$attrs' 調試一下源碼可以看到:

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; // 看看當前 dep 是啥 debugger; // debugger 斷點 return value }

當綁定了 v-bind='$attrs' 時,會多收集到一個依賴。

Vue為什么要謹慎使用$attrs與$listeners

會有一個 id 為 8 的 dep 里面收集了 $attrs 所在的 Watcher ,我們再對比一下有無 v-bind='$attrs' 時的 set

派發更新狀態:

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; // 查看當前 dep debugger; // debugger 斷點 dep.notify(); }

Vue為什么要謹慎使用$attrs與$listeners

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

其實值并沒有改變而進行了更新這個問題。

問題:$attrs 的依賴是如何被收集的呢?

我們知道依賴收集是在 get 中完成的,但是我們初始化的時候并沒有訪問數據,那這是怎么實現的呢?

答案就在 vm._render() 這個方法會生成 Vnode 并在這個過程中會訪問到數據,從而收集到了依賴。

那還是沒有解答出這個問題呀,別急,這還是一個鋪墊,因為你在 vm._render() 里也找不到在哪訪問到了 $attrs ...

柳暗花明

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

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

這就是最終訪問 $attrs 的地方了,所以 $attrs 會被收集到依賴中,當 input 中 v-model 的值更新時,觸發 set 通知更新,而在更新組件時調用的 updateChildComponent 方法中會對 $attrs 進行賦值:

// 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;

所以會觸發 $attrs 的 set ,導致它所在的 Watcher 進行更新,也就會導致子組件更新了。而如果沒有綁定 v-bind='$attrs' ,則雖然也會到這一步,但是沒有依賴收集的過程,就無法去更新子組件了。

奇淫技巧

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

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

總結

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

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

一開始這樣設計目的是為了 HOC 高階組件更好的創建使用,便于 HOC 組件總能對數據變化做出反應,但是在實際過程中與 v-model 產生了一些副作用,對于這兩者的使用,建議在沒有數據頻繁變化時可以使用,或者使用上面的奇淫技巧,以及……把產生頻繁變化的部分扔到一個單獨的組件中讓他自己自娛自樂去吧。

到此這篇關于Vue為什么要謹慎使用$attrs與$listeners的文章就介紹到這了,更多相關Vue $attrs與$listeners內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!

標簽: Vue
相關文章:
成人在线亚洲_国产日韩视频一区二区三区_久久久国产精品_99国内精品久久久久久久
成人综合婷婷国产精品久久| 一区二区三区国产| 亚洲午夜一区二区| 91福利在线看| 亚洲国产一区二区三区在线播| 日韩国产欧美在线视频| 亚洲视频免费在线观看| 91精品婷婷国产综合久久竹菊| 一区二区三区三区在线| 欧美系列亚洲系列| 影音先锋一区| 欧美日韩在线精品| 大桥未久av一区二区三区中文| 亚洲国产精品成人综合| 国产成人久久精品77777最新版本 国产成人鲁色资源国产91色综 | aa级大片欧美| 欧美tk丨vk视频| 亚洲视频中文字幕| 色诱亚洲精品久久久久久| 玉足女爽爽91| 欧美成人猛片aaaaaaa| av成人激情| 久久综合网色—综合色88| 久久国产欧美日韩精品| 一区二区冒白浆视频| 国产午夜精品一区二区三区嫩草| 国产一区在线视频| 亚洲一区二区精品在线观看| 国产精品丝袜91| 99视频在线观看一区三区| 8x8x8国产精品| 美腿丝袜亚洲一区| 欧美亚洲三区| 亚洲人妖av一区二区| 99国产精品一区| 色偷偷久久一区二区三区| 久久久www成人免费毛片麻豆| 国产剧情av麻豆香蕉精品| 在线视频中文字幕一区二区| 亚洲人成影院在线观看| 91论坛在线播放| 欧美一级国产精品| 久久电影网电视剧免费观看| 久久久成人网| 亚洲国产日韩一级| 国产欧美一区二区色老头| 亚洲欧美怡红院| 好吊日精品视频| 国产目拍亚洲精品99久久精品| 美女被吸乳得到大胸91| 国产精品入口| 亚洲精品欧美二区三区中文字幕| 激情婷婷亚洲| 自拍偷拍欧美精品| 亚洲私人影院| 亚洲视频1区2区| 国内精品久久久久影院一蜜桃| 色呦呦网站一区| 日本欧美一区二区在线观看| 一本大道久久a久久综合| 天天综合天天综合色| 裸体一区二区| 天使萌一区二区三区免费观看| 亚洲国产成人不卡| 国产日韩欧美精品在线| 国产成人在线视频网站| 欧美色偷偷大香| 洋洋av久久久久久久一区| 国产精品一区二区不卡| 销魂美女一区二区三区视频在线| 亚洲国产精品久久久久婷婷884| 99国产精品久久久久老师| 亚洲美女视频一区| 国产区欧美区日韩区| 亚洲成人综合在线| 欧美唯美清纯偷拍| 激情图区综合网| 欧美一区二区三区免费在线看| 国产一区二区主播在线| 日韩一二三四区| 风间由美中文字幕在线看视频国产欧美| 欧美一区二区视频在线观看| 成人网在线免费视频| 久久精品一区二区三区四区| 欧美婷婷久久| 一区二区三区在线观看网站| 99成人在线| 亚洲成人免费看| 色婷婷久久久综合中文字幕| 日本不卡视频一二三区| 在线观看日韩精品| 国产乱对白刺激视频不卡| 欧美一级片在线| av成人免费在线观看| 久久香蕉国产线看观看99| av在线不卡观看免费观看| 亚洲精品在线网站| 国色天香一区二区| 成人欧美一区二区三区黑人麻豆| 国产欧美成人| 久久国产夜色精品鲁鲁99| 日韩欧美国产一区二区在线播放| 午夜精品免费| 亚洲免费在线视频| 亚洲欧美影院| 国产精品久久久久久久久快鸭| 亚洲片区在线| 奇米亚洲午夜久久精品| 3d动漫精品啪啪| 午夜视频久久久| 国产精品久久久久久久久动漫| 在线亚洲免费| 久久婷婷一区二区三区| 在线日韩av| 日本欧美韩国一区三区| 日韩欧美国产一区二区在线播放| 欧美日韩精品久久| 性欧美疯狂xxxxbbbb| 欧美午夜一区二区三区免费大片| 成人午夜激情在线| 国产欧美一区二区精品久导航 | 色综合久久久久久久久久久| 国产69精品久久久久毛片| 久久久久88色偷偷免费| 国产偷国产偷亚洲高清97cao| 国产在线国偷精品产拍免费yy| 欧美一区二区黄色| 欧美不卡高清| 一区二区三区在线免费观看 | 欧美优质美女网站| 92国产精品观看| 日韩女优电影在线观看| 99热在线精品观看| 国产乱码字幕精品高清av| 亚洲欧美色图小说| 3d成人h动漫网站入口| 欧美激情精品久久久六区热门| 中文字幕一区二区5566日韩| 久久精品99| 国产福利一区二区三区视频在线| 欧美日韩综合在线免费观看| 亚洲欧美日韩国产综合在线| 日韩福利视频网| 欧美精品一区二区精品网| 国产精品日韩欧美一区二区| 国产999精品久久久久久| 中文字幕亚洲在| 在线一区二区观看| 欧美日韩喷水| 亚洲精品乱码久久久久| 日韩欧美电影一区| 成人激情免费网站| 亚洲美女屁股眼交| 日韩视频免费直播| 亚洲每日在线| 日本成人在线网站| 欧美日韩国产首页| 欧美一区二区三区另类| 天堂成人国产精品一区| 在线不卡欧美精品一区二区三区| 青青草成人在线观看| 精品国产制服丝袜高跟| 久久久999| 欧美高清视频一区二区三区在线观看 | 亚洲综合精品自拍| 99精品热6080yy久久| 久久国产欧美日韩精品| 日韩一区二区免费在线观看| 欧美日韩亚洲一区在线观看| 婷婷综合另类小说色区| 久久伊人蜜桃av一区二区| 久久aⅴ国产紧身牛仔裤| 欧美fxxxxxx另类| 国产寡妇亲子伦一区二区| 亚洲成人av在线电影| 中文字幕av免费专区久久| 91精品国产色综合久久| 先锋影音久久久| 亚洲高清不卡一区| 成人久久久精品乱码一区二区三区 | 毛片一区二区三区| 国产精品国产三级国产普通话蜜臀| 在线一区二区观看| 国产欧美激情| 欧美三级免费| 不卡av在线网| 麻豆视频一区二区| 亚洲国产精品久久人人爱| 26uuu久久综合| 欧美一区二区三区视频在线观看| 亚洲一区区二区| 欧美日韩在线一二三| www.亚洲色图.com| 蜜桃久久av一区| 亚洲国产wwwccc36天堂| 欧美mv日韩mv国产网站| 欧美午夜一区二区三区免费大片| 久久久综合香蕉尹人综合网| 日韩午夜在线| 影音先锋在线一区|