Vue 고급 - newlife-js/Wiki GitHub Wiki

Hacker News 구현

Hacker News
API

※ 함수 내 this == (브라우저 this / 객체 인스턴스의 this) / 비동기 this
함수 내 this는 해당 함수가 포함된 브라우저나 객체 인스턴스의 this와 같고,
비동기 호출 내부의 this는 객체 내에 있더라도 비동기 객체를 가리킨다.
단, 화살표 함수를 사용하면, 비동기 내부의 this도 객체의 인스턴스를 가리키게 된다.


※ Vuex에서는 action에서 바로 state를 변경하지 못하고, mutations을 거쳐야 가능하다.

action에서 mutations로 데이터를 넘길 때는 context 키워드를 사용한다.

export const store = new Vuex.Store({
    state: {
        new: [],
    },
    mutations: {
        SET_NEWS(state, news) {
            state.news = news;
        }
    },
    actions: {
        FETCH_NEWS(context) {
            fetchNewsList()
                .then((response) => context.commit('SET_NEWS', response.data))
                .catch((error) => console.log(error));
        },
    }
})

동적 라우트 매칭

<router-link> 태그 사용

@router
{
   path: '/user/:id',
   component: UserView,
}

@NewsView.vue
<router-link v-bind:to="`/user/${item.user}`">{{ item.user }}</router-link>

:id에 들어가는 값들은
this.$route.params.id와 같이 접근할 수 있음.
:variable을 여러개 사용할 수도 있음.

변수 내의 html 태그를 인식하는 방법

<div v-html="fetchedItem.content"/>

이벤트 버스

event emit을 받아서 뿌려주는 vue 객체

@bus.js

import Vue from 'vue';

export const bus = new Vue();

@App.vue

created() {
    bus.$on('start:spinner', this.startSpinner);
    bus.$on('end:spinner', this.endSpinner);
},
beforeDestroy() {
    bus.$off('start:spinner', this.startSpinner);
    bus.$off('end:spinner', this.endSpinner);
}

@NewsView.vue

created() {
    bus.$emit('start:spinner');
    this.$store.dispatch('FETCH_NEWS')
    .then(() => {
      console.log('fetched');
      bus.$emit('end:spinner');
    })
    .catch(error => console.log(error));
}

** ※ 왜인지 모르겠지만 WEBPACK 어쩌구 하면서 동작이 안됨. **
_utils_bus_js__WEBPACK_IMPORTED_MODULE_7___default.a.$emit is not a function

참고자료
@main.js

Vue.prototype.$bus = new Vue();

를 new Vue 위에 등록해주고, bus대신에 this.$bus를 사용해야 한다...
왜일까...

router 객체에서는 this.$bus가 안되어서... 다른 방법을 시도해보니,
bus 객체를 const로 바꿔주면 또 되네

HOC(High Order Component)

컴포넌트 로직을 재사용하기 위한 기술

router에 넘겨주는 component를 render해서 return하는 함수를 구현

@views/CreateListView.js

import ListView from './ListView.vue';

export default function createListView(name) {
  return {
    name: name,
    created() {
      this.$bus.$emit('start:spinner');
      this.$store.dispatch('FETCH_LIST', this.$route.name)
      .then(() => {
        console.log('fetched');
        this.$bus.$emit('end:spinner');
      })
      .catch(error => console.log(error));
    },
    render(createElement) {
      return createElement(ListView);
    }
  }
}

@routes/index.js

{
            path: '/news',
            name: 'news',
            component: createListView('NewsView'),
},
{
            path: '/ask',
            name: 'ask',
            component: createListView('AskView'),
},
{
            path: '/jobs',
            name: 'jobs',
            component: createListView('JobsView'),
},

※ 단점: 컴포넌트의 깊이가 깊어지면서 통신이 불편해짐...

Mixin

여러 컴포넌트 간에 공통으로 사용하고 있는 로직, 기능들을 재사용하는 방법

new Vue({
  mixins: [someMixin]
})

데이터 호출 시점

  1. 컴포넌트 라이프 사이클 훅 vuejs API 문서
  • created : 컴포넌트가 생성되자마자 호출되는 로직
  1. 라우터 내비게이션 가드 강의자료
  • 특정 URL로 접근하기 전의 동작을 정의하는 속성

to: 목표 url
from: 이전 url
next를 해줘야 routing 페이지로 넘어감

@routers/index.js

{
      path: '/news',
      name: 'news',
      component: createListView('NewsView'),
      beforeEnter: (to, from, next) => {

          next();
      }
},

Async & Await

기존의 비동기 처리 방식에 비해 직관적인 코딩 가능
promise의 .then().catch() 구문을 안 쓸 수 있음
예외처리는 try{ } catch {error} 로 대신함

async fuction fetchData()({
  try{ 
    var user = await getUserList(); // promise를 return하는 함수에만 await 사용할 수 있음.
  } catch (error) {
    console.log(error); 
  }
}

Plugin

모든 컴포넌트에 공통으로 적용하고 싶은 기능을 포함시키는 것
컴포넌트마다 동일 library를 import할 필요가 없도록 함

@main.js

Vue.use(플러그인);

@plugin.js

import chart from 'chart.js';

export default {
  install(Vue){
     Vue.prototype.$_Chart = Chart;
  }
}

Component Design Pattern

1. Common

props와 $emit 사용

2. Slot

화면별 component customize

3. Controlled

상위 컴포넌트에서 props 대신 v-model로 데이터를 내려보냄
하위 컴포넌트에서는 props로 'value'를 내려받고, $emit으로 'input'을 올려보낸다.

4. Renderless

표현하지 않는 컴포넌트(데이터 제공만 하는..)

** ※ render function **

render : function(createElement) { // createElement -> h : hyper script를 뜻함
  return createElement(App);
}

@fetchedData.vue

render() {
    // 하위 컴포넌트에서 상위컴포넌트로 올린다 $scopedSlots
    // slot -> v-slot // $scopedSlots.default -> $slots.default
    return this.$scopedSlots.default({
      response: this.response,
      loading: this.loading,
    });
},

@App.vue

<fetch-data url="https://jsonplaceholder.typicode.com/users/1">
  <!-- ... response, loading 을 가져올수 있다-->
  <div v-slot="{response, loading}">
    <div v-if="!loading">
      <p>{{response.name}}</p>
      <p>{{response.email}}</p>
    </div>
    <div v-if="loading">
      loading...
    </div>
  </div>
</fetch-data>
⚠️ **GitHub.com Fallback** ⚠️