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

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

 更新時間:2020年08月27日 09:43:26   作者:眼已望穿  
這篇文章主要介紹了Vue為什么要謹慎使用$attrs與$listeners,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

前言

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

隱患

先來看一個例子:

父組件:

{
 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 則是沒有修改的,按照 誰更新,更新誰 的標準來看,子組件是不應該更新觸發(fā) updated 方法的,那這是為什么呢?

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

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

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

前因

首先介紹一個前提,就是 Vue 在更新組件的時候是更新對應的 dataprops 觸發(fā) Watcher 通知來更新渲染的。

每一個組件都有一個唯一對應的 Watcher ,所以在子組件上的 props 沒有更新的時候,是不會觸發(fā)子組件的更新的。當我們?nèi)サ糇咏M件上的 v-bind="$attrs" 時可以發(fā)現(xiàn), updated 鉤子不會再執(zhí)行,所以可以發(fā)現(xià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 上,并且設置成響應式對象,離發(fā)現(xiàn)奧秘又近了一步。

依賴收集

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

 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 收集到 depsubs 里面,從而在設置時進行派發(fā)更新( notify() ),通知視圖渲染。

派發(fā)更新

下面是在改變響應式數(shù)據(jù)時派發(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()
 }
 })

很簡單的一部分代碼,就是在響應式數(shù)據(jù)被 set 時,調(diào)用 depnotify 方法,遍歷每一個 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()
 }
 }

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

要知道子組件會被更新,肯定是在某個地方訪問到了 $attrs ,依賴被收集到 subs 里了,才會在派發(fā)時被通知需要更新。我們對比添加 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; // 看看當前 dep 是啥
 debugger; // debugger 斷點
 return value
 }

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

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

派發(fā)更新狀態(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; // 查看當前 dep
 debugger; // debugger 斷點
 dep.notify();
 }

這里可以明顯看到也是 id8dep 正準備遍歷 subs 通知 Watcher 來更新,也能看到 newValvalue

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

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

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

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

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

柳暗花明

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

const compiler = require('vue-template-compiler');

const result = compiler.compile(
 // `
 // <div :test="test">
 //  <p>測試內(nèi)容</p>
 // </div>
 // `
 `
 <div v-bind="$attrs">
 <p>測試內(nèi)容</p>
 </div>
`
);

console.log(result.render);

// with (this) {
// return _c(
//  'div',
//  { attrs: { test: test } },
//  [
//  _c('p', [_v('測試內(nèi)容')])
//  ]
// );
// }

// with (this) {
// return _c(
//  'div',
//  _b({}, 'div', $attrs, false),
//  [
//  _c('p', [_v('測試內(nèi)容')])
//  ]
// );
// }

這就是最終訪問 $attrs 的地方了,所以 $attrs 會被收集到依賴中,當 inputv-model 的值更新時,觸發(fā) set 通知更新,而在更新組件時調(diào)用的 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;

所以會觸發(fā) $attrsset ,導致它所在的 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>

總結(jié)

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

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

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

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

相關(guān)文章

  • Vue常見面試題整理【值得收藏】

    Vue常見面試題整理【值得收藏】

    本文是小編給大家收藏整理的Vue常見面試題,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2018-09-09
  • 詳解Vue如何實現(xiàn)響應式布局

    詳解Vue如何實現(xiàn)響應式布局

    這篇文章主要為大家詳細介紹了Vue實現(xiàn)響應式布局的兩種方法,文中的示例代碼講解詳細,具有一定的學習價值,感興趣的小伙伴可以跟隨小編一起了解一下
    2023-12-12
  • vue單文件組件lint error自動fix與styleLint報錯自動fix詳解

    vue單文件組件lint error自動fix與styleLint報錯自動fix詳解

    這篇文章主要給大家介紹了關(guān)于vue單文件組件lint error自動fix與styleLint報錯自動fix的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-01-01
  • vue如何通過src引用assets中的圖片

    vue如何通過src引用assets中的圖片

    這篇文章主要介紹了vue如何通過src引用assets中的圖片,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • vue-cli+webpack項目 修改項目名稱的方法

    vue-cli+webpack項目 修改項目名稱的方法

    下面小編就為大家分享一篇vue-cli+webpack項目 修改項目名稱的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-02-02
  • vuex狀態(tài)管理數(shù)據(jù)狀態(tài)查詢與更改方式

    vuex狀態(tài)管理數(shù)據(jù)狀態(tài)查詢與更改方式

    這篇文章主要介紹了vuex狀態(tài)管理數(shù)據(jù)狀態(tài)查詢與更改方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • electron-vue?項目添加啟動loading動畫的實現(xiàn)思路

    electron-vue?項目添加啟動loading動畫的實現(xiàn)思路

    electron-vue腳手架搭建的項目,在開發(fā)階段可能你注意不到項目啟動慢的問題,但是在build?生成的exe可執(zhí)行文件,啟動后,要反應很久才能進入到app.vue?中加載的頁面,體驗性很差,本文給大家介紹electron?vue啟動動畫效果的實例代碼,感興趣的朋友一起看看吧
    2022-01-01
  • vue動畫打包后失效問題的解決方法

    vue動畫打包后失效問題的解決方法

    這篇文章主要介紹了vue動畫打包后失效問題的解決方法,在文中給大家提到了vue-cli 打包后自定義動畫未執(zhí)行的解決方法,需要的朋友可以參考下
    2018-09-09
  • vue中使用iview自定義驗證關(guān)鍵詞輸入框問題及解決方法

    vue中使用iview自定義驗證關(guān)鍵詞輸入框問題及解決方法

    這篇文章主要介紹了vue中使用iview自定義驗證關(guān)鍵詞輸入框問題及解決方法,本文通過實例結(jié)合代碼的形式給大家介紹解決方法,需要的朋友可以參考下
    2018-03-03
  • vue實現(xiàn)修改圖片后實時更新

    vue實現(xiàn)修改圖片后實時更新

    今天小編就為大家分享一篇vue實現(xiàn)修改圖片后實時更新,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-11-11

最新評論

黄山市| 商水县| 东宁县| 临安市| 塘沽区| 西乌珠穆沁旗| 兴山县| 额尔古纳市| 贵南县| 南华县| 上蔡县| 高雄县| 绵竹市| 黄平县| 泸水县| 宁城县| 霍州市| 绍兴县| 武汉市| 彰化县| 施秉县| 澎湖县| 邵武市| 大丰市| 巴彦县| 自贡市| 孟津县| 张家川| 通城县| 清徐县| 蕉岭县| 隆昌县| 历史| 调兵山市| 宜川县| 临沭县| 云龙县| 措美县| 桦甸市| 甘泉县| 乌兰浩特市|