본문 바로가기

프로그래밍(TA, AA)/자바스크립트

[Vue.js] 공식 가이드 문서 요약 (1)

[선언적 렌더링]

Vue.js의 핵심은 간단한 템플릿 구문을 사용해 선언적으로 DOM에 데이터를 렌더링 하는것.

data와 DOM이 연결되어 모든것이 반응형(reactive)하다.  element 속성을 바인딩 하는것도 가능



[디렉티브]

v-bind 속성 디렉티브(e.g. <a :href="url">)라고 한다. 디렉티브는 Vue에서 제공하는 특수 속성인 v- 접두어가 붙어있으며 렌더링된 DOM에 특수한 반응형 동작을 한다. 


v-if 속성을 통해서 엘리먼트 토글이 가능하다. v-for 디렉티브는 배열의 데이터를 사용해 목록을 표시한다. v-model 디렉티브는 앱 상태를 양방향으로 바인딩한다.


v-on 디렉티브(e.g. <a @click="doSomething">...</a>)를 사용하여 Vue 인스턴스에 메소드를 호출하는 이벤트 리스터를 첨부할 수 있다. DOM을 건드리지 않고 앱의 상태를 업데이트한다. 모든 DOM 조작은 Vue에 의해 처리되며 작성한 코드는 기본 로직에만 초점을 맞춘다.

<div id="app-5">
	<p>{{ message }}</p>
	<button v-on:click="reverseMessage">메시지 뒤집기</button>
</div>
var app5 = new Vue({
	el: "#app-5",
	data: {
		message: '안녕하세요! Vue.js!'
	},
	methods: {
		reverseMessage: function() {
			this.message = this.message.split('').reverse().join('')
		}
	}
});




[컴포넌트 트리]

컴포넌트 시스템은 Vue의 중요한 개념이다. 작은 단위 기능으로 동작하며 재사용할 수 있는 컴포넌트로 구성된 응용애플리케이션을 작성할 수 있게 해준다.


Vue에서 컴포넌트는 미리 정의된 옵션을 가진 Vue 인스턴스다. Vue에서 컴포넌트를 등록하는 방법이다.

Vue.component('todo-item', {
	template: '<li>할일 항목 하나입니다.</li>'
})

이제 다른 컴포넌트의 템플릿에서 이 컴포넌트를 사용할 수 있다.  부모 영역의 데이터를 자식 컴포넌트에 주입 즉, prop을 전달받을수 있도록 컴포넌트 정의를 수정하는 것도 가능하다.

<div id="app-7">
	<ol>
		<!-- 각 구성 요소에 "키"를 제공해야 함. -->
		<todo-item
			v-for="item in groceryList"
			v-bind:todo="item"
			v-bind:key="item.id">
		</todo-item>
	</ol>
</div>
Vue.component('todo-item', {
	props: ['todo'],
	template: '<li>{{ todo.text }}</li>'
})

var app7 = new Vue({
	el: '#app-7',
	data: {
		groceryList: [
			{ id: 0, text: 'Vegetables' },
			{ id: 1, text: 'Cheese' },
			{ id: 2, text: 'Whatever else humans are supposed to eat'}
		]
	}
})


부모 컴포넌트에 영향을 주지 않으면서 <todo-item> 컴포넌트를 더 복잡한 템플릿과 로직으로 더욱 향상시킬 수 있게된다.




[Vue 인스턴스 생성]

모든 Vue앱은 Vue 함수로 신규 Vue 인스턴스를 만드는 것부터 시작된다.

var vm = new Vue({
	// 옵션
})

Vue 인스턴스를 인스턴스화할 때는 데이터, 템플릿, 마운트할 엘리먼트, 메소드, 라이프사이클 콜백등의 옵션을 포함할 수 있는 options 객체를 전달해야 한다. Vue 생성자는 미리 정의된 옵션으로 재사용 가능한 컴포넌트 생성자를 생성하도록 확장될 수 있다. 


대개 템플릿에서 사용자 지정 엘리먼트로 선언적으로 작성하는 것이 권장된다. 모든 Vue 컴포넌트는 본질적으로 확장된 Vue 인스턴스이다.


Vue 인스턴스 option 중 data가 변경되면 화면은 다시 렌더링된다. data에 있는 속성들은 인스턴스가 생성될때 존재한 것들만 반응형이 된다. 유일한 예외는 Object.freeze()를 사용하는 경우다. 이는 기존 속성이 변경되는 것을 막아 반응성 시스템이 추적할 수 없다는 것을 의미한다.

var obj = {
	foo: 'bar'
}

Object.freeze(obj)

new Vue({
	el: '#app',
	data: obj
})




내부적으로 Vue는 템플릿을 가상 DOM 렌더링함수로 컴파일한다. 앱상태가 변경될때 최소한으로 DOM 변경처리를 하려는 의도를 가지고 있다.  


Vue가 v-for에서 렌더링된 엘리먼트 목록을 갱신할때 기본적으로 "in-place patch" 전략을 사용한다. 데이터 항목의 순서가 변경된 경우 항목의 순서와 일치하도록 DOM 요소를 이동하는 대신 Vue는 각 요소를 적절한 위치에 패치하고 해당 인덱스에서 렌더링할 내용을 반영하는지 확인한다.


Vue에서 DOM 노드들을 추적하고 기존 엘리먼트를 재사용/재정렬하기 위해서 v-for의 각 항목들에 고유한 key 속성을 제공해야 한다. 속성처럼 작동하기 때문에 v-bind를 사용하여 동적 값에 바인딩 해야 한다.

<div v-for="item in items" :key="item.id">
	<!-- content -->
</div>


반복되는 DOM 내용은 v-for에 key를 추가하는 것이 좋다. key는 Vue가 노드를 식별하는 일반적인 메커니즘이기 때문에 v-for와 특별히 연관되지 않는 다른 용도로 사용된다. Vue.js 2,2.0 이상부터는 v-for는 key가 필수이다.


Vue는 배열에 대해 다음과 같은 변경 변경사항을 감지할 수 없다.


인덱스로 배열에 있는 항목을 직접 설정하는 경우 (vm.items[indexOfItem] = newValue)

// 해결책1. Vue.set
Vue.set(example1.items, indexOfItem, newValue)

// 해결책2. Array.prototype.splice
example1.items.splice(indexOfItem, 1, newValue)




모던 Javascript의 한계로 Vue는 속성 추가/삭제를 감지하지 못한다.

var vm = new Vue({
	data: {
		a: 1	// 반응형
	}
})

vm.b = 2 // 반응형이 아님.


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

var vm = new Vue({
	data: {
		userProfile: {
			name: 'Anika'
		}
	}
})

// userProfile 객체에 새로운 속성 age 추가
Vue.set(vm.userProfile, 'age', 27)

// 인스턴스 메소드 vm.$set 사용도 가능. Vue.set의 별칭
vm.$set(this.userProfile, 'age', 27)

// Object.assign(), _.extend()를 사용하여 기존 객체에 새속성 할당도 가능
// 이경우, 두 객체의 속성을 사용해 새 객체를 만들어야함
Object.assign(this.userProfile, {
	age: 27,
	favoriteColor: 'Vue Green'
})

this.userProfile = Object.assign({}, this.userProfile, {
	age: 27,
	favoriteColor: 'Vue Green'
})


컴포넌트에는 자체 범위가 분리되어있기때문에 컴포넌트에 데이터를 자동으로 전달하지는 않는다. 반복할 데이터를 컴포넌트로 전달하려면 props를 사용해야 한다. 데이터의 출처를 명확히하면 다른 상황에서 컴포넌트를 재사용할 수 있다.

<div id="todo-list-example">
	<input
		v-model="newTodoText"
		@keyup.enter="addNewTodo"
		placeholder="Add a todo"
	>
	<ul>
		<li
			is="todo-item"
			v-for="(todo, index) in todos"
			:key="todo.id"
			:title="todo.title"	// prop 넘기는 방법
			@remove="todos.splice(index, 1)"
	</ul>
</div> 
Vue.component('todo-item', {
	template: '\
		<li>\
			{{ title }}\
			<button @click="$emit(\'remove\')">X</button>\
		</li>\
	',
	props: ['title']
})

new Vue({
	el: '#todo-list-example',
	data: {
		newTodoText: '',
		todos: [
			{
				id: 1,
				title: 'Do the dishes',
			},
			{
				id: 2,
				title: 'Take out the trash',
			},
			{
				id: 3,
				title: 'Now the lawn'
			}
		],
		nextTodoId: 4
	},
	methods: {
		addNewTodo: function() {
			this.todos.push({
				id: this.nextTodoId++,
				title: this.newTodoText
			})
			this.newTodoText = ''
		}
	}
})