Vue.js_5일차(완벽가이드)

써니·2022년 12월 30일
0

vue.js

목록 보기
5/15

Vue.js

Vuex

상태 관리 도구 / 여러 컴포넌트가 공유되는 데이터 속성

  • state : 컴포넌트 간에 공유할 data
  • view : 데이터가 표현될 template
  • actions : 사용자의 입력에 따라 반응할 methods

Vuex 설치

npm i vuex@3.6.2

📂 store

  • index.js
import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

export const store = new Vuex.Store({
  state: {
    news: []
  }
});
  • main.js
import Vue from 'vue'
import App from './App.vue'
import { router } from './router/index.js';
import { store } from './store/index.js';

Vue.config.productionTip = false

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


Vuex - actions

api를 vuex에서 호출해야한다고 하면 actions를 사용해야한다.
vue components에서 Dispatch라는 api를 이용해서 actions를 호출할 수 있음.
그러면 actions가 backend API를 가져와 mutations에 넘겨줌.
비동기호출은 actions에서 담당

Mutations

Mutations 이란 Vuex 의 데이터, 즉 state 값을 변경하는 로직들을 의미한다.
Getters 와 차이점은
1. 인자를 받아 Vuex 에 넘겨줄 수 있고
2. computed 가 아닌 methods 에 등록
또한, 다음 챕터에 나올 Actions 와의 차이점이다.

  • Mutations 는 동기적 로직을 정의
  • Actions 는 비동기적 로직을 정의
    Mutations 의 성격상 안에 정의한 로직들이 순차적으로 일어나야 각 컴포넌트의 반영 여부를 제대로 추적할 수가 있기 때문이다.
  • NewsView.vue
<template>
  <div>
    <div v-for="(user, index) in this.$store.state.news" :key="index">{{ user.title }}</div>
  </div>
</template>

<script>
// import { fetchNewsList } from '../api/index.js';

export default {
  created() {
    this.$store.dispatch('FETCH_NEWS');
    // console.log('호출 전->',this);
    // fetchNewsList()
    // .then(response => {
    //   console.log('호출 후->',this);
    //   this.users = response.data;
    // })
    // .catch(function(error){
    //   console.log(error);
    // });
  },
}
</script>

<style>

</style>

SET_NEWS -> news = response.data

  • index.js
import Vue from 'vue';
import Vuex from 'vuex';
import { fetchNewsList } from '../api/index.js';

Vue.use(Vuex);

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


(실습) Vuex

AskView, JobsView를 vuex 방식으로 바꿔라

  • index.js
import Vue from 'vue';
import Vuex from 'vuex';
import { fetchAskList, fetchJobsList, fetchNewsList } from '../api/index.js';

Vue.use(Vuex);

export const store = new Vuex.Store({
  state: {
    news: [],
    ask: [],
    jobs: [],
  },
  mutations:{
    SET_NEWS(state, news) {
        state.news = news; // state 변경 가능
    },
    SET_ASK(state, ask) {
        state.ask = ask;
    },
    SET_JOBS(state, jobs) {
        state.jobs = jobs;
    }
  },
  actions: {
    FETCH_NEWS(context) {
        fetchNewsList()
        .then(response=> {
            console.log(response.data);
            context.commit('SET_NEWS', response.data);
        })
        .catch(error=>{
            console.log(error);
        })
    },
    FETCH_ASK(context) {
        fetchAskList()
        .then(response => {
            context.commit('SET_ASK', response.data);
        })
        .catch(error=>{
            console.log(error);
        })
    },
    FETCH_JOBS({commit}) {
        fetchJobsList()
        .then(({ data })=>{
            commit('SET_JOBS', data);
        })
        .catch(error=> {
            console.log(error);
        })
    }
  }
});

mapState 사용

  • AskView.vue

    ask에 state.ask를 담음

<template>
  <div>
    <div v-for="(user, index) in ask" :key="index">{{ user.title }}</div>
  </div>
</template>

<script>
import { mapState } from 'vuex';

export default {
  computed: {
    ...mapState({
      ask: state => state.ask
    }),
  },
  created() {
    this.$store.dispatch('FETCH_ASK');
  },
}
</script>

<style>

</style>


Getters 사용

  • index.js
import Vue from 'vue';
import Vuex from 'vuex';
import { fetchAskList, fetchJobsList, fetchNewsList } from '../api/index.js';

Vue.use(Vuex);

export const store = new Vuex.Store({
  state: {
    news: [],
    ask: [],
    jobs: [],
  },
  getters:{
    fetchedAsk(state) {
        return state.ask;
    }
  },
  mutations:{
    SET_NEWS(state, news) {
        state.news = news; // state 변경 가능
    },
    SET_ASK(state, ask) {
        state.ask = ask;
    },
    SET_JOBS(state, jobs) {
        state.jobs = jobs;
    }
  },
  actions: {
    FETCH_NEWS(context) {
        fetchNewsList()
        .then(response=> {
            console.log(response.data);
            context.commit('SET_NEWS', response.data);
        })
        .catch(error=>{
            console.log(error);
        })
    },
    FETCH_ASK(context) {
        fetchAskList()
        .then(response => {
            context.commit('SET_ASK', response.data);
        })
        .catch(error=>{
            console.log(error);
        })
    },
    FETCH_JOBS({commit}) {
        fetchJobsList()
        .then(({ data })=>{
            commit('SET_JOBS', data);
        })
        .catch(error=> {
            console.log(error);
        })
    }
  }
});
  • AskView.vue
<template>
  <div>
    <div v-for="(user, index) in fetchedAsk" :key="index">{{ user.title }}</div>
  </div>
</template>

<script>
import { mapGetters } from 'vuex';
// import { mapState } from 'vuex';

export default {
  computed: {
    // 배열 표기법
    ...mapGetters([
      'fetchedAsk'
    ]),
    
    // #3
    // ...mapGetters({
    //   fetchedAsk: 'fetchedAsk'
    // }),


    // #2
    // ...mapState({
    //   fetchedAsk: state => state.ask
    // }),
   
   // #1
   // ask() {
    //   return this.$store.state.ask;
    // }
  },
  created() {
    this.$store.dispatch('FETCH_ASK');
  },
}
</script>

<style>

</style>

Mutations/Actions 모듈화

  • actions.js
import { fetchAskList, fetchJobsList, fetchNewsList } from '../api/index.js';

export default {
    FETCH_NEWS(context) {
        fetchNewsList()
        .then(response=> {
            console.log(response.data);
            context.commit('SET_NEWS', response.data);
        })
        .catch(error=>{
            console.log(error);
        })
    },
    FETCH_ASK(context) {
        fetchAskList()
        .then(response => {
            context.commit('SET_ASK', response.data);
        })
        .catch(error=>{
            console.log(error);
        })
    },
    FETCH_JOBS({commit}) {
        fetchJobsList()
        .then(({ data })=>{
            commit('SET_JOBS', data);
        })
        .catch(error=> {
            console.log(error);
        })
    }
  }
  • mutations.js
export default {
    SET_NEWS(state, news) {
        state.news = news; // state 변경 가능
    },
    SET_ASK(state, ask) {
        state.ask = ask;
    },
    SET_JOBS(state, jobs) {
        state.jobs = jobs;
    }
  }

-index.js

import Vue from 'vue';
import Vuex from 'vuex';
import mutations from './mutations.js';
import actions from './actions.js';

Vue.use(Vuex);

export const store = new Vuex.Store({
  state: {
    news: [],
    ask: [],
    jobs: [],
  },
  getters:{
    fetchedAsk(state) {
        return state.ask;
    }
  },
  mutations,
  actions,
});

중간 스타일링

  • NewsView.vue
<template>
  <div>
    <p v-for="(item, index) in this.$store.state.news" :key="index">
      <a v-bind:href="item.url">
        {{ item.title }}
      </a>
      <small>{{ item.time_ago }} by {{ item.user }}</small>
    </p>
  </div>
</template>

<script>
// import { fetchNewsList } from '../api/index.js';

export default {
  created() {
    this.$store.dispatch('FETCH_NEWS');
  },
}
</script>

<style>

</style>




사용자 상세 정보페이지 이동

동적 라우트 매칭 원리 및 적용

Dynamic Route Matching

공식문서

  • index.js

    path를 /user/:id로 넘김

import Vue from 'vue'
import VueRouter from 'vue-router';
import NewsView from '../views/NewsView.vue';
import AskView from '../views/AskView.vue';
import JobsView from '../views/JobsView.vue';
import ItemView from '../views/ItemView.vue';
import UserView from '../views/UserView.vue';

Vue.use(VueRouter);

export const router = new VueRouter({
  mode: 'history',
  routes: [
    {
        path: '/',
        redirect: '/news',
    },
    {   
        // path: url 주소
        path: '/news',
        // component: url 주소로 갔을 때 표시될 컴포넌트
        component: NewsView,
    },
    {
        path: '/ask',
        component: AskView,
    },
    {
        path: '/jobs',
        component: JobsView,
    },
    {
        path: '/user/:id',
        component: UserView,
    },
    {
        path: '/item',
        component: ItemView,
    }
  ]
});
  • NewsView.vue

    router-link를 통해 이동함
    이동할 링크에 아이디를 담아서 이동함
    그러면 router에서 :id로 받아서 넘기기 때문에
    파라미터에 id라는 이름으로 담김

<template>
  <div>
    <p v-for="(item, index) in this.$store.state.news" :key="index">
      <a v-bind:href="item.url">
        {{ item.title }}
      </a>
      <small>
        {{ item.time_ago }} by 
        <!-- <router-link v-bind:to="'/user/' + item.user">{{ item.user }}</router-link> -->
        <router-link v-bind:to="`/user/${item.user}`">{{ item.user }}</router-link>
      </small>
    </p>
  </div>
</template>

<script>
// import { fetchNewsList } from '../api/index.js';

export default {
  created() {
    this.$store.dispatch('FETCH_NEWS');
  },
}
</script>

<style>

</style>


  • index.js

    api 주소 설정

import axios from 'axios';

const config = {
    baseUrl: 'https://api.hnpwa.com/v0/'
}

function fetchNewsList() {
    // return axios.get(config.baseUrl + 'news/1.json');
    return axios.get(`${config.baseUrl}news/1.json`);
}

function fetchAskList() {
    return axios.get(`${config.baseUrl}ask/1.json`);
}

function fetchJobsList() {
    return axios.get(`${config.baseUrl}jobs/1.json`);
}

function fetchUserInfo(username) {
    return axios.get(`${config.baseUrl}user/${username}.json`)
}

export {
    fetchNewsList,
    fetchAskList,
    fetchJobsList,
    fetchUserInfo,
}

  • actions.js

    UserView.js에서 const userName을 dispatch에서 같이 보냄

import { fetchAskList, fetchJobsList, fetchNewsList, fetchUserInfo } from '../api/index.js';

export default {
    FETCH_NEWS(context) {
        fetchNewsList()
        .then(response=> {
            console.log(response.data);
            context.commit('SET_NEWS', response.data);
        })
        .catch(error=>{
            console.log(error);
        })
    },
    FETCH_ASK(context) {
        fetchAskList()
        .then(response => {
            context.commit('SET_ASK', response.data);
        })
        .catch(error=>{
            console.log(error);
        })
    },
    FETCH_JOBS({commit}) {
        fetchJobsList()
        .then(({ data })=>{
            commit('SET_JOBS', data);
        })
        .catch(error=> {
            console.log(error);
        })
    },
    FETCH_USER({commit}, name) {
        fetchUserInfo(name)
        .then(({data}) => {
            commit('SET_USER', data);
        })
        .catch(error => {
            console.log(error);
        });
    },
  }

  • mutations.js
export default {
    SET_NEWS(state, news) {
        state.news = news; // state 변경 가능
    },
    SET_ASK(state, ask) {
        state.ask = ask;
    },
    SET_JOBS(state, jobs) {
        state.jobs = jobs;
    },
    SET_USER(state, user) {
        state.user = user;
    }
  }

📂store

  • index.js
import Vue from 'vue';
import Vuex from 'vuex';
import mutations from './mutations.js';
import actions from './actions.js';

Vue.use(Vuex);

export const store = new Vuex.Store({
  state: {
    news: [],
    ask: [],
    jobs: [],
    user: [],
  },
  getters:{
    fetchedAsk(state) {
        return state.ask;
    }
  },
  mutations,
  actions,
});

  • UserView.vue

    computed를 사용해서 받아온 값의 객체를 간단하게 표현

<template>
  <div>
    <p>name: {{ userInfo.id}}</p>
    <p>karam: {{ userInfo.karma}}</p>
    <p>created: {{ userInfo.created}}</p>
  </div>
</template>

<script>

export default {
  computed: {
    userInfo() {
      return this.$store.state.user;
    }
  },
  created() {
    console.log(this.$route.params.id);
    const userName = this.$route.params.id;
    this.$store.dispatch('FETCH_USER', userName);
    // axios.get(`https://api.hnpwa.com/v0/user/${userName}.json`);
  },
}
</script>

<style>

</style>

(실습) 질문 내용 상세페이지

실습 순서
1. ItemView.vue를 생성
2. 라우터에 ItemView로 갈 수 있는 라우터를 정보를 등록
3. 해당 페이지 컴포넌트로 이동했을 때 받아온 params(id)를 이용해서 페이지의 데이터를 표시


  • AskView.js
<template>
  <div>
    <p v-for="(item, index) in fetchedAsk" :key="index">
      <router-link v-bind:to="`/item/${item.id}`">{{ item.title }}</router-link>
      <small>{{ item.time_ago }} by {{ item.user }}</small>
    </p>
  </div>
</template>

<script>
import { mapGetters } from 'vuex';
// import { mapState } from 'vuex';

export default {
  computed: {
    // 배열 표기법
    ...mapGetters([
      'fetchedAsk'
    ]),
    
    // #3
    // ...mapGetters({
    //   fetchedAsk: 'fetchedAsk'
    // }),


    // #2
    // ...mapState({
    //   fetchedAsk: state => state.ask
    // }),
   
   // #1
   // ask() {
    //   return this.$store.state.ask;
    // }
  },
  created() {
    this.$store.dispatch('FETCH_ASK');
  },
}
</script>

<style>

</style>

  • ItemView.js

    created() 설정 후 마지막에 computed

<template>
  <div>
    <p>name: {{ itemInfo.id}}</p>
    <p>title: {{ itemInfo.title}}</p>
    <p>points: {{ itemInfo.points}}</p>
    <p>user: {{ itemInfo.user}}</p>
  </div>
</template>

<script>
export default {
  computed: {
    itemInfo() {
      return this.$store.state.item;
    }
  },
  created() {
    console.log(this.$route.params.id);
    const itemId = this.$route.params.id;
    this.$store.dispatch('FETCH_ITEM', itemId);
  }
}
</script>

<style>

</style>

📂api - index.js
import axios from 'axios';

const config = {
    baseUrl: 'https://api.hnpwa.com/v0/'
}

function fetchNewsList() {
    // return axios.get(config.baseUrl + 'news/1.json');
    return axios.get(`${config.baseUrl}news/1.json`);
}

function fetchAskList() {
    return axios.get(`${config.baseUrl}ask/1.json`);
}

function fetchJobsList() {
    return axios.get(`${config.baseUrl}jobs/1.json`);
}

function fetchUserInfo(username) {
    return axios.get(`${config.baseUrl}user/${username}.json`);
}

function fetchCommentInfo(id) {
    return axios.get(`${config.baseUrl}item/${id}.json`);
}

export {
    fetchNewsList,
    fetchAskList,
    fetchJobsList,
    fetchUserInfo,
    fetchCommentInfo,
}

📂store

  • action.js
import { fetchAskList, fetchCommentInfo, fetchJobsList, fetchNewsList, fetchUserInfo } from '../api/index.js';

export default {
    FETCH_NEWS(context) {
        fetchNewsList()
        .then(response=> {
            console.log(response.data);
            context.commit('SET_NEWS', response.data);
        })
        .catch(error=>{
            console.log(error);
        })
    },
    FETCH_ASK(context) {
        fetchAskList()
        .then(response => {
            context.commit('SET_ASK', response.data);
        })
        .catch(error=>{
            console.log(error);
        })
    },
    FETCH_JOBS({commit}) {
        fetchJobsList()
        .then(({ data })=>{
            commit('SET_JOBS', data);
        })
        .catch(error=> {
            console.log(error);
        })
    },
    FETCH_USER({commit}, name) {
        fetchUserInfo(name)
        .then(({data}) => {
            commit('SET_USER', data);
        })
        .catch(error => {
            console.log(error);
        });
    },
    FETCH_ITEM({commit}, id) {
        fetchCommentInfo(id)
        .then(({data}) =>{
            commit('SET_ITEM', data);
        })
        .catch(error=>{
            console.log(error);
        });
    }
  }
  • index.js
import Vue from 'vue';
import Vuex from 'vuex';
import mutations from './mutations.js';
import actions from './actions.js';

Vue.use(Vuex);

export const store = new Vuex.Store({
  state: {
    news: [],
    ask: [],
    jobs: [],
    user: {},
    item: [],
  },
  getters:{
    fetchedAsk(state) {
        return state.ask;
    }
  },
  mutations,
  actions,
});
  • mutations.js
export default {
    SET_NEWS(state, news) {
        state.news = news; // state 변경 가능
    },
    SET_ASK(state, ask) {
        state.ask = ask;
    },
    SET_JOBS(state, jobs) {
        state.jobs = jobs;
    },
    SET_USER(state, user) {
        state.user = user;
    },
    SET_ITEM(state, item) {
        state.item = item;
    }
  }

0개의 댓글