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 이하를 지원하지 않는 이유
- Vue 인스턴스에 자바스크립트 객체를
getter/setter
는 사용자에게는 보이지 않으나 속성에 액세스하거나 수정할 때 Vue가 종속성 추적 및 변경 알림을 수행할 수 있음- 한가지 주의사항: 변환된 데이터 객체가 기록될 때 브라우저가
getter/setter
형식을 다르게 처리하므로 친숙한 인터페이스를 사용하기 위해vue-devtools
를 설치하는 것이 좋음
- 한가지 주의사항: 변환된 데이터 객체가 기록될 때 브라우저가
- 모든 컴포넌트 인스턴스에는 해당
watcher
인스턴스가 있음- 이 인스턴스는 컴포넌트가 종속적으로 렌더링되는동안 ‘수정'된 모든 속성을 기록
- 나중에 종속적인
setter
가 트리거되면watcher
에 알리고 컴포넌트가 다시 렌더링
1.2 변경 감지 경고
- 최신 자바스크립트의 한계(그리고
Object.observe
의 포기)로 인해 Vue는 속성의 추가 제거를 감지할 수 없음- Vue는 인스턴스 초기화중에
getter/setter
변환 프로세스를 수행하기 때문에data
객체에 속성이 있어야 Vue가 이를 변환하고 응답할 수 있음
- 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.then
와MessageChannel
를 시도하고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) // => '갱신됨'
}
}