뷰 비동기

in vue, vue 팁

비동기 갱신 큐

Vue는 비동기 적으로 DOM을 업데이트 합니다. 데이터의 변경이 발견 되면 큐를 열고 같은 이벤트 루프에서 발생한 변경된 모든 데이터를 버퍼에 담습니다. 같은 wather가 여러번 호출 되더라도 큐에서 한번만 푸시 됩니다. 이러한 동작은 불필요한 계산과 렌더링을 방지해줍니다. 그리고 다음 이벤트 루프인 “tick”에서 큐를 모두 비우고 실제 행동을 수행합니다.


예를 들어 vm.someData = 'new value' 라고 설정 할때, Vue는 바로 렌더링을 수행하지 않고 큐가 비워진 후인 다음 “tick”에서 업데이트 됩니다. 대부분의 상황에서는 이러한 작업이 영향을 받지 않지만, DOM state가 업데이트 된 후 작업을 수행하려는 겨우 신경을 써야 할수 있습니다. 데이터가 변경 된 후, DOM에 업데이가 완료되는 것을 기다리려면 데이터가 변경된 직후에 Vue.nextTick(callback)을 사용하면 됩니다. 콜백은 DOM이 업데이트 된 후 호출이 됩니다.


nextTick 사용하기

아래의 코드를 실행해보면 data가 변경 되더라도 실제로 바로 렌더링되지 않는 다는 것을 확인 할수 있습니다.
그리고 nextTic 메소드의 콜백함수가 DOM 업데이트가 완료 된 후 실행되었음을 확인할수 있습니다.

1
<div id="example">{{ message }}</div>
1
2
3
4
5
6
7
8
9
10
11
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
})

https://vuejs.org/v2/guide/reactivity.html를 참조하였습니다.

Comment and share

함수형 컴포넌트

어떠한 상태도 없고 라이프사이클 관련 메소드도 사용하지 않을때 지금까지 사용해왔었던 컴포넌트 생성 방법을 사용한다면 사용하지도 않는 메소드를 추가하게 됩니다. 함수형 컴포넌트를 사용하면 심플하게 작성할수 있습니다.

사용방법

functional 속성으로 사용

functional 속성을 사용하는 경우 template 태그를 사용할수 없습니다. 이때 render 함수를 사용 합니다.

1
2
3
4
5
6
7
8
<script>
export default {
functional : true,
render(h, context) {
// ...
}
}
</script>

장점

함수형 컴포넌트는 라이프 사이클 메소드를 가지지 않습니다. 이를 통해서 앱 퍼포먼스 향상 효과를 가질수 있습니다.

FunctionalView
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<template>
<div>
<template v-for="item in list">
<functional-component :key="item"/>
</template>
</div>
</template>

<script>
import FunctionalComponent from '../components/functional/FunctionalComponent.vue';

export default {
components: {
FunctionalComponent
},
data() {
return {
list: []
}
},
created() {
for (let i = 0; i < 1000; i++) {
this.list.push(i);
}
}
}
</script>

<style scoped>

</style>
FunctionalComponent
1
2
3
4
5
6
7
8
9
10
11
12
<script>
export default {
functional: true,
render(h) {
return h('div', '함수형 컴포넌트');
}
}
</script>

<style scoped>

</style>

함수형 컴포넌트를 1000개 생성하고 있습니다. 하지만 count는 1이라는 것을 확인할수 있습니다.

NoneFunctionalComponent

functional만 지우고 나머지는 위의 코드와 동일 합니다.

1
2
3
4
5
6
7
8
9
10
11
<script>
export default {
render(h) {
return h('div', '함수형 컴포넌트');
}
}
</script>

<style scoped>

</style>


SPA에서 functional 사용하기

2.5.0+ 이후로는 템플릿 기반의 함수형 컴포넌트를 정의할수 있습니다. template 태그에 functional 속성을 추가하면 동일하게 함수형 컴포넌트를 사용할수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
<template functional>
<div>함수형입니다.</div>
</template>
<script>
export default {
}
</script>

<style scoped>

</style>

Comment and share

Render 함수를 이용한 컴포넌트 작성

template을 사용하지 않고 render함수를 이용하여 컴포넌트를 작성 할수 있습니다. 이때 createElement 함수를 사용하여 작성합니다.

기본 구성

컴포넌트 작성시의 차이점은 template 속성을 사용 하는 대신에 render 함수를 사용한다는 차이점 뿐입니다. render함수는 return으로 VNode를 반환해주기만 하면 됩니다. VNode 생성은 createElement함수를 호출하면 반환해 줍니다(this.$createElement로도 동일하게 사용 가능).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export default {
name: '',
proprs: {},
created() {

},
data() {
return {}
},

// h를 쓰기도 하고 createElement라고 쓰기도 한다.
render(h) {
return h('div', ['값']);
}
}
createElement 함수 호출
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
createElement(
// {String | Object | Function}
// HTML 태그 이름(사용자가 만든 컴포넌트 태그 이름 포함), 컴포넌트 옵션 또는 함수 중
// 하나를 반환하는 함수. 필수사항
'div',

// {Object}
// 컴포넌트에서 사용할 속성 값을 설정합니다.
// 해당 값은 선택사항 입니다.
{

},

// {String | Array}
// VNode 자식들
// 하위 태그를 작성할때 사용 합니다.
// createElement()를 사용하거나, 문자열을 입력 가능합니다.
// 해당 값은 선택사항 입니다.
[
'문자열',
createElement('h1', '옵션 사용 없이 바로 문자열')
]
)
1
2
3
4
5
6
7
8
9
createElement(
'div',
{
props
},
[
'문자열'
]
)

지금까지 확인결과 VNode 자식값을 사용할시에 배열로 넘기자.

데이터 객체

createElement의 두번째 파라미터로 사용 하는 데이터 객체는 컴포넌트에 속성을 정의 할때 사용하게 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
// v-bind:class와 동일
'class': {
foo: true,
bar: false
},

// v-bind:style와 동일
style: {
color: 'red'
},

// 일반 HTML 속성을 정의할때 사용
attrs: {
id: 'foo'
},

// DOM 속성을 정의할때 사용
domProps: {
innerHTML: 'baz'
},

// v-on:click 등과 같은 역할을 합니다.
on: {
click: this.handlerClcik
},

// 다른 컴포넌트의 slot으로 들어가는 컴포넌트인 경우 슬롯의 이름을 여기에 적어줍니다.
slot: 'slot-name',

// ref를 지정하고 싶으면 해당 key를 사용합니다.
ref: 'refValue'
}

Comment and share

Vue 컴포넌트 작성

Vue에서는 템플릿을 통해서 HTML을 작성하는 것을 권장하고 있습니다. 하지만 특정 상황에서는 자바스크립트를 이용해야 하는 상황이 있습니다. 그렇기 때문에 템플릿 뿐 아니라 자바스크립트로도 컴포넌트를 작성하는 방법에 대해서 알고 있어야 합니다.

싱글 파일 컴포넌트

template, script, style 3개의 태그를 이용하여 하나의 파일에서 작성 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<div>
{{ str }}
</div>
</template>

<script>
export default {
name: 'VueSingleFileComponent',
props: {
str: {
type: String
}
}
}
</script>

<style scoped>

</style>
사용하기

다른 컴포넌트에서 import를 통해 사용할수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<vue-string-file-component/>
</template>

<script>
import VueSingleFileComponent from './VueSingleFileComponent.vue';

export default {
name: 'VueView',
components: {
VueSingleFileComponent
}
}
</script>

<style>
</style>

Vue.component를 통해서 등록하면 실제 사용하는 곳에서 import를 할 필요 없이 사용할수 있습니다.

main.js
1
2
3
4
import Vue from 'vue';
import VueSingleFileComponent from './VueSingleFileComponent.vue';

Vue.component('vue-single-file-component', VueSingleFileComponent);
VueView.vue

컴포넌트를 import 하거나 components 속성을 등록 할 필요 없이 사용가능합니다.

1
2
3
4
5
6
7
8
9
10
11
12
<template>
<vue-string-file-component/>
</template>

<script>
export default {
name: 'VueView',
}
</script>

<style>
</style>

String으로 컴포넌트 작성

템플릿을 js의 String으로 정의하는 방법입니다. 위 처럼 vue 확장자를 가진 파일을 따로 생성하지 않고 Vue.component에 직접 등록 합니다.

1
2
3
4
5
6
7
8
9
10
import Vue from 'vue';

Vue.component('vue-string', {
template: '<div> {{ str }}</div>',
props: {
str: {
type: String
}
}
})

Vue.component를 사용하기 때문에 import로 호출만 하고 compoents 속성에 따로 등록 하진 않아도 됩니다.

1
2
3
4
5
6
7
8
<template>
<vue-string/>
</template>
import '../components/vue-render/VueString.js';

export default {
name: 'VueView',
}

Render function을 사용하여 컴포넌트 작성

template을 사용하지 않고 render 함수를 이용해서 컴포넌트를 작성 할수 있습니다. 해당 컴포넌트를 import해서 사용방법은 SPA랑 동일 합니다.

1
2
3
4
5
6
7
8
9
10
11
export default {
name: "VueRenderFunc",
props: {
str : {
type: String
}
},
render(h) {
return h('div', [this.str]);
}
}

결론

기본적으로 SPA 방식으로 작성하는 게 좋다고 생각합니다. js를 활용해야 하는 경우에는 render 함수를 이용하여 작성하는 것도 하나의 선택이 될수 있을거라 생각합니다.

Comment and share

데이터 흐름의 처리

1. vuex를 최대한 활용
1
흐름 : 부모 => actions => API => mutations => state => 자식에서 state 접근

이 방법은 vuex를 최대한 활용 하는 방법으로 데이터를 표현하는 곳에서 직접 state에 접근 하는 방법이다. 이때 데이터를 표현하는 곳에서는 actions를 호출하지 않는다.
내 생각에 actions에 대한 결합도가 높아지기 때문인듯하다(해당 컴포넌트의 범용성이 낮아진다).

2. props 활용
1
흐름 : 부모 => actions => API => mutations => state => 부모에서 state에 접근 후 props로 전달 => 자식

부모에서 API 호출 및 값을 가져와서 자식에게 props로 넘겨준다.

컴포넌트 태그처럼 명시적으로 표현하려면 이걸 쓰자.

Comment and share

vuex의 기본

in vue, vuex

vuex의 설치 및 시작

기본구성

설치

1
npm i vuex

vuex 구성

라우터와 마찬가지로 main.js에 직접 구성하는 것은 좋지 못하므로 다른 디렉토리를 구축해서 코드 구성은 해당 파일에서 작성하여 import만 해오자.

vuex의 정의

vuex란 간단히 말해 상태관리도구이다. 애플리케이션의 모든 컴포넌트에 대한 중앙 집중식 저장소 역할을 한다. 그래서 어느 컴포넌트에서나 쉽게 상태에 접근할수 있다.

simple vuex 코드

1
2
3
4
5
6
7
8
9
10
import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

new Vuex.Store({
state: {
array: []
}
});

main.js

라우터와 마찬가지로 main.js에서 직접 Vuex를 생성하지 않고 따로 작성된 js에서 import 받아와서 사용한다.

1
2
3
4
5
6
import {store} from 'store/index.js';

new Vue({
render: h => h(App),
store
}).$mount('#app');

프로퍼티

1. state

state는 말그대로 상태를 저장하고 있다.

1
2
3
4
5
6
7
let vuex = {
state : {
array: [],
data: {},
str: ""
}
}
2. actions

api와의 통신 역할을 담당한다. api 호출 코드를 여기에서 작성 하면 된다. action에 정의 된 메소드라는 것을 표시하기 위해 대문자 스네이크 표기법을 강의에서는 사용했었다.

1
2
3
4
5
6
7
8
9
10
var actions = {
state: {},
actions: {
FETCH_DATA(context, param) {
fetchDataList()
.then()
.catch()
}
}
}

component에서 actions에 정의된 메소드 호출하기

1
2
this.$store.disaptch('메소드명');
this.$store.disaptch('메소드명', 값); // 값은 오직 하나만 전달 가능하므로 여러개를 전달하려면 객체를 통해 전달한다.

actions에서 api통신을 한 결과를 state에 전달하여야 하는데 이때 actions 내부에서 바로 state에 접근이 불가능하다. mutations라는 것을 통해서만 가능하다.
actions에 정의된 메소드의 파라미터로 context값이 넘어오는데 context의 commit 메소드를 호출하면 mutation에 전달가능하다.

1
2
3
4
5
6
7
8
9
10
11
12
var actions = {
state: {},
actions: {
FETCH_DATA(context, param) { // param에 dispatch를 실행할때 넘겼던 값을 받을수 있다.
fetchDataList()
.then(({ data }) => {
context.commit('mutation메소드명', '전달할 값')
})
.catch()
}
}
}
3. mutations

actions에서 backend(api)와이 실행 결과를 state에 저장할때 사용되는 중간 연결자이다. actions에서 context.commit으로 실행될 메소드를 정의한다.

1
2
3
4
5
6
7
8
9
10
11
var mutations = {
state: {
data: {}
},
actions: {},
mutations: {
SET_DATA(state, data) {
state.data = data;
}
}
}

첫번째 파라미터는 state가 넘어오고 두번째 파라미터는 commit 호출시에 넘겨준 파라미터가 넘어온다.

vue에서 store 접근

1. 가장 원시적인 방법

아래는 자바스크립트에서 사용방법

1
this.$store.state.프로퍼티

아래는 vue의 template에서 사용방법

1
<div>{{ this.$store.state.프로퍼티 }}</div>

위의 방법은 Vuex를 모듈방식으로 작성하기 시작하면 참조가 너무 길어진다.(a.b.c.d.e.f.g.value)

2. computed를 이용한 방법
1
2
3
4
5
6
7
var Vue = {
computed: {
state() {
return this.$store.state
}
}
}

단순하게 vue의 computed를 이용해서 공통된 부븐을 메소드에 저장하는 방법이다.

3. vuex의 mapState를 이용
1
2
3
4
5
6
7
8
9
import { mapState } from 'vuex';

var Vue = {
computed: {
...mapState({
value: state => state.value
})
}
}

vuex에서 제공하는 mapState를 활용하는 방법으로 파라미터로 state가 넘어오다보니 위와 마찬가지로 모듈이 많아지게 되면 state.a.b.c.d.value로 길어질수 있다.

4. vuex의 mapGetter를 이용(mapGetters의 배열을 이용하는 방법)
1
2
3
4
5
6
7
8
9
// vuex 파일

new Vuex.Store({
getters: {
value(state) {
return state.value;
}
}
})
1
2
3
4
5
6
7
8
// state 값 사용 할 파일
import { mapGetters } from 'vuex';

var Vue = {
computed: {
...mapGetters(['value'])
}
}

getters는 computed와 비슷한 역할을 한다. 컴포넌트에서는 mapGetter에서 배열을 넘겨준다(이때 배열의 문자열이 vuex의 getters에 정의 된 메소드 이름을 넘겨주면 해당값을 받아온다. getters에 등록 하는 번거로움이 있지만 사용할때는 변수명이 명료해서 보기 좋다.

5. vuex의 mapGetter를 이용(mapGetters의 객체를 이용하는 방법)

vuex 파일의 사용법은 위와 동일하다.

1
2
3
4
5
6
7
8
9
10
// state 값 사용 할 파일
import { mapGetters } from 'vuex';

var Vue = {
computed: {
...mapGetters({
myValue: 'value'
})
}
}

배열을 이용할때는 변수명을 마음대로 지정이 불가능하지만 객체를 이용하면 이름을 컴포넌트에서 마음대로 지정 할수 있다.

모듈화

하나의 파일에서 모든 데이터를 관리하면 알아보기 힘들기 때문에 모듈화 작업을 한다. mutations, actions, state, getters를 따로 파일을 만들어서 관리한다. 상황에 따라 분리 할수도 있고 길지 않다면 하나의 파일에서 관리할수 있고 더 세분화로 분리할수도 있다.

Comment and share

하이오더컴포넌트 기본사용 방법

정의

Mixin와 마찬가지로 컴포넌트의 로직을 재사용하기 위한 기술이다.

방법

1. 공통으로 사용할 부분을 js로 따로 만든다.
2. 함수를 하나 선언하고 return 해준다.
3. return에 들어가는 것 : render, 나머지 공통으로 사용되는 부분 작성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import Component from 'component.vue';

export default function hoc(componentName) {
return {
name: componentName, // 해당 이름으로 컴포넌트 생성됨
computed() {

},
data() {
return {

}
},
runder(h) {
return h(Component);
}
}
}

render 부분은 공통으로 사용되는 템플릿 부분을 사용하기 위한 것이다.

단점

HOC를 사용하게 되면 depth가 하나 추가된다. 이는 많이 사용하게 되면 컴포넌트의 깊이가 깊어져서 데이터 통신이 어려워진다.

1
2
일반구조 : 상위 - 하위
HOC구조 : 상위 - HOC - 하위

vue에서는 mixins와 scoped slotㅇ을 활용하고 함수형 프로그래밍이나 컴포넌트 재사용성을 극대화 하고 싶다면 HOC 활용하는것도 좋을거 같다.

Comment and share

Minxin 기본 사용방법

정의

믹스인은 여러 컴포넌트 간에 공통으로 사용하고 있는 로직, 기능들을 재사용하는 방법이다. 믹스인에서 정의할수 있는 재사용 로직은 data, methods, crated 등과 같은 컴포넌트 옵션이다.

사용방법

1. js 파일에서 객체를 반환
1
2
3
4
5
6
7
8
9
10
11
export default {
computed() {

},

data() {
return {

}
}
}
1
2
3
4
5
import Mixin from 'Mixin경로'

export default {
mixins: [Mixin]
}

Comment and share

라우터 네비게이션 가드

데이터를 호출 시점은 여러가지가 존재한다. 대표적으로 라이프 사이클 중 하나인 created와 지금 소개할 라우터 네이게이션 가드가 존재한다. 상황에 따라
적절한 위치에서 데이터를 호출 해서 사용 하면 된다.

정의

라우터 네비게이션 가드란 vue-router가 제공하는 기능으로 주로 리다이렉션 또는 라우터 이동을 취하여서 네비게이션을 보호하는데 사용한다. 라우터 네비게이션 가드는
created보다 먼저 호출이 된다.

사용방법

라우터에서 직접 사용

1
2
3
4
5
6
7
8
9
10
11
var routed = {
routes : [{
path: 'list',
name: 'list',
beforeEnter: (to, from, next) => {
console.log('to', to); // 현재 이동하고자 하는 url 정보
console.log('from', from); // 현재 url 정보
console.log('next', next); // next함수를 호출해야 이동하고자 하는 url로 접속 가능
}
}]
}

params 또는 쿼리의 변경에 의해서는 가드가 실행되지 않는다.

next의 사용

1. next()

네이게이션을 승인 해준다. 라우터의 URL 이동을 허락 해준다.

2. next(false)

네이게이션을 중단한다. from 경로의 URL로 재설정된다.

3. next(‘/‘) or next({path : ‘/‘})

다른 위치로 리다이렉션 해 준다.

4. next(error)

next에 전달된 인자가 Error의 인스턴스이면 탐색 중단 및 router.onError()를 이용해 등록 된 콜백에 에러가 전달 된다.

가드의 종류

1. 전역 가드 : beforeEnter
2. 글로벌 가드 : beforeResolve, afterEach
3. 내부가드 : beforeRouteEnter, beforeRouteUpdate, beforeRouteLeave

내부가드는 router가 아닌 컴포넌트 안에 직접 네비게이션 가드를 정의할수 있다.

beforeRouterEnter는 해당 가드 생성시에 컴포넌트 생성 전이라 this에 접근이 불가능하다.

1
2
3
4
5
6
7
var component = {
beforeRouteEnter(to, from, next) {
next(vm => {
// 'vm'을 통한 컴포넌트 인스턴스 접근이 가능하다.
})
}
}

beforeRouteLeave는 this에 직접 접근이 가느하다. 사용자가 저장하지 않는 편집 내용을 두고 라우트를 떠나는 것을 방지할때 사용한다.

전체 네비게이션 시나리오

1
2
3
4
5
6
7
8
9
10
11
12
1) 네비게이션이 트리거됨.
2) 비활성화될 컴포넌트에서 가드를 호출.
3) 전역 beforeEach 가드 호출.
4) 재사용되는 컴포넌트에서 beforeRouteUpdate 가드 호출. (2.2 이상)
5) 라우트 설정에서 beforeEnter 호출.
6) 비동기 라우트 컴포넌트 해결.
7) 활성화된 컴포넌트에서 beforeRouteEnter 호출.
8) 전역 beforeResolve 가드 호출. (2.5이상)
9) 네비게이션 완료.
10) 전역 afterEach 훅 호출.
11) DOM 갱신 트리거 됨.
12) 인스턴스화 된 인스턴스들의 beforeRouteEnter가드에서 next에 전달 된 콜백을 호출합니다.

Comment and share

라우터 트랜지션

화면 전환시에 좀더 부드럽게 이동하도록 해준다. 실제로 화면전환시에 애니메이션을 넣어주는 건 아니고 화면전환시마다 적절한 class를 부여해주는데
해당 class에 css로 직접 원하는 애니메이션을 작성하면 된다.

1
2
3
<transition>
<router-view></router-view>
</transition>
  1. router-view 태그를 transition 태그로 감싼다.
  2. transition은 라우터 트랜지션 이외에도 사용가능하다.

Comment and share

Moon Star

author.bio


author.job