Vue 는 왜$attrs 와$listeners 를 신중하게 사용 합 니까?

10283 단어 Vue$attrs$listeners
머리말Vue개발 과정 에서 조상 구성 요 소 를 만나면 손자 구성 요소 에 값 을 전달 해 야 할 때 아들 구성 요소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필드 만 수정 하여 부모 구성 요 소 를 업데이트 하 는 것 을 볼 수 있 습 니 다.하위 구성 요소 의 propstest는 수정 되 지 않 았 습 니 다.기준 으로 볼 때 하위 구성 요 소 는 트리거updated방법 을 업데이트 하지 말 아야 합 니 다.왜 일 까요?
그래서 나 는 이'bug'를 발견 하고 신속하게 열 었 다gayhub.나 도 중대 한 오픈 소스 프로젝트 에 참여 한 사람 이 라 고 생각 하고 깜짝 놀 랐 다.사실 이 잔혹 하 다.이렇게 뚜렷 한 문제 가 어떻게 아직 발견 되 지 않 았 을 수 있 겠 는가..

무정 하 다..........................................................................

그럼"bug"가 아 닌 이상 왜 그런 지 봅 시다.
원인
먼저 전 제 를 소개 합 니 다.issue구성 요 소 를 업데이트 할 때 해당 하 는Vuedata트리거props알림 을 업데이트 하여 렌 더 링 을 업데이트 하 는 것 입 니 다.
모든 구성 요 소 는 유일 하 게 대응 하 는Watcher이 있 기 때문에 하위 구성 요소Watcher가 업데이트 되 지 않 았 을 때 하위 구성 요소 의 업 데 이 트 를 촉발 하지 않 습 니 다.우리 가 하위 구성 요소 의props를 제거 할 때v-bind="$attrs"갈고리 가 더 이상 실행 되 지 않 기 때문에 문제 가 여기에 나타 난 것 을 발견 할 수 있다.
원인 분석updated원본 코드 에서 검색Vue,파일 찾기$attrs:

export function initRender (vm: Component) {
 // ...
 defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true)
 defineReactive(vm, '$listeners', options._parentListeners || emptyObject, null, true)
}
오,놀 랍 군!바로 그것 이다.src/core/instance/render.js방법 에서initRender속성 을$attrs에 연결 하고 응답 식 대상 으로 설정 하여 비밀 발견 에 한 걸음 더 가 까 워 진 것 을 볼 수 있다.
수집 에 의존 하 다
우 리 는thisVue방법 을 통 해 수집 에 의존 할 것 이라는 것 을 알 고 있다.이 부분 도 비교적 많 기 때문에 여 기 는 간단 한 이해 만 할 수 있다.

 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
 }
 })
Object.defineProperty에 대한 납 치 를 통 해 우 리 는get를 방문 할 때$attrsdep가 있 는$attrsWatcherdep에 수집 하여 설정 할 때 배포 업데이트subs를 하고 보기 렌 더 링 을 알 립 니 다.
업 데 이 트 를 배포 하 다.
다음은 응답 식 데 이 터 를 바 꿀 때 업 데 이 트 를 보 내 는 핵심 논리 입 니 다.

 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()
 }
 })
아주 간단 한 일부 코드 는 응답 식 데이터 가notify()되 었 을 때 호출set하 는dep방법 으로 각각notify을 옮 겨 다 니 며 업데이트 하 는 것 이다.

 notify () {
 // stabilize the subscriber list first
 const subs = this.subs.slice()
 for (let i = 0, l = subs.length; i < l; i++) {
  subs[i].update()
 }
 }
이러한 기 초 를 알 게 된 후에 우 리 는Watcher이 어떻게 하위 구성 요 소 를 촉발 하 는$attrs방법 을 되 돌아 보 았 다.
하위 구성 요소 가 업 데 이 트 될 것 이라는 것 을 알 아야 합 니 다.어 딘 가 에 방문 한 것 이 분명 합 니 다updated.수집$attrs에 의존 해 야 배포 할 때 업데이트 가 필요 하 다 는 통 지 를 받 을 수 있 습 니 다.우 리 는 추가subs와 추가 하지 않 음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"시 의존 도 를 하나 더 수집 합 니 다.

하나v-bind="$attrs"id8에서 수집dep이 있 는$attrs이 있 습 니 다.유 무Watcher시의v-bind="$attrs"를 비교 해 보 겠 습 니 다.
배포 업데이트 상태:

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

여기 서 분명히 볼 수 있 듯 이setid을 위 한8이 옮 겨 다 니 며dep알림subs을 업데이트 하려 고 준비 하고 있 고WatchernewVal도 볼 수 있다.
사실 값 은 변 하지 않 고 업데이트 되 었 다.
질문:$attrs 의 의존 은 어떻게 수집 되 었 습 니까?
우 리 는 수집 에 의존 하 는 것 이value에서 이 루어 졌 다 는 것 을 알 고 있 지만,우리 가 초기 화 할 때 데 이 터 를 방문 하지 않 았 다 면 어떻게 이 루어 졌 을 까?
답 은get이 방법 으로 생 성vm._render()되 고 이 과정 에서 데 이 터 를 방문 하여 의존 도 를 수집 하 는 것 이다.
그래도 이 문 제 를 풀 지 못 했 군요.서 두 르 지 마 세 요.이것 은 방석 입 니 다.Vnode에서 도 어디서 방 문 했 는 지 찾 을 수 없 기 때 문 입 니 다vm._render().
유암 화 명
우리 의 코드 와$attrs모두vm._render()에 접근 하지 않 았 습 니 다.이 유 는$attrs에 만 나타 날 수 있 기 때 문 입 니 다.우 리 는v-bind을 사용 하여 템 플 릿 을 컴 파일 해 보 겠 습 니 다.

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('    ')])
//  ]
// );
// }
이것 이 최종 방문vue-template-compiler의 곳 입 니 다.따라서$attrs은 의존 에 수 집 됩 니 다.$attrs에서input의 값 이 업데이트 되 었 을 때v-model업데이트 알림 을 터치 하고 구성 요 소 를 업데이트 할 때 호출 되 는set방법 에서updateChildComponent에 값 을 부여 합 니 다.

 // 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$attrs를 촉발 하여 그 가 있 는set을 업데이트 시 키 고 하위 구성 요소 의 업 데 이 트 를 초래 할 수 있 습 니 다.바 인 딩Watcher이 없 으 면 여기까지 올 수 있 지만 수집 과정 에 의존 하지 않 으 면 하위 구성 요 소 를 업데이트 할 수 없습니다.
기음 기교
또 남 의 몸 을 빌 고 싶다 면,아 퉤,남 의 편 의 를 빌 고,또 좋 은 성능 을 원한 다 면 어 떡 하지?여기에 곡선 으로 나 라 를 구 하 는 방법 이 있다.

<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>
총결산
지금까지 우 리 는v-bind="$attrs"데이터 가 변화 가 없 지만 하위 구성 요 소 를 업데이트 시 키 는 원인 을 분 석 했 습 니 다.소스 코드 에 다음 과 같은 말 이 있 습 니 다.
// $attrs & $listeners are exposed for easier HOC creation. // they need to be reactive so that HOCs using them are always updated
처음에 이런 디자인 의 목적 은$attrs고급 구성 요 소 를 더욱 잘 만 들 고 사용 하기 위해 서 였 다.HOC구성 요 소 는 항상 데이터 변화 에 반응 하지만 실제 과정 에서HOC와 부작용 이 생 겼 다.이 두 가지 사용 에 대해 데이터 가 빈번하게 변화 하지 않 을 때 사용 하거나 위의 기 음 기 교 를 사용 하 는 것 을 권장 했다.그리고..........................................................................
Vue 가 왜$attrs 와$listeners 를 신중하게 사용 하 는 지 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.Vue$attrs 와$listeners 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 많은 응원 부 탁 드 리 겠 습 니 다!

좋은 웹페이지 즐겨찾기