32 vue $set, $nextTick

source: categories/study/vue-experiance/vue-experiance_9-22.md

32. vue $set, $nextTick

  • 반응형에 대해 깊이 알아보기

  • Vue 특징

    • 반응형 시스템 (단순한 자바스크립트 객체): 수정하면 화면 갱신
    • 스테이트 관리를 간단하고 직관적으로 만들어줌

1.1 변경 내용을 추적하는 방법

  • 변경 내용을 추적하는 방법
    • Vue 인스턴스에 자바스크립트 객체를 data 옵션으로 전달 -> Vue 모든 속성에 Object.defineProperty를 사용하여 getter/setter로 변환
    • 이는 Vue가 ES5를 사용할 수 없는 IE8 이하를 지원하지 않는 이유
  • getter/setter는 사용자에게는 보이지 않으나 속성에 액세스하거나 수정할 때 Vue가 종속성 추적 및 변경 알림을 수행할 수 있음
    • 한가지 주의사항: 변환된 데이터 객체가 기록될 때 브라우저가 getter/setter 형식을 다르게 처리하므로 친숙한 인터페이스를 사용하기 위해 vue-devtools를 설치하는 것이 좋음
  • 모든 컴포넌트 인스턴스에는 해당 watcher 인스턴스가 있음
    • 이 인스턴스는 컴포넌트가 종속적으로 렌더링되는동안 ‘수정'된 모든 속성을 기록
    • 나중에 종속적인 setter가 트리거되면 watcher에 알리고 컴포넌트가 다시 렌더링

1.2 변경 감지 경고

  • 최신 자바스크립트의 한계(그리고 Object.observe의 포기)로 인해 Vue는 속성의 추가 제거를 감지할 수 없음
    • Vue는 인스턴스 초기화중에 getter/setter 변환 프로세스를 수행하기 때문에 data 객체에 속성이 있어야 Vue가 이를 변환하고 응답할 수 있음


var vm = new Vue({
  data: {
    a: 1
  }
})
// `vm.a` 은 이제 반응적입니다.

vm.b = 2
// `vm.b` 은 이제 반응적이지 않습니다.


  • Vue는 이미 만들어진 인스턴스에 새로운 루트 수준의 반응 속성을 동적으로 추가하는 것을 허용하지 않음
    • 그러나 Vue.set(object, key, value) 메소드를 사용하여 중첩된 객체에 반응성 속성을 추가할 수 있음


Vue.set(vm.someObject, 'b', 2)


  • Vm.$set 인스턴스 메소드를 사용할 수도 있음
    • 이 메소드는 전역 Vue.set에 대한 별칭임


this.$set(this.someObject, 'b', 2)


Note

알겠다.. 즉, data에 루트수준에 초기화 안되어있던 프로퍼티 키를 정의하고 그 키를 추적하게하기 위한 수단이구나.
현재 프로젝트의 vue-range-slider 컴포넌트에서 this.$set으로 vue-range-slider 인스턴스의 data루트수준에 프로퍼티 키와 값을 설정하고,
이를 this.$emit으로 이벤트와 함께 그 데이터를 올려서 실제 바인딩해준 상위 컴포넌트에서 값을 할당!
이런 원리구나. 굿굿

그런데 궁금한점.
이렇게 추가된 속성은 그럼 변경 내용을 트리거하진 않는다는건가??
내용 읽어보면 굳이 추가하려면 $set을 사용해서 추가는하되, 이 속성은 변경감지는 안돼! 라는 뜻인거같음..

  • 때로는 예를 들어 Object.assign() 또는 _.extend()를 사용하여 기존 객체에 많은 속성을 할당할 수 있음
    • 그러나 객체에 추가된 새 속성은 변경 내용을 트리거하지 않음
    • 이 경우 원본 객체와 mixin 객체의 속성을 사용하여 새 객체를 만듭니다.


// `Object.assign(this.someObject, { a: 1, b: 2 })` 대신에
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })


Note

위 코드보면 deep copy 개념인데..
속성 추가이긴한데 변경감지되게하려면 새로 할당해! 이거말하는건가?

1.3 반응형 속성 선언하기

  • Vue는 루트 수준의 반응성 속성을 동적으로 추가할 수 없으므로 모든 루트 수준의 반응성 데이터 속성을 빈 값으로라도 초기에 선언하여 Vue 인스턴스를 초기화해야함


var vm = new Vue({
  data: {
    // 빈 값으로 메시지를 선언 합니다.
    message: ''
  },
  template: '
{{ message }}
' }) // 나중에 `message`를 설정합니다. vm.message = 'Hello!'
  • data 옵션에 message를 선언하지 않으면 Vue는 render 함수가 존재하지 않는 속성에 접근하려고 한다는 경고를함
    • 이 제한 사항에는 기술적인 이유가 있음
    • 종속성 추적 시스템에서 엣지 케이스 클래스를 제거하고 Vue 인스턴스를 유형 검사 시스템으로 더 멋지게 만듦
    • 그러나 코드 유지 관리 측면에서도 중요한 고려 사항이 있음
    • data 객체는 컴포넌트 상태에 대한 스키마와 같음
    • 모든 반응 속성을 미리 선언하면 나중에 다시 방문하거나 다른 개발자가 읽을 때 구성 요소 코드를 더 쉽게 이해할 수 있음

1.4 비동기 갱신 큐

  • Vue는 DOM 업데이트를 비동기로함
    • 데이터 변경이 발견될 때마다 큐를 열고 같은 이벤트 루프에서 발생하는 모든 데이터 변경을 버퍼에 담음
    • 같은 Watcher가 여러번 발생하면 대기열에서 한번만 푸시됨
    • 이 버퍼링된 중복의 제거는 불필요한 계산과 DOM 조작을 피하는데 있어 중요함
    • 그 다음 이벤트 루프 ‘tick'에서 Vue는 대기열을 비우고 실제 (이미 중복 제거 된) 작업을 수행함
    • 내부적으로 Vue는 비동기 큐를 위해 네이티브 Promise.thenMessageChannel를 시도하고 setTimeout(fn, 0)으로 돌아감
  • 예를 들어, vm.someData = 'new value'를 설정하면 컴포넌트는 즉시 재랜더링되지 않음
    • 큐가 플러시될 때 다음 ‘tick'에서 업데이트됨
    • 대개의 경우 이 작업을 신경쓸 필요는 없지만 업데이트 후 DOM 상태에 의존하는 작업을 수행하려는 경우 까다로울 수 있음
    • Vue.js는 일반적으로 개발자가 데이터 중심 방식으로 생각하고 DOM을 직접 만지지 않도록 권장하지만 때로는 건드려야 할 수도 있음
    • Vue.js가 데이터 변경 후 DOM 업데이트를 마칠 때까지 기다리려면 데이터가 변경된 직후에 Vue.nextTick(콜백)을 사용할 수 있음
      • 콜백은 DOM이 업데이트된 후에 호출됨
  • 예시


<div id="example">{{ message }}</div>




var vm = new Vue({
  el: '#example',
  data: {
    message: '123'
  }
})
vm.message = 'new message' // 데이터  변경
vm.$el.textContent === 'new message' // false
Vue.nextTick(function () {
  vm.$el.textContent === 'new message' // true
})


  • 또한 vm.$nextTick() 인스턴스 메소드가 있음
    • 이는 내부 컴포넌트들에 특히 유용함
    • 왜냐하면 전역 Vue가 필요없고 콜백의 this 컨텍스트가 자동으로 현재 Vue 인스턴스에 바인드될 것이기 때문


Vue.component('example', {
  template: '{{ message }}',
  data: function () {
    return {
      message: '갱신 안됨'
    }
  },
  methods: {
    updateMessage: function () {
      this.message = '갱신됨'
      console.log(this.$el.textContent) // => '갱신 안됨'
      this.$nextTick(function () {
        console.log(this.$el.textContent) // => '갱신됨'
      })
    }
  }
})


  • $nextTick()은 promise를 반환함
    • ES2017 async/await 문법을 사용하여 독같은 동작을 수행할 수 있음


methods: {
  updateMessage: async function () {
    this.message = '갱신됨'
    console.log(this.$el.textContent) // => '갱신 안됨'
    await this.$nextTick()
    console.log(this.$el.textContent) // => '갱신됨'
  }
}


2. nextTick