프로그래밍/JavaScript

VueJS 학습 - Basic

seungdols 2022. 10. 13. 23:19

VueJS

사용자 입력

<div id="event-handling">
  <p>{{ message }}</p>
  <button v-on:click="reverseMessage">Reverse Message</button>
</div>
const EventHandling = {
  data() {
    return {
      message: 'Hello Vue.js!'
    }
  },
  methods: {
    reverseMessage() {
      this.message = this.message
        .split('')
        .reverse()
        .join('')
    }
  }
}

v-on:event를 통해서 이벤트를 제어 한다.

<input @keyup.enter="submit" />

vuejs에서는 기본적인 key 이벤트를 제공한다.

  • enter
  • tab
  • delete
  • esc
  • space
  • up
  • down
  • left
  • right

Vue는 또한 양식에 대한 입력과 앱 상태를 양방향으로 바인딩하는 v-model 디렉티브를 제공한다.

<div id="two-way-binding">
  <p>{{ message }}</p>
  <input v-model="message" />
</div>
const TwoWayBinding = {
  data() {
    return {
      message: 'Hello Vue!'
    }
  }
}

양방향 바인딩이 뭔지 좀 더 찾아봐야 한다. react는 단반향 바인딩만 지원 하기 때문에, vuejs랑 다르다.

Model에서 데이터를 정의한 후 View와 연결하면 Model, View 중 어느 한쪽에서 변경이 일어났을 때, 다른 한쪽에 자동으로 반영 되는 것을 데이터 양방향 바인딩이라고 한다.

조건문과 반복문

<div id="conditional-rendering">
  <span v-if="seen">이제 나를 볼수 있어요</span>
</div>
const ConditionalRendering = {
  data() {
    return {
      seen: true
    }
  }
}

조건문의 경우 v-id를 이용하면 된다. Vue 엘리먼트가 Vue에 삽입/업데이트/제거 될때 자동으로 전환 효과 적용 된다고 한다. ref. https://v3.ko.vuejs.org/guide/transitions-enterleave.html

<div id="list-rendering">
  <ol>
    <li v-for="todo in todos">
      {{ todo.text }}
    </li>
  </ol>
</div>
const ListRendering = {
  data() {
    return {
      todos: [
        { text: 'Learn JavaScript' },
        { text: 'Learn Vue' },
        { text: 'Build something awesome' }
      ]
    }
  }
}

v-for는 반복문 directive이다. 반복문 시에는 v-bind:key를 필수적으로 지정 해주어야 한다.

컴포넌트 조립

const TodoList = {
  data() {
    return {
      groceryList: [
        { id: 0, text: '야채' },
        { id: 1, text: '치즈' },
        { id: 2, text: '사람이 먹을수 있는거라면 뭐든지' }
      ]
    }
  }
}

const app = Vue.createApp(TodoList)

app.component('todo-item', {
  props: ['todo'],
  template: `<li>{{ todo.text }}</li>`
})

app.mount('#todo-list-app')
<div id="todo-list-app">
  <ol>
    <!--
      이제 할일 todo-item 에 할일을 전달합니다.
      콘텐츠는 동적으로 포현됩니다.
      여기에서 "key"를 또 전달하고 있는데
      이것에 대해서는 나중에 설명하겠습니다.
    -->
    <todo-item
      v-for="item in groceryList"
      v-bind:todo="item"
      v-bind:key="item.id"
    ></todo-item>
  </ol>
</div>

컴포넌트에 v-bind를 통해서 데이터를 전달 할 수 있다.

<template>
</template>

<script>
    export default {
        name: '' // 컴포넌트 이름
        components: {}, // 외부 컴포넌트를 사용하게 되면, 해당 컴포넌트를 import한 후 등록 해주어야 한다.
        data() {
            return {

            };
        }, // 전역 데이터 선언 (html, js간의 양방향 데이터 바인딩 가능하며, 데이터 프로퍼티에서는 this로 접근 해야 한다.)
        setup() {}, // 컴포지션 API를 구현하는 메소드
        created() {}, // 컴포넌트 생성시, 실행
        mounted() {}, // template에 정의된 html코드가 렌더링 된 후 실행
        unmounted() {}, // unmount 이후 실행 
        methods: {} // 컴포넌트 내에서 사용할 메소드 정의
    }
</script>

문자열 데이터 바인딩

    <h1>Hello, {{ title }}</h1>

{{}}로 감싸주면 된다.

    <h1 v-once>Hello, {{ title }}</h1>

v-once 디렉티브를 사용하면, 데이터가 변경 되어도 갱신 되지 않는 렌더를 보장 할 수 있으나, 같은 노드의 바인딩에도 영향을 준다.

raw HTML 데이터 바인딩

<template>
    <h1>Hello, {{ title }}</h1>
    <div v-html="htmlString"></div>
</template>

<script>
export default {
    data() {
        return {
            title: "DataBinding",
            htmlString: '<p style="color:red">This is a red string</p>'
        }
    }
}
</script>

v-html 디렉티브를 사용하면 html도 렌더링시 적용 되도록 할 수 있다.

<div v-bind:id="dynamicID"></div>

이중 중괄호 구문은 HTML 속성에 사용할 수 없다. 대신, v-bind를 사용할 수는 있다.

전달 인자

일부 디렉티브는 디렉티브 명 뒤에 콜론으로 표기되는 전달인자를 가질 수 있다.

<a v-bind:href="url"></a>

동적 전달인자

<a v-bind:[attributeName]="url"></a>

JavaScript 표현식을 대괄호로 묶어 디렉티브 전달인자로 사용할 수 있다.

동적인자는 null을 제외하고 string으로 변환되어야 한다.

<!-- 컴파일러 경고가 발생합니다. -->
<a v-bind:['foo' + bar]="value"> ... </a>

위와 같은 경우 computed 속성을 이용하면 된다. ref. https://v3.ko.vuejs.org/guide/computed.html#%E1%84%80%E1%85%B5%E1%84%87%E1%85%A9%E1%86%AB-%E1%84%8B%E1%85%A8%E1%84%8C%E1%85%A6

<!--
in-DOM 템플릿에서는 이 부분이 v-bind:[someattr]로 변환됩니다. 인스턴스에 "someattr" 속성이 없는 경우, 이 코드는 동작하지 않습니다.
-->
<a v-bind:[someAttr]="value"> ... </a>

수식어

디렉티브를 특별한 방법으로 바인딩해야 함을 나타낸다.

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

.prevent의 경우 트리거 된 이벤트에서 event.preventDefault()를 호출하도록 명시하는 것이다.

약어

v-bind, v-on에 대한 약어를 제공한다.

<!-- 전체 문법 -->
<a v-bind:href="url"> ... </a>

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

<!-- 동적 전달인자와 함께 쓴 약어 -->
<a :[key]="url"> ... </a>
<!-- 전체 문법 -->
<a v-on:click="doSomething"> ... </a>

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

<!-- 동적 전달인자와 함께 쓴 약어 -->
<a @[event]="doSomething"> ... </a>

v-if, v-show 차이

v-if는 조건식이 맞지 않으면, html 블록 자체가 생성 되지 않는다. 그런데, v-show는 일단 html 블록은 생성 되지만, 조건에 따라 display를 이용하는 차이가 있다.

어플리케이션 & 컴포넌트 인스턴스

모든 Vue 어플리케이션은 createApp함수를 사용하여 새로운 어플리케이션 인스턴스를 생성하여 시작한다.

const RootComponent = {}
const app = Vue.createApp({})
const vm = app.mount('#app')

어플리케이션 인스턴스에 의해 노출된 대부분의 메소드들은 동일한 인스턴스를 반환하여 chaining을 허용한다.
다만, mount() 함수의 경우 어플리케이션을 반환하지 않고, 루트 컴포넌트 인스턴스를 반환 한다.

라이프 사이클 훅

각 컴포넌트는 생성될 때 일련의 초기화 단계를 거친다. 모든 라이프 사이클 훅에서는 Vue인스턴스를 가리키는 this context와 함께 호출 된다.

options 속성이나 콜백에서 created: () => console.log(this.a)와 같은 arrow function을 사용하지 않는다. 이유는 arrow function은 context를 function 내부로 한정 하기 때문에, this가 없어서 this를 parent scope까지 모두 찾다가 없으면 오류를 발생시킨다.

라이프 사이클 다이어그램

Data 속성 / Method

컴포넌트의 data 옵션은 함수다. Vue는 새로운 컴포넌트 인스턴스 생성의 일환으로 data 함수를 호출한다.

const app = Vue.createApp({
  data() {
    return { count: 4 }
  }
})

const vm = app.mount('#app')

console.log(vm.$data.count) // => 4
console.log(vm.count)       // => 4

새로운 속성을 data에 포함하지 않고 컴포넌트 인스턴스에 직접 추가할 수 있습니다. 하지만, 이렇게 추가한 속성은 반응형 $data 객체로 처리되지 않기 때문에 Vue의 반응형 시스템에 의해 자동으로 추적되지 않습니다.

컴포넌트 인스턴스에 메서드를 추가하려면 methods 옵션을 사용하세요. methods 옵션은 동작하기를 원하는 메서드들이 담긴 하나의 객체여야 합니다.

const app = Vue.createApp({
  data() {
    return { count: 4 }
  },
  methods: {
    increment() {
      // `this`는 컴포넌트 인스턴스를 참조합니다.
      this.count++
    }
  }
})

const vm = app.mount('#app')

console.log(vm.count) // => 4

vm.increment()

console.log(vm.count) // => 5

Vue는 methods 안에서 컴포넌트 인스턴스를 항상 참조할 수 있도록 this 값을 자동으로 바인딩합니다. 이렇게 하면 메서드가 이벤트 리스너나 콜백으로 사용될 때, 올바른 this 값을 유지하게 됩니다. 화살표 함수를 사용해서 methods를 정의하면 Vue가 적절한 this 값을 바인딩하지 못합니다. 따라서 methods를 정의할 때, 화살표 함수를 사용하지 않도록 합니다.

Computed 속성 / watch

<div id="computed-basics">
  <p>출판된 책:</p>
  <span>{{ publishedBooksMessage }}</span>
</div>
Vue.createApp({
  data() {
    return {
      author: {
        name: '존 도우',
        books: [
          'Vue 2 - Advanced Guide',
          'Vue 3 - Basic Guide',
          'Vue 4 - The Mystery'
        ]
      }
    }
  },
  computed: {
    // computed getter
    publishedBooksMessage() {
      // 여기서의 `this` 는 vm 인스턴스이다.
      return this.author.books.length > 0 ? '있음' : '없음'
    }
  }
}).mount('#computed-basics')

computed 속성 대신에 메서드와 동일한 함수를 정의할 수 있습니다. 최종 결과를 위해, 두가지 접근 방식은 정확히 동일합니다. 그러나 차이점은 computed 속성은 반응형(reactive) 종속성에 기반하여 캐시된다는 것 입니다. computed 속성은 반응형 종속성 중 일부가 변경된 경우에만 재평가됩니다. 즉, author.books 가 변경되지 않는다면 publishedBooksMessage computed 속성에 대해 여러번 접근하더라도 함수를 다시 실행할 필요없이 이전에 계산된 결과를 즉시 반환합니다.

Watch

데이터 변경에 대한 응답으로 비동기 혹은 비용이 많이 드는 작업을 수행하려는 경우가 가장 유용합니다.

<!-- 이미 Ajax 라이브러리의 풍부한 생태계와 범용 유틸리티 메소드 컬렉션이 있기 때문에, -->
<!-- Vue 코어는 다시 만들지 않아 작게 유지됩니다. -->
<!-- 이것은 이미 익숙한 것을 선택할 수 있는 자유를 줍니다. -->
<script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>
<script>
  const watchExampleVM = Vue.createApp({
    data() {
      return {
        question: '',
        answer: '질문은 보통 물음표를 포합합니다. ;-)'
      }
    },
    watch: {
      // question 이 변경될 때마다, 이 함수가 실행될 것 입니다.
      question(newQuestion, oldQuestion) {
        if (newQuestion.indexOf('?') > -1) {
          this.getAnswer()
        }
      }
    },
    methods: {
      getAnswer() {
        this.answer = '생각중...'
        axios
          .get('https://yesno.wtf/api')
          .then(response => {
            this.answer = response.data.answer
          })
          .catch(error => {
            this.answer = '에러! API에 닿지 못했습니다. ' + error
          })
      }
    }
  }).mount('#watch-example')
</script>
반응형