잠깐 Vue.js 문법 정리

카탈로그
  1. 1. 필수요소
  2. 2. 설치방법
    1. 2.1. npm
    2. 2.2. cli
    3. 2.3. 번들러
  3. 3. 시작하기
    1. 3.1. 선언적 렌더링
      1. 3.1.1. 템플릿 안 변수 삽입
      2. 3.1.2. v-bind = 단방향 바인딩 (v-dom -> dom)
    2. 3.2. 조건문 / 반복문
      1. 3.2.1. 조건문 v-if
      2. 3.2.2. 반복문 v-for
    3. 3.3. 사용자 입력 핸들링 v-on:event, v-model
      1. 3.3.1. v-on
      2. 3.3.2. v-model = 양방향 바인딩
    4. 3.4. 컴포넌트 이용법
  4. 4. Vue 인스턴스
    1. 4.1. vue 인스턴스 만들기
    2. 4.2. 속성과 메소드
      1. 4.2.1. Object.freeze()
      2. 4.2.2. $ 유용한 속성 및 메소드
    3. 4.3. 인스턴스 라이프사이클 훅
      1. 4.3.1. beforeCreate
      2. 4.3.2. created
      3. 4.3.3. beforeMount
      4. 4.3.4. mounted
      5. 4.3.5. beforeUpdate
      6. 4.3.6. updated
      7. 4.3.7. beforeDestroy
      8. 4.3.8. destroyed
  5. 5. 템플릿 문법
    1. 5.1. interpolation
      1. 5.1.1. v-once
      2. 5.1.2. v-html
      3. 5.1.3. v-bind:
      4. 5.1.4. Javascript 표현식
    2. 5.2. 디렉티브
      1. 5.2.1. 동적 전달인자 (2.6.0+부터 생김)
      2. 5.2.2. preventDefault()
    3. 5.3. 약어
      1. 5.3.1. v-bind 약어
      2. 5.3.2. v-on 약어
  6. 6. computed와 watch
    1. 6.1. computed 배경
    2. 6.2. computed 예제
    3. 6.3. computed vs method
    4. 6.4. computed vs watch
    5. 6.5. computed(setter)
    6. 6.6. watch
  7. 7. 클래스와 스타일 바인딩
    1. 7.1. 클래스 동적 토글
      1. 7.1.1. 배열
        1. 7.1.1.1. Result:
        2. 7.1.1.2. 삼항연산자
      2. 7.1.2. 컴포넌트
        1. 7.1.2.1. Result:
    2. 7.2. 인라인 스타일 바인딩
      1. 7.2.1. b-bind:style
      2. 7.2.2. 자동 접두사
  8. 8. 조건부 렌더링
    1. 8.1. v-if, v-else
      1. 8.1.1. <template>의 v-if
    2. 8.2. v-show
    3. 8.3. 주의사항 v-for우선순위
  9. 9. 리스트 렌더링
    1. 9.1. v-for와 객체
      1. 9.1.1. Result
      2. 9.1.2. 주의점
    2. 9.2. Maintaining State
  10. 10. 이벤트 핸들링
  11. 11. 폼 입력 바인딩
  12. 12. 컴포넌트
  • 컴포넌트 모아보기
    1. 1. 컴포넌트 등록
    2. 2. Props
    3. 3. 커스텀 이벤트
    4. 4. 슬롯(slots)
    5. 5. 동적 & 비동기 컴포넌트
    6. 6. Handling Edge Cases
  • 트랜지션 & 애니메이션
    1. 1. 진입/진출? 봐야알 듯
    2. 2. 상태 트랜지션
  • 재사용 & 컴포지션
    1. 1. 믹스인
    2. 2. 사용자 지정 디렉티브
    3. 3. Render Functions & JSX
    4. 4. 플러그인
      1. 4.1. 사용법
      2. 4.2. 작성법
    5. 5. 필터
  • 도구
    1. 1. 싱글 파일 컴포넌트
    2. 2. 단위 테스팅
    3. 3. Typescript
    4. 4. 배포
  • 스케일링 업
    1. 1. 라우팅
    2. 2. 상태 관리
    3. 3. 서버사이드 렌더링
    4. 4. Security
  • 내부
    1. 1. 반응형
  • Vue.js 사용 안한지 오래되서, 한번 문법 정리하는 시간을 갖음.

    한번 문법 정리 후, Codepen을 이용하여 예제 샘플 나열해서 정리해둬야겠다…

    필수요소

    Link : https://kr.vuejs.org/v2/guide/installation.html 링크 학습

    설치방법

    npm

    1
    2
    # 최신 안정화 버전
    npm i vue

    cli

    Link: https://cli.vuejs.org/

    번들러

    번들러는 Vue.js + Webpack 템플릿 보면 알 수 있을 듯

    시작하기

    선언적 렌더링

    템플릿 안 변수 삽입

    1
    <div id="app">{{ message }}</div>
    1
    2
    3
    4
    5
    6
    var app = new Vue({
    el: `#app`,
    data: {
    message: `Hello, World`
    }
    })

    v-bind = 단방향 바인딩 (v-dom -> dom)

    1
    2
    3
    4
    5
    <div id="app-2">
    <span v-bind:title="message">
    텍스트
    </span>
    </div>
    1
    2
    3
    4
    5
    6
    var app2 = new Vue({
    el: `#app-2`,
    data: {
    message: `데이터: ${new Date()}`
    }
    })

    조건문 / 반복문

    조건문 v-if

    1
    2
    3
    4
    5
    <div id="app-3">
    <p v-if="seen">
    seen의 bool값에 따라 보일지 결정됨
    </p>
    </div>
    1
    2
    3
    4
    5
    6
    var app = new Vue({
    el: `#app-3`,
    data: {
    seen: true
    }
    })

    반복문 v-for

    1
    2
    3
    4
    5
    6
    7
    <div id="app-4">
    <ol>
    <li v-for="todo in tods">
    {{ todo.text }}
    </li>
    </ol>
    </div>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    var app4 = new Vue({
    el: `#app-4`,
    data: {
    todos: [
    {text: `Text1`},
    {text: `Text2`},
    {text: `Text3`}
    ]
    }
    })

    // 리스트 추가법
    // app4.todos.push({text: `Text4`})

    사용자 입력 핸들링 v-on:event, v-model

    v-on

    1
    2
    3
    4
    5
    6
    <div id="app-5">
    <p>{{message}}</p>
    <button v-on:click="onclickBtn">
    버튼
    </button>
    </div>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var app5 = new Vue({
    el: `#app-5`,
    data: {
    message: `First Message.`
    },
    methods: {
    onclickBtn: function () {
    this.message = `Message Change!`
    }
    }
    })

    v-model = 양방향 바인딩

    1
    2
    3
    4
    <div id="app-6">
    <p>{{ message }}</p>
    <input v-model="message">
    </div>
    1
    2
    3
    4
    5
    6
    var app6 = new Vue({
    el: `#app-6`,
    data: {
    message: `Hello`
    }
    })

    컴포넌트 이용법

    1
    2
    3
    4
    5
    6
    Vue.component(`todo-item`, {
    props: [`todo`]
    template: `<li>{{ todo.text }}</li>`
    })

    var app = new Vue(...)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    <div id="app-7">
    <ol>
    <todo-item
    v-for="item in list"
    v-bind:todo="item"
    v-bind:key="item.id"
    ></todo-item>
    </ol>
    </div>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    Vue.component(`todo-item`, {
    props: [`todo`],
    template: `<li>{{ todo.text }}</li>`
    })

    var app7 = new Vue({
    el: `#app-7`,
    data: {
    list: [
    {id: 0, text: `text1`},
    {id: 1, text: `text2`},
    {id: 2, text: `text3`}
    ]
    }
    })

    Vue 인스턴스

    vue 인스턴스 만들기

    1
    var vm = new Vue({})

    속성과 메소드

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    var data = {a: 1}

    var vm = new Vue({
    data: data
    })

    vm.a === data.a // true

    vm.a = 2
    data.a // => 2 (참조 복사임, swallow)

    data.a = 3
    vm.a // => 3 양방향임

    Object.freeze()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var obj = {
    foo: `bar`
    }

    // obj가 변경되도 반영되지 않음
    Object.freeze(obj)

    new Vue({
    el: `#app`,
    data: obj
    })
    1
    2
    3
    4
    5
    6
    <div id="app">
    <p>{{ foo }}</p>
    <button v-on:click="foo = 'baz'">
    Change
    </button>
    </div>

    $ 유용한 속성 및 메소드

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var data = {a: 1}
    var vm = new Vue({
    el: `#example`,
    data: data
    })

    vm.$data === data // true
    vm.$el === document.querySelector(`#example`) // true

    vm.$watch(`a`, (newVal, oldVal) => {
    // vm.a 메소드가 변경되면 호출됨
    })

    인스턴스 라이프사이클 훅

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    new Vue({
    data: {
    a: 1
    },
    created: function() {
    // this는 vm 인스턴스를 가르킴
    console.log(`a = ${this.a}`)
    },

    })

    beforeCreate

    가장 먼저 실행되는 훅

    data, events(vm.$on, $once, $off, $emit) 세팅되지 않음

    created

    data와 events가 활성화된 시점 ~ 템플릿과 가상돔은 마운트·렌더링 되지 않음

    beforeMount

    렌더링 일어나기 직전

    mounted

    컴포넌트, 템플릿, 돔에 접근가능한 시점, 모든 하위 컴포넌트 마운트를 보장하지는 않음
    자식부터 차근차근 생성 후 부모가 생성되기 때문에 주의 (무조건, 순서대로 되지 않을 수도 있음)

    beforeUpdate

    dom이 새로 업데이트되기 직전 시점

    updated

    재 렌더링 이후, 실행된다. 잘못 작성한다면 무한루프에 빠질 수 있음

    beforeDestroy

    뷰 인스턴스 제거되기 직전 시점, 컴포넌트의 원래 형태를 그대로 유지된 상태
    이벤트 리스너를 제거하기 좋은 시점

    destroyed

    인스턴스 제거 후 시점, vue의 모든 디렉티브 바인딩과 리스너가 제거

    1577214829972

    1577209781216

    템플릿 문법

    interpolation

    v-once

    1
    <span v-once>한번 이후, 변경되지 않음 {{ msg }}</span>

    v-html

    1
    2
    # rawHtml = `<span style="color:red">HTML</span>`
    <span v-html="rawHtml"></span>

    데이터 바인딩이 무시됨, Vue는 문자열 기반 템플릿 엔진은 아니여서, v-html을 이용한 템플릿을 사용할 수는 없음
    또한 XSS 취약점이 존재하기 때문에, 사용자 폼에서는 금지

    v-bind:

    1
    <div v-bind:id="dynamicId"></div>
    1
    <button v-bind:disabled="isButtonDisabled">Button</button>

    isButtonDisablednull, undefined, false면 속성으로 표시되지 않음

    Javascript 표현식

    1
    2
    3
    4
    5
    6
    // 여러 표현식 사용 가능, 대신 구문은 작성 불가능(ex. 선언/if문은 작동하지 않음)
    {{ number + 1 }}

    {{ ok ? 'YES' : 'NO' }}

    {{ message.split(``).reserve().join(``) }}
    1
    <div v-bind:id="'last-' + id"></div>

    디렉티브

    동적 전달인자 (2.6.0+부터 생김)

    1
    2
    3
    <a v-bind:[attribueName]="url">...</a>

    <a v-on:[attribueName]="function">...</a>

    대신 스페이스나 따옴표를 쓰지말 것, 또한 소문자만 쓰는 것이 좋음

    1
    2
    <!-- 컴파일러 경고를 불러옵니다 -->
    <a v-bind:['foo' + bar]="value"> ... </a>

    preventDefault()

    1
    <form v-on:submit.prevent="onSubmit"></form>

    약어

    v-bind 약어

    1
    2
    3
    4
    5
    6
    7
    8
    <!-- 전체 -->
    <a v-bind:href="url">...</a>

    <!-- 약어 -->
    <a :href="url">...</a>

    <!-- 동적 -->
    <a :[key]="url">...</a>

    v-on 약어

    1
    2
    3
    4
    5
    6
    7
    8
    <!-- 전체 -->
    <a v-on:click="function">...</a>

    <!-- 약어 -->
    <a @click="function">...</a>

    <!-- 동적 -->
    <a @[event]="function">...</a>

    computed와 watch

    computed 배경

    너무 많은 템플릿 내 표현식은 유지보수가 힘듬

    1
    2
    3
    4
    <!-- 어려움 -->
    <div id="example">
    {{ message.split('').reverse().join('') }}
    </div>

    computed 예제

    1
    2
    3
    4
    <div id="example">
    <p>원본: {{ message }}</p>
    <p>역순: {{ reversedMessage }}</p>
    </div>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var vm = new Vue({
    el: `#example`,
    data: {
    message: `안녕`
    },
    computed: {
    reversedMessage: function() {
    return this.message.split(``).reverse().join(``)
    }
    }
    })

    computed vs method

    차이점: computed는 종속 대상이 변경될 때만 함수를 실행하고, 변경되지 않으면 그 이전 계산했던 값을 돌려줌
    Date.now()처럼 아무곳에 의존되지 않은 값은 업데이트 되지 않음

    이것과 비교하여 method는 항상 함수를 재실행함

    computed vs watch

    1
    <div id="demo">{{ fullName }}</div>

    비교(watch)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    var vm = new Vue({
    el: `#demo`,
    data: {
    firstName: `TAEUK`,
    lastName: `KANG`,
    fullName: `TAEUK KANG`
    },
    watch: {
    firstName: function(val) {
    this.fullName = val + ` ` + this.lastName
    },
    lastName: function(val) {
    this.fullName = this.firstName + ` ` + val
    }
    }
    })

    computed

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var vm = new Vue({
    el: `#demo`,
    data: {
    firstName: `TAEUK`,
    lastName: `KANG`
    },
    computed: {
    fullName: function() {
    return `${this.firstName} ${this.lastName}`
    }
    }
    })

    computed(setter)

    computed는 기본적으로 getter함수이지만, setter로 지정하여 만들 수 있음

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    computed: {
    fullName: {
    // getter
    get: function() {
    return `${this.firstName} ${this.lastName}`
    },
    // setter
    set: function(newValue) {
    var names = newValue.split(` `)
    this.firstName = names[0]
    this.lastName = names[names.length - 1]
    }
    }
    }

    vm.fullName = TAEUK KANG 실행하면, 업데이트가 되는 것을 볼 수 있음

    watch

    비동기식 또는 시간이 많이 소요되는 조작을 수행하려는 경우에 가장 유용

    1
    2
    3
    4
    5
    6
    7
    <div id="watch-example">
    <p>
    yes/no ?
    <input v-model="question">
    </p>
    <p>{{ answer }}</p>
    </div>
    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
    34
    35
    // axios, lodash 사용 예제
    var watchExampleVM = new Vue({
    el: `#watch-example`,
    data: {
    question: ``,
    answer: `대답 미입력`
    },
    watch: {
    question: function(newQuestion) {
    this.answer = `입력 대기 중...`
    this.getAnswer()
    }
    },
    methods: {
    // _.debounce는 lodash가 제공하는 기능, 시간이 많이 소요되는 작업에 실행 빈도를 제한
    getAnswer: _.debounce(
    function() {
    if (this.question.indexOf(`?`) === -1) {
    this.answer = `질문에 물음표를 넣어줘`
    return
    }
    this.answer = `생각 중...`
    var vm = this

    axios.get(`api url`).then(res => {
    vm.answer = _.capitalize(res.data.answer)
    }).catch(err => {
    vm.answer = `Err! ${err}`
    })
    },
    // 사용자가 기다리는 시간(ms)
    500
    )
    }
    })

    클래스와 스타일 바인딩

    클래스 동적 토글

    1
    <div v-bind:class="{ active isActive }"></div>
    1
    2
    3
    4
    <div
    class="static"
    v-bind:class="{ active: isActive, 'test-danger': hasError}">
    </div>
    1
    2
    3
    4
    data: [
    isActive: true,
    hasError: false
    ]

    결과값:

    1
    <div class="static active"></div>

    굳이 인라인 스크립트를 쓰지 않아도 됨

    1
    <div v-bind:class="classObject"></div>
    1
    2
    3
    4
    5
    6
    data: {
    classObject: {
    active: true,
    'text-danger': false
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // computed도 가능
    data: {
    isActive: true,
    error: null
    },
    computed: {
    classObject: function () {
    return {
    active: this.is Active && !this.error,
    'text-danger': this.error && this.error.type === 'fatal'
    }
    }
    }

    배열

    1
    <div v-bind:class="[activeClass, errorClass]"></div>
    1
    2
    3
    4
    data: {
    activeClass: `active`,
    errorClass: `text-danger`
    }
    Result:
    1
    <div class="active text-danger"></div>
    삼항연산자
    1
    <div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
    1
    <div v-bind:class="[{active: isActive}, errorClass]"

    컴포넌트

    Vue 컴포넌트의 기존 클래스가 덮어쓰기 되지않고, 추가됨. 바인딩도 마찬가지

    1
    2
    3
    Vue.component(`my-component`, {
    template: `<p class="foo bar">Hi</p>`
    })
    1
    <my-component class="baz boo"></my-component>
    Result:
    1
    <p class="foo bar baz boo"></p>

    인라인 스타일 바인딩

    b-bind:style

    속성 이름에 camelCase와 kebab-case (따옴표를 함께 사용해야 합니다)를 사용

    1
    <div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px'}"></div>
    1
    2
    3
    4
    data: {
    activeColor: `red`,
    fontSize: 30
    }

    데이터 밑 객체를 삽입하여 간결하게

    1
    <div v-bind:style="styleObject"></div>
    1
    2
    3
    4
    5
    6
    data: {
    styleObject: {
    color: `red`,
    fontSize: `13px`
    }
    }
    1
    2
    <!-- 여러 스타일시트 사용 가능 -->
    <div v-bind:style="[baseStyles, overridingStyles]"></div>

    자동 접두사

    1
    <div v-bind:style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>

    그런데 그냥 PostCSS의 autoPrefixer을 쓰는게 편해보임.

    조건부 렌더링

    v-if, v-else

    1
    2
    3
    4
    5
    6
    7
    <h1 v-if="ok">Yes</h1>
    <h1 v-else>No</h1>

    <div v-if="type === 'A'">A</div>
    <div v-else-if="type === 'B'">B</div>
    <div v-else-if="type === 'C'">C</div>
    <div v-else>Not A/B/C</div>

    v-else 엘리먼트는 v-if 엘리먼트 또는 v-else-if 엘리먼트 바로 뒤에 있어야 합니다. 그렇지 않으면 인식할 수 없습니다.

    <template>v-if

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <template v-if="ok">
    <h1>Title</h1>
    </template>

    <template v-if="loginType === 'username'">
    <label>사용자 이름</label>
    <input placeholder="사용자 이름을 입력하세요" key="username-input">
    </template>
    <template v-else>
    <label>이메일</label>
    <input placeholder="이메일 주소를 입력하세요" key="email-input">
    </template>

    key 속성이란, “이 두 엘리먼트는 완전히 별개이므로 다시 사용하지 마십시오.”라고 알리는 행위

    없으면 input 엘리먼트를 재사용함

    v-show

    1
    <h1 v-show="ok">안녕하세요!</h1>

    v-if와 다른 점은 항상 DOM에 남겨져 있고, css의 display 상태로만 감춰져있음
    그러므로, 매우 자주 바뀌는 렌더링 비용은 v-show를 이용하는 것을 권장

    주의사항 v-for우선순위

    v-forv-if보다 높은 우선순위 권을 갖기 때문에 사용에 주의할 것

    리스트 렌더링

    1
    2
    3
    <ul id+"example-1">
    <li v-for="(item, index) in items">{{ index }}: {{ item.message }}</li>
    </ul>
    1
    2
    3
    4
    5
    6
    7
    8
    var example1 = new Vue({
    data: {
    items: [
    { message: `item1`},
    { message: `item2`}
    ]
    }
    })

    in 대신 of 반복문도 사용 가능 (차이점은 일반적인 JS문법과 동일: in은 key값을 순회, of는 value값을 순회)

    v-for와 객체

    1
    2
    3
    <ul id="v-for-object" class="demo">
    <li v-for="(value, key, index) in object">{{index}}. {{key}}: {{value}}</li>
    </ul>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    new Vue({
    el: `#v-for-object`,
    data: {
    object: {
    title: `제목`,
    author: `저자`,
    publishedAt: `2019-12-29`
    }
    }
    })

    Result

    1
    2
    3
    0. title: 제목
    1. author: 저자
    2. publishedAt: 2019-12-29

    주의점

    객체 순서가 일관적이지 않음. Object.keys()의 결과에 따름.

    Maintaining State

    이벤트 핸들링

    폼 입력 바인딩

    컴포넌트

    컴포넌트 모아보기

    컴포넌트 등록

    Props

    커스텀 이벤트

    슬롯(slots)

    동적 & 비동기 컴포넌트

    Handling Edge Cases

    트랜지션 & 애니메이션

    진입/진출? 봐야알 듯

    상태 트랜지션

    재사용 & 컴포지션

    믹스인

    사용자 지정 디렉티브

    Render Functions & JSX

    플러그인

    사용법

    1
    2
    3
    4
    5
    Vue.use(MyPlugin)

    new Vue({
    // ...options
    })

    Vue.use는 자동으로 같은 플러그인 중복사용을 방지함 (한번만 설치됨)

    작성법

    추후 작성

    필터

    도구

    싱글 파일 컴포넌트

    단위 테스팅

    Typescript

    배포

    스케일링 업

    라우팅

    상태 관리

    서버사이드 렌더링

    Security

    내부

    반응형