중첩된 컴포넌트들은 개별 컴포넌트가 독립된 공간을 갖고 Javascript의 스코프 체이닝과 달리 하위 컴포넌트가 상위 컴포넌트의 변수를 참조할 수 없다.
- 부모, 자식 컴포넌트간 필연적으로 의사소통이 필요한데
부모는 자식에게 데이터를 전달 할 수 있으며, 자식은 부모에게 이벤트를 전달 할 수 있다. - 즉, 중첩된 컴포넌트 사이 통신 방법을 명시적으로 처리할 수 있는 인터페이스를 제공
- 부모 컴포넌트의 템플릿에서는 자식 컴포넌트를 사용할 수 있다.
- 부모 컴포넌트의 템플릿에서 속성 정의를 통해 자식 컴포넌트에게 데이터를 전달 할 수 있다.
- 또한 커스텀 이벤트 리스너를 할당해서 자식 컴포넌트에서 방출(emit)시킨 이벤트를 받을 수 있다.
- Props로 데이터 전달하기
부모 컴포넌트는 자식 컴포넌트에 속성(props)을 사용하여 하위 컴포넌트에 전달할수 있다.
<!-- [ParentComp.vue 파일] -->
<template lang="pug">
div.component
h1 {{ mine }}
child(
class="child-component"
parent-name="ParentComponent"
:temp="1990"
:prop-message="message"
)
</template>
<script>
import ChildComp from './ChildComp';
export default {
components: {
'child': ChildComp
},
data () {
return {
message: ['오늘도', '내일도', '행복하길'],
mine: 'Parent Component'
}
}
}
</script><!-- [ChildComp.vue 파일] -->
<template lang="pug">
div.component
h1 {{ mine }}
h3 상위 컴포넌트로 부터 전달 받은 속성들
ul
li {{ parentName }}
li {{ temp }}
li Computed: {{ mine_propMessage }}
</template>
<script>
export default {
props: {
parentName: {
type: String,
validator(v){
return v.trim() !== '';
}
},
temp: {
type: Number,
required: true,
},
propMessage: {
type: Array,
// required: false,
// 주의! 참조형 데이터의 경우, 함수를 통해 객체를 반환해야 한다.
default: function(){
return ['오늘도'];
}
}
},
data () {
return {
mine: 'Child Component',
mine_prop_msg: this.propMessage,
copyed_message: []
}
},
computed: {
mine_propMessage() {
this.copyed_message = this.propMessage;
return this.copyed_message.join(' ');
}
}
}
</script>cf) HTML 속성은 대소 문자를 구분하지 않으므로 문자열이 아닌 템플릿을 사용할 때 camelCase prop 이름에 해당하는 kebab-case(하이픈 구분)를 사용해야 한다.
-
일반속성 바인딩 & 동적 속성 바인딩
위 예제 [ParentComp.vue 파일]에서 temp 의 경우, 숫자로 전달시키기 위해서는 temp="1990"는 문자열 "1990"으로 인식하므로 이를 숫자로 전달하기 위해서는 동적 속성바인딩을 이용하여, :temp="1990"와 같이 숫자로 전달시킨다.
-
단방향 데이터 흐름 모든 Props속성는 하위 속성과 상위 속성 사이의 단방향 바인딩을형성. => 하위 컴포넌트에서 Props를 변경할 경우 console창 Error !
: 하위 컴포넌트의 Props를 변경하고자 할 경우
- 자식컴포넌트의 Props를 data(로컬데이터 속성)으로 할당받아 사용.
- Propsr값에 의존하는 computed 속성으로 정의하여 사용.
- 배열 혹은 객체 (참조형 데이터)의 경우 값을 복사하여 사용.
- Props 검증
사용자로 부터 Props 에 대한 요구 사항을 지정한다. => [ChildComp.vue 파일]의 경우, parentName,temp,propMessage 데이터 타입(type), 사용자 정의 검증 (validator), 기본값(default), 반드시 전달되어야하는값 (required) 등을 설정할 수 있다.
export default {
props: {
parentName: {
type: String,
validator(v){
return v.trim() !== '';
}
},
temp: {
type: Number,
required: true,
},
propMessage: {
type: Array,
// required: false,
// 주의! 참조형 데이터의 경우, 함수를 통해 객체를 반환해야 한다.
default: function(){
return ['오늘도'];
}
}
}:자식이 $emit을 방출하는 순간. 커스텀이벤트를 방출하여 이를 감지한 부모가 이벤트 적용한다. :부모 컴포넌트는 자식 컴포넌트가 사용되는 템플릿에서 직접 v-on 을 사용하여 자식 컴포넌트에서 보내진 이벤트를 들을 수 있다.
- $on(이벤트 감지) & $emit(이벤트 트리거)
<!-- [Counter.vue 파일 내 methods] -->
methods: {
dispatchCount(){
this.$emit('changeCount',this.index,this.count);
}
} <!-- [TotalCounter.vue 파일] -->
<template lang="pug">
div.total-counter
div.total-count {{ total_count }}
counter(
v-for="(counter,index) in mine_counters"
v-on:changeCount="recieveCount"
)
</template>
<script>
import Counter from './Counter';
export default {
components: { Counter },
props: {
counters: {
type: Array,
required: true
},
appMood: String
},
computed:{
total_count(){
return this.mine_counters.reduce((prev,next)=>prev+next);
}
},
data () {
return {
mine_counters: this.counters.slice()
}
},
methods: {
recieveCount(index,count){
this.mine_counters.splice(index,1,count);
}
}
}=> [Counter.vue 파일 내 methods] 내에 정의된 dispatchCount메소드를 changeCount로 $emit 하여 [TotalCounter.vue 파일 ]내 template 코드에서 v-on:changeCount="recieveCount" 이를 감지하여 사용.
부모 / 자식 관계가 아닌 컴포넌트간 통신을 통해 상태를 관리하기 위한 가장 손쉽고 견고한 방법은 상태를 관리하는 객체를 사용하는 것이다. 이벤트 버스는 이벤트가 이동하는 통로와 비슷한 개념이다.
<!-- App.vue -->
<template lang="pug">
#app
otherparent
p
parent
child
</template>
<script>
import otherparent from './component/otherparent'
import parent from './component/parent'
import child from './component/child'
export default {
name: 'app',
components: {
otherparent,
parent,
child
}
}
</script><!-- parent.vue -->
<template lang="pug">
.parent(@click="callChild")
slot
| 부모
</template>
<script>
import Vue from 'vue';
import EventBus from '../EventBus'; // 이벤트버스를 불러온다.
export default {
methods:{
callChild(){
EventBus.$emit('call-child', 40); // 이벤트를 받아온다.
}
},
data () {
return {
}
}
}
</script><!-- child.vue -->
<template lang="pug">
//- 부모이벤트 캡쳐링이 일어나지 않도록 하는 방법.
.child(@click.stop="callChild")
slot
| 자식
</template>
<script>
import Vue from 'vue';
import EventBus from '../EventBus';
export default {
// otherparent.vue 에서 보낸 이벤트를 받는다.
methods:{
callChild(){
EventBus.$emit('call-child', 10);
// 이벤트버스를 받는다.
}
}
}
</script><!-- otherparent.vue -->
<template lang="pug">
.parent.is-other {{ payload }}
| 다른 부모야
slot
</template>
<script>
import Vue from 'vue';
import EventBus from '../EventBus';
export default {
// 이곳에서 직접 EventBus를 건다.
mounted(){
EventBus.$on('call-child', (payload)=>{ // on 이벤트를 이벤트버스에 건다.
this.payload = payload;
});
},
data(){
return {
payload: 0
}
}
}
</script>- template 과 같은 웹 컴포넌트 기술로 웹 컴포넌트 내에 있는 자리 표시자로 사용자가 별도의 DOM 트리를 만들고 함께 제시 할 수 있는 자체 마크업으로 채울 수 있게 하는 역할을 한다.
- slot 요소는 외부에서 전달된 HTML 코드가 삽입되는 투입구라고 할 수 있다.
- 하위 컴포넌트 템플릿에 최소한 하나의 slot 콘텐츠가 포함되어 있지 않으면 부모 콘텐츠가 삭제 되고 속성이 없는 슬롯이 하나뿐일 경우 전체 내용조각이 DOM의 해당 위치에 삽입되어 슬롯 자체를 대체한다.
- 특히 부모 컴포넌트가 항상 같지 않을 때 컴포넌트 구성에 있어서 상당히 유용하다
<!-- FormHelper.vue -->
<form class="form">
<h3>{{ title }}</h3>
<slot name="help"></slot>
<slot name="elements"></slot><!-- 자식요소가 들어온다 -->
<slot name="buttons"></slot>
</form>
<!-- App.vue -->
<form-helper title="Request Form">
<div slot="help">
매니저에게 요청하세요.
</div>
<div slot="elements">
<input type="text" name="item_name" value="">
<input type="text" name="item_value" value="">
</div>
<div slot="buttons">
<button type="button" name="button">Request</button>
</div>
</form-helper>- slot 엘리먼트는 특별한 속성인 name을 가지고 있다 이 속성은 어떻게 내용을 배포해야 하는지를 커스터 마이징 하는데 사용한다.
<!--아래 컨텐츠에 page 컴포넌트를 삽입-->
<aside>
<slot name="sidebar"></slot>
</aside>
<main>
<slot name="content"></slot>
</main>
<!--page 컴포넌트 -->
<!--named slot으로 컨텐츠가 어디에 들어갈지 결정한다 -->
<page>
<p slot="sidebar">This is sidebar content.</p>
<article slot="content"></article>
</page>
<!--결과 -->
<aside>
<p>This is sidebar content.</p>
</aside>
<main>
<article></article>
</main>- Scoped slot은 Vue.js 2.1.0 버전 부터 만들어진 새 기능으로 자식요소의 속성을 전달하고 부모로부터 접근할 수 있다.
- 스코프 슬롯은 이미 렌더링된 엘리먼트 대신 재사용 가능한 템플릿으로 작동하는 특별한 유형의 slot이다
- 하위 컴포넌트에서 속성을 컴포넌트에게 전달하는 것처럼 slot에게 데이터를 전달하기만 하면 된다.
- Scope 값은 자식으로부터 전달 된 props 객체를 담고 있는 임시 변수의 이름
// 자식요소에서 slot을 이용하여 데이터를 전달
const Child = {
data () {
return { msg: 'hello from child' }
},
template: `
<div class="child">
<slot :text="msg"></slot>
</div>
`
}
// 부모 요소
const Parent = {
components: { Child },
template: `
<div class="parent">
<child>
<template scope="props">
<span>hello from parent</span>
<span>{{ props.text }}</span>
</template>
</child>
</div>
`
}
//<!--렌더링 결과 -->
<div class="parent">
<div class="child">
<span>hello from parent</span>
<span>hello from child</span>
</div>
</div>- 자바스크립트에서는 배열 및 객체를 다른 변수에 할당하면 참조값이 할당된다. 하지만 종종 다른 작업을 위해 참조 없는 복사가 필요해 질때가 있다
- 객체 복제 하기
// Object.assign() 메소드 이용
var obj = {a: 1};
var clone = Object.assign({}, obj);
console.log(clone) //--> {a: 1};
// 메서드 사용
var cloneObj = function(obj){
return JSON.parse(JSON.stringify(obj));
};
var obj = {a: 1, b: 3};
// test
console.log(obj) // {a: 1, b: 3}
cloneObj(obj) // {a: 1, b: 3}- Array 복사
var arr = [1, 2 ,4];
var clone = arr.slice();
console.log(clone); // [1, 2, 4];