Vue.js 중급 메모

Jinmin Kim·2020년 12월 19일
0

Vue.js 중급강좌 인프런 메모

Todo Project 및 ES6 정리

  1. 뷰 CLI 이용한 프로젝트 구성 방법

  2. 컴포넌트 기반 설계 방법

  3. 컴포넌트 구조화 및 컴포넌트 통신 방법

  4. ES6를 이용한 효울적이고 간결한 뷰 코딩 방법

  5. Vuex를 이용한 상태 관리의 이해 및 적용 방법

  • 개발 환경 설정
    vetur , TSLint 설치

  • 최근의 CLI 방향
    npm install -g @vue/cli을 사용해서 3.0을 설치하고나면(지금은 4.0버전이엇다)
    webpack.config 파일이 없는것을 확인할수 있는데 이것은 입문자, 초보자들의
    config 파일 설정을 어렵게하지않아도 되게 만들어주려는 진입장벽을 낮추어주는 방법이었다.

Vetur를 사용하면 vue하고 Tab을 누르면 구조가 간단하게 만들어진다.

meta viewport -> 반응형웹 이라는것을 설정하는 태그이다.

favicon 생성 방법 ->

awesome icons에서 아이콘을 쉽게 가져올수있다

v-on:keyup.enter="" -> enter key를 눌렀을때 이벤트를 가져오게된다( @keyup.enter="")

ul>li라고 쳐서 tab을 치게되면 ul에 li태그가 만들어진다

할일완료 기능 구현-> 체크할때에 책과는 다른 기능이 추가됫다

localStorage는 Update가 되지않는다. 그래서 Update를 하기위해선 removeItem 이후 다시 setItem해줘야한다.

TodoApp Project 구현
github의 branch는 가지를 만들어서 그때의 코드나 상태를 확인하고싶을때
branch를 만들어서 저장시켜준다.

TodoApp Project 구현

github의 branch는 가지를 만들어서 그때의 코드나 상태를 확인하고싶을때
branch를 만들어서 저장시켜준다.

Code Refactory

여튼 vscode에서 [File]-[Preference]-[User Snippet]으로 들어가면 User의 스니펫을 만들어 줄수있다.

안티패턴?? -> 무엇인가

사용자 경험 개선

vue에서 alert이 아닌 제공하는 라이브러리를 사용할수잇는것중 modal component
vue.js 홈페이지의 learn -> Examples에 가면 내용을 확인할수있다.

** slot -> 특정 컴포넌트의 특정 UI부분을 변경할수있게 만드는것(다시 한번 재정의하게 만들어준다)
상위 컴포넌트에서 정의해둔곳에서 재정의를 하여서 나타낼수잇게 된다.(중요한 부분)

``
Vue Transitions
트렌지션 -> Vue 에서 제공하는 effect 효과?
1. 구현관점
-> 트렌지션을 입힐때는 클래스의 이름에 따라서 입힐수있다.
(transition-group vue에서 제공해주는 태그 name이 class와 관계가 있다.)

  1. 사용관점
    -> 디자이너 퍼플리셔가 좋아할만한 기능들이 프레임워크가 많이 들어있다.
    ``

ES6

Vue.js 코딩을 간편하게 해주는 부분
const & let, Arrow Function, Enhanced Object Literals, Modules

Babel
ES5를 transpiling을 해주는 컴파일러

const : 한번 선언값은 변경이 안된다.[const로 지정한 값은 변경이 불가능하지만, 객체나 배열의 내부는 변경이 가능하다.]
let: 한번 선언값은 다시 선언할수가 없다.

ES5 특징

  • 변수의 Scope
    기존 자바스크립트(ES5)는 {}에 상관없이 스코프가 설정된다
  • 호이스팅
    선언한함수와 변수를 해석기가 가장 상단에 있는것 처럼 인식한다
  • {} 단위로 변수의 범위가 제한될수있다.
function f(){
    {
        let x;
        {
            //새로운 블록안에 새로운 x의 스코프가 생기게된다.
            const x = "sneaky";
            x = "foo";
        }
        x = "bar";
        let x = "inner";
    }
}
  • Arrow Function - 화살표 함수
    function 이라는 키워드 대신 =>로 대체, 즉 콜백함수 문법을 간결하게 만든다.
function(){

}
() => {

}
  • 속성 메서드의 축약 특징
    var dictionary = {
    words: 100,
    //ES5
    lookup : function(){
    console.log("find words");
    },
    //ES6
    lookup(){
    console.log("find words");
    }
    }
    :function을 지우고 사용하면된다(개간단)

  • 속성명 축약 하기
    var figures = 10;
    var dictionary = {
    //figures : figures, //es5
    figures //es6
    }
    vscode 단축키 같은것을 고칠때에 컨트롤+D를 누르면 같은것에 대해서 선택이된다.

  • Modules(자바스크립트 모듈화 방법)
    Why? 사용하는가
    es5에서는 모듈화 방법이 없었다.
    code의 재사용성이 많은 부분을 재사용을 하기위해서 module을 만들어두고
    계속해서 재사용하기위해서 보통은 사용된다. -> Java에서 class로 나누어서 사용하는것 처럼

Vue.js에서의 export default
한개의 파일에서 한개만 export가 되게한다.
다른파일의 내용이 들어가는것이 아니라 딱 필요한 부분만 가져가기위해서 사용하게된다.

Vuex 내용

복잡한 애플리케이션의 컴포넌트들을 효율적으로 관리하는 Vuex
Vuex 라이브러리의 등장 배경인 Flux 패턴
Vuex 라이브러리의 주요 속성인 state(data), getters(computed), mutations(methods), actions(비동기 methods)
Vuex 쉽게 코딩하는 Helper 함수
Vuex 프로젝트 구조화하는 방법 및 모듈 구조화 방법

Vuex 라이브러리

Vuex
-> 무수히 많은 컴포넌트의 데이터를 관리하기 위한 상태 관리 패턴이자 라이브러리
React의 Flux 패턴에서 기인하였으며
Vue.js 중고급 개발자의 필수 관문

Flux


Flux란?
Action -> Dispathcer -> Model -> View
MVC 패턴의 복잡한 데이터 흐름 문제를 해결하는 개발 패턴
1.action : 화면에서 발생하는 이벤트 또는 사용자의 입력
2.dispatcher : 데이터를 변경하는 방법, 메서드
3.model : 화면에 표시할 데이터
4.view : 사용자에게 비춰지는 화면

MVC
Controller -> Model -> View

Flux
Action -> Dispatcher -> Model -> View

MVC 패턴의 문제점
기능 추가 및 변경에 따라 생기는 문제점을 예측할수가없다
앱이 복잡해지면서 생기는 업데이트 루프 때문
따라서 Flux 패턴의 단방향 데이터 흐름으로 여러갈래로 나뉘지않고 생기는 문제점 같은것들이
쉽게 파악이 될수있다.

Vuex 컨셉과 구조

복잡한 애플리케이션에서 컴포넌트의 개수가 많아지면 컴포넌트간에 데이터 전달이 어려워지게된다. 이러한것들을 이벤트 버스로 어느정도 해결할수있지만,
어디서 이벤트를 보냈는지 받았는지 알기가 어려워진다. 즉 컴포넌트간 데이터전달이 명시적이지 않게됨. 그래서 Vuex를 사용하면,
1. MVC 패턴에서 발생하는 구조적오류
2. 컴포넌트간 데이터 전달 명시
3. 여러개의 컴포넌트에서 같은 데이터를 업데이트 할때 동기화 문제가 해결된다

Vuex의 컨셉


State : 컴포넌트간에 공유하는 데이터 data()
View : 데이터를 표시하는 화면 template
Actions : 사용자의 입력에 따라 데이터를 변경하는 methods

Vuex의 구조

컴포넌트 -> 비동기 로직 -> 동기 로직 -> 상태로 진행된다.
(Actions는 비동기, Mutations는 동기) Methods, State는 데이터로 볼수있다.
이곳에는 Mutations와 Devtools에 관한 내용이 자세하게 나타난다.
나머지는 Vuex 페이지 참고!!

Vuex 설치 및 시작

npm install vuex --save
-> Vuex 설치 명령어

src/store 라는 폴더를 만들어주고, src/store/store.js를 만들어준다.

import Vue from 'vue'
import Vuex from 'vuex'


Vue.use(Vuex);

export const store = new Vuex.Store({
    //
});
//store.js
//이렇게 했을때에 다른곳에서 this.$store를 접근할수있게 만들어준다.

main.js에도 store를 입력 import 시켜주어서 Vue 컴포넌트에 대입한다.

import {store} from './store/store.js'

new Vue({
  el: '#app',
  store,
  render: h => h(App)
})

//main.js

Vuex 기술 요소(state, getters, mutations, actions)

state: 여러 컴포넌트에 공유되는 데이터 (data)
getters: 연산된 state 값을 접근하는 속성 (computed)
mutations: state 값을 변경하는 이벤트 로직.메서드(methods)
actions: 비동기 처리 로직을 선언하는 메서드(aysnc methods)

state

data와 거의 똑같다고 보면된다. Vuex로 인해서 다른 컴포넌트 즉 어디에서든
이곳에 접근을 할수있다는것이 차이점이다.

//vue
data:{
	meesage: "Hello Vue.js!"
}
//vuex
state:{
	meesage: "Hello Vue.js!"
}
//vue
<p>{{message}}</p>
//vuex
<p>{{this.$store.state.message}}</p>

getters

vue의 computed 처럼 미리 연산된 값을 접근하는 속성이다(캐싱효과도 동일하게 적용)

//store.js
state:{
        num:10
    },
getters:{
  getNumber(state){
        return state.num;
   },
  doubleNumber(state){
              return state.num * 2;
   }
} 
  <p>{{this.$store.getters.getNumber}}</p>
  <p>{{this.$store.getters.doubleNumber}}</p>

Vue와 무엇이 다른것인가 싶기도 하겠지만, Helpers를 사용하면 느끼게 될수있다.

mutations

state의 변경할수잇는 유일한 방법과 메서드
mutations을 동작하는 방법은 commit()으로 동작시킬수있다.
commit('실행할 mutations', 인자)이며, 인자값은 값이든 객체든
파라미터로 보낼수가 있다. 대신 인자로 보내는것은 하나만 가능하다
ex) this.$store.commit('testcommit', value, text) X
또한 mutations를 동작시킬때에 인자(payload)를 전달할수 있다.

    state:{
        storeNum: 10
    },
    mutations:{
        modifyState(state, payload){
            console.log(payload.str);
            return state.storeNum += payload.num;
        }
    }

    this.$store.commit('modifyState',{
        str: 'passed from payload',
        num: 20
    });

왜 state는 직접 변경하지 않고 mutations로 변경할까?
1. 여러개의 컴포넌트에서 state를 직접 변경하는경우 어느 컴포넌트에서 해당 state를 변경했는지 추적하기가 어려워진다.
2. 또한 특정 시점에 컴포넌트가 state를 접근하여서 변경한건지도 확인이 어려워진다
3. Vuex는 Vue core 개발자들이 개발하였기때문에 mutations를 사용여서 state를 변경하게끔 다른 기능들도 만들어두었다. Vue의 반응성을 거스리지 않고 명시적으로 상태 변화를 수행하며 디버깅, 테스팅 혜택을 볼수있게 사용하기 위함

!! Tip


Vue devtool 에서 Vuex의 도구를 사용할때에
Time Travel 기능 : 이 mutations를 사용하기전의 상태로 state를 돌릴수있다
Revert : mutations 취소 시키기

actions

비동기 처리 로직을 선언하는 메서드이며 비동기 로직을 담당하는 mutations라고 할수있다.
데이터요청, Promise, ES6 async와 같은 비동기 처리는 모두 actions에 선언이된다.


    //store.js
    mutations:{
        setData(state, fetchedData){
            state.product = fetchedData;
        }
    },
    actions:{
        fetchProductData(context){
            return axios.get('https://domain.com/products/1')
                        .then(response => context.commit('setData', response));
        }
    }

    //App.vue(component)
    methods:{
        getProduect(){
            this.$store.dispatch('fetchProductData');
        }
    }


위의 그림을 설명하자면,
1. 컴포넌트딴에서 dispatch로 actions의 메소드 이름을 불러오고
2. actions의 메소드가 비동기로 실행되며 context라는것을 받는데 이 context로
commit을 한다
3. 그래서 mutations의 동기 메소드를 불러오게 되어 그 메소드를 실행한다

왜 actions로 비동기 처리를 해야할까?
mutations와 마찬가지로 비동기가 되게되면 언제 어느 컴포넌트에서 해당 state를 호출하고 변경했는지 알수가없기때문, state의 값의 변화를 추적하기 쉽게하기 위해서는 actions를 사용하여서 관리를 해야한다.

Vuex Helpers

Helper?
Store에 있는 4가지 각 속성들을 더 쉽게 사용하는 방법

  • state -> mapState
  • getters -> mapGetters
  • mutations -> mapMutations
  • actions -> mapActions

헬퍼를 사용하고자 하는 vue 파일에서 해당 헬퍼를 로딩

import { mapState } from 'vuex'
import { mapGetters } from 'vuex'
import { mapMutations } from 'vuex'
import { mapActions } from 'vuex'


export defualt {
  //this.$store.state.num, this.$store.getters.countedNum
  computed() {...mapState(['num']), ...mapGetters(['countedNum'])},
  methods: { ...mapMutations(['clickBtn']), ...mapActions(['asyncClickBtn'])}
  //this.$store.mutations.clickBtn, this.$store.actions.asyncClickBtn
}

...는 ES6의 Object Spread Operator이다.

let tt = {
	field : 'web'
    language: 'js'
};

let developer = {
	nation: 'korea',
    ...tt
    //field: 'web',
    //lanaguage: 'js'
}
Spread Operator를 사용하면 위의 주석의 결과와 같이
객체 안에 있는 key value가 나오게 된다.

mapState

Vuex에 선언한 state 속성을 뷰 컴포넌트에 더 쉽게 연결해주는 헬퍼

// App.vue
import {mapState} from 'vuex'
computed(){
	...mapState(['num'])
    //num() { return this.$store.state.num; }
}

//store.js
state:{
	num : 10
}
<!--<p>{{this.$store.state.num}}</p>-->
<p>{{this.num}}</p>

mapGetters

Vuex에 선언한 getters 속성을 뷰 컴포넌트에 더 쉽게 연결해주는 헬퍼

// App.vue
import {mapState} from 'vuex'
computed(){
	...mapGetters(['reverseMessage'])
}

//store.js
getters:{
	reverseMessage(state){
    	return state.msg.split('').reverse().join('');
    }
}
<!--<p>{{this.$store.getters.reverseMessage}}</p>-->
<p>{{this.reverseMessage}}</p>

mapMutations

Vuex에 선언한 mutations 속성을 뷰컴포넌트에 더 쉽게 연결해주는 헬퍼

import { mapMutations } from 'vuex'

methods : {
  ...mapMutations(['clickBtn']),
  authLogin(){},
  displayTable(){}
}

mutations : {
  clickBtn(state){
    alert(state.msg);
  }
}

<button @click="clickBtn">popup message</button>

mapActions

Vuex에 선언한 actions 속성을 뷰 컴포넌트에 더 쉽게 연결해주는 헬퍼


import { mapActions } from 'vuex'

methods : {
  ...mapActions(['delayClickBtn']),
}

actions : {
  delayClickBtn(context){
    setTimeout(() => context.commit('clickBtn'), 2000);
  }
}

<button @click="delayClickBtn">delay popup message</button>

헬페의 유연한 문법

// 배열 리터럴
...mapMutations([
    'clickBtn', //'clickBtn' : clickBtn
    'addNumber'  // addNumber(인자)
])

// 객체 리터럴
...mapMutations({
	popupMsg: 'clickBtn' // 컴포넌트 메서드 명 : Store의 Mutations명
})

Vuex로 프로젝트 구조화 및 모듈화 방법

1.store 속성 나누기

store.js안에 모든것을 다 넣게된다면 결국 store.js가 가지고 있는
양도 너무 방대해질것이므로 이것을 모듈화를 사용하여서 나눌수가있다.
store라는 폴더를 만들고나서 그안에 getters, mutations, actions를 만들어서 넣으면 되겠다.(store/...)
ES6의 Import & Export를 이용하여 속성별로 모듈화

import Vue from 'vue'
import Vuex from 'vuex'
import * as getters from 'store/getters.js'
import * as mutations from 'store/mutations.js'
import * as actions from 'store/actions.js'

export const store = new Vuex.store({
  state: {},
  getters: getters,
  mutations: mutations,
  actions: actions
});

ex) mutations.js 를 구성해본다면

const addOneItem = (state, todoItem) => {
			const obj = {completed: false, item: todoItem};
      		localStorage.setItem(todoItem, JSON.stringify(obj));
			state.todoItems.push(obj);
		},
const removeOneItem = (state, payload) => {
			state.todoItems.splice(payload.index, 1);
      		localStorage.removeItem(payload.todoItem.item);
		},
const toggleOneItem = (state, payload) => {
			state.todoItems[payload.index].completed = !state.todoItems[payload.index].completed;
			localStorage.removeItem(payload.todoItem.item);
			localStorage.setItem(payload.todoItem.item, JSON.stringify(payload.todoItem));
		},
const clearAllItems = (state) => {
			state.todoItems = [];
			localStorage.clear();
		}

export {addOneItem, removeOneItem, toggleOneItem, clearAllItems}

원래의 ES6로 나타내어져있던 함수들을
함수 표현식으로 나타내어준다. 그리고 뒷부분은 =>(화살표함수)로
파라미터들을 나타내어 준것이며, 한꺼번에 export를 해주었다.

2.store의 modules 나누기

이번에는 modules안에 입력시켜서 그 안에서 다른속성들을 관리하는것.

//store.js
import Vue from 'vue'
import Vuex from 'vuex'
import todo from 'modules/todo.js'

export const store = new Vuex.store({
  modules:{
  	moduleA: todo, //모듈 명칭 : 모듈 파일명
    todo // todo: todo
  }
});

// todo.js
const state = {}
const getters = {}
const mutations = {}
const actions = {}

ex)

const storage = {
	fetch(){
    	.....
    }
}

const state = {
		todoItems: storage.fetch()
	};

const getters = {
		getTodoItems(state) {
			return state.todoItems;
		}
	}
const mutations = {
		addOneItem(state, todoItem) {
			const obj = {completed: false, item: todoItem};
      		localStorage.setItem(todoItem, JSON.stringify(obj));
			state.todoItems.push(obj);
		},
		removeOneItem(state, payload) {
			state.todoItems.splice(payload.index, 1);
      		localStorage.removeItem(payload.todoItem.item);
		},
		toggleOneItem(state, payload) {
			state.todoItems[payload.index].completed = !state.todoItems[payload.index].completed;
			localStorage.removeItem(payload.todoItem.item);
			localStorage.setItem(payload.todoItem.item, JSON.stringify(payload.todoItem));
		},
		clearAllItems(state) {
			state.todoItems = [];
			localStorage.clear();
		}
	}
    
export default{
	state,
   	getters,
   	mutations
}
profile
Let's do it developer

0개의 댓글