상태 관리 도구 / 여러 컴포넌트가 공유되는 데이터 속성
- state : 컴포넌트 간에 공유할 data
- view : 데이터가 표현될 template
- actions : 사용자의 입력에 따라 반응할 methods
npm i vuex@3.6.2
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
news: []
}
});
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')
api를 vuex에서 호출해야한다고 하면 actions를 사용해야한다.
vue components에서 Dispatch라는 api를 이용해서 actions를 호출할 수 있음.
그러면 actions가 backend API를 가져와 mutations에 넘겨줌.
비동기호출은 actions에서 담당
Mutations 이란 Vuex 의 데이터, 즉 state 값을 변경하는 로직들을 의미한다.
Getters 와 차이점은
1. 인자를 받아 Vuex 에 넘겨줄 수 있고
2. computed 가 아닌 methods 에 등록
또한, 다음 챕터에 나올 Actions 와의 차이점이다.
- Mutations 는 동기적 로직을 정의
- Actions 는 비동기적 로직을 정의
Mutations 의 성격상 안에 정의한 로직들이 순차적으로 일어나야 각 컴포넌트의 반영 여부를 제대로 추적할 수가 있기 때문이다.
<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
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);
})
}
}
});
AskView, JobsView를 vuex 방식으로 바꿔라
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);
})
}
}
});
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>
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);
})
}
}
});
<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>
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);
})
}
}
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,
});
<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>
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,
}
]
});
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>
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,
}
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);
});
},
}
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
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,
});
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)를 이용해서 페이지의 데이터를 표시
<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>
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>
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
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);
});
}
}
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,
});
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;
}
}