/ #JAVASCRIPT#VUE.JS

Vue.js: option

이번 글에서는 Vue 인스턴스의 옵션에 대해 알아보겠습니다.



el

el 옵션은 Vue 인스턴스를 DOM에 바인딩 하기위한 옵션입니다. css 선택자를 통해 el 옵션의 값을 설정해주면 해당 선택자에 대한 DOM에 Vue 인스턴스가 바인딩됩니다.

index.html
<!DOCTYPE html>
<html lang="ko">
<head>
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script defer src="./main.js"></script>
  <title>Vue 연습</title>
</head>
<body>
  <div id="app"></div>
</body>
</html>
main.js
const mv = new Vue({
  el: '#app'
})

index.html 파일을 열고 콘솔창에 mv를 입력하면 Vue 인스턴스가 생성되어 있습니다.

img005

해당 Vue 인스턴스는 id가 app인 div DOM에 바인딩되어 있으며, $el 프로퍼티를 통해 확인가능합니다.

img006

data

data 옵션은 Vue 인스턴스가 관리할 데이터를 정의합니다. 해당 옵션에 정의된 데이터는 Model에 해당하며, Vue 인스턴스는 모델 데이터의 변경을 감지하여 View를 업데이트하게 됩니다.

앞서 설명했듯이 data는 Object 또는 Object를 리턴하는 함수 형태로 정의할 수 있습니다.

main.js (Object로 정의)
const mv = new Vue({
  data: {
    message: 'Hello, world!'
  }
}).$mount('#app')
main.js (Function으로 정의)
const mv = new Vue({
  data() {
    return {
      message: 'Hello, world!'
    }
  }
}).$mount('#app')

data 옵션에 정의된 모델은 Vue 인스턴스의 $data 프로퍼티를 통해 접근할 수 있으며, Vue 인스턴스가 모델의 프록시가 되어 모델 속성의 이름으로 Vue 인스턴스를 통해서도 접근할 수도 있습니다.

Vue 인스턴스는 모델을 프록시하여 모델 속성값을 통해 $data 프로퍼티와 같은 데이터를 참조

img004

methods

methods 옵션은 Vue 인스턴스 내부에서 사용하기위한 메소드를 정의합니다. 저번 글에서 v-on 디렉티브를 통해 클릭 이벤트에 메소드를 바인딩했습니다.

index.html
<!DOCTYPE html>
<html lang="ko">
<head>
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script defer src="./main.js"></script>
  <title>Vue 연습</title>
</head>
<body>
  <div id="app">
    <div>{{ num }}</div>
    <button v-on:click="increase">증가</button>
    <button v-on:click="decrease">감소</button>
  </div>
</body>
</html>
main.js
const mv = new Vue({
  data: {
    num: 0
  },
  methods: {
    increase() {
      this.num++
    },
    decrease() {
      this.num--
    }
  }
}).$mount('#app')
출력 결과

img25

data 옵션과 마찬가지로 Vue 인스턴스는 해당 옵션에 정의된 메소드를 프록시하고 있어서 Vue 인스턴스의 프로퍼티를 통해 메소드의 호출이 가능합니다.

Vue 인스턴스를 통한 메소드 호출

img26

즉, method 옵션에 선언된 메소드 또한 Vue 인스턴스가 관리하기 때문에 DOM에 바인딩할 수 있습니다. 만약 메소드 내부에서 사용하는 변수가 변경되어 View가 업데이트 된다면, 바인딩된 메소드는 자동으로 호출됩니다.

index.html
<!DOCTYPE html>
<html lang="ko">
<head>
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script defer src="./main.js"></script>
  <title>Vue 연습</title>
</head>
<body>
  <div id="app">
    <div>{{ calcNum() }}</div>
    <button @click="num++">증가</button>
    <button @click="num--">감소</button>
  </div>
</body>
</html>
main.js
const mv = new Vue({
  data: {
    num: 0
  },
  methods: {
    calcNum() {
      console.log("num: " + this.num)
      return this.num
    }
  }
}).$mount('#app')

위의 코드와 다르게 메소드를 2개로 정의한 것이 아니라 클릭 이벤트에 의하여 num 데이터를 증가시키거나 감소시키며, calcNum 메소드의 리턴값을 바인딩했습니다.

num 데이터가 변경되면 바인딩된 calcNum 메소드가 호출되어 View가 업데이트 됩니다.

출력 결과

img27

calcNum 메소드가 호출되면 콘솔 로그가 출력되는데, 이벤트를 통해 num 데이터 값을 변경시킬 때마다 calcNum 메소드가 호출되는 것을 확인할 수 있습니다.

computed

computed 옵션은 methods 옵션과 동일한 기능을 하지만 차이점이 있습니다.

우선 메소드의 바인딩 방식에 차이가 있습니다.

index.html
<!DOCTYPE html>
<html lang="ko">
<head>
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script defer src="./main.js"></script>
  <title>Vue 연습</title>
</head>
<body>
  <div id="app">
    <div>{{ calcNum }}</div>
    <button @click="num++">증가</button>
    <button @click="num--">감소</button>
  </div>
</body>
</html>
main.js
const mv = new Vue({
  data: {
    num: 0
  },
  computed: {
    calcNum() {
      console.log("num: " + this.num)
      return this.num
    }
  }
}).$mount('#app')

methods의 경우 명령형의 관점으로 calcNum()을 바인딩한 반면, computed는 선언형 관점으로 메소드 이름인 calcNum만 바인딩합니다. 결과를 확인하면 methods와 동일하게 calcNum 메소드가 호출됩니다.

출력 결과

img28

동작 방식에도 차이점이 존재합니다. methods는 View가 업데이트될 때마다 호출되는 반면, computed는 리턴값이 캐싱되어 내부 로직에 변화가 있을때만 호출됩니다.

예를 들어 calcNum 메소드가 2 * this.num이라는 값을 리턴한다면 methods는 View가 업데이트 될 때마다 calcNum이 호출되어 2 * this.num을 연산한 후 View에 출력될 것입니다.

반면에 computed는 num 데이터에 변경이 있어야만 calcNum이 호출되고 리턴값은 캐싱되어 num 데이터에 변경이 없다면 View가 업데이트 되어도 calcNum이 호출되지 않고 캐싱된 값이 View에 출력됩니다.

우선 아래와 같이 코드를 작성합시다.

index.html
<!DOCTYPE html>
<html lang="ko">
<head>
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script defer src="./main.js"></script>
  <title>Vue 연습</title>
</head>
<body>
  <div id="app">
    <div>
      <div>{{ calcMethodsNum() }}</div>
      <button @click="methodsNum++">증가</button>
      <button @click="methodsNum--">감소</button>
    </div>
    <hr>
    <div>
      <div>{{ calcComputedNum }}</div>
      <button @click="computedNum++">증가</button>
      <button @click="computedNum--">감소</button>
    </div>
  </div>
</body>
</html>
main.js
const mv = new Vue({
  data: {
    computedNum: 0,
    methodsNum: 0
  },
  methods: {
    calcMethodsNum() {
      console.log("methodsNum: " + this.methodsNum)
      return this.methodsNum
    }
  },
  computed: {
    calcComputedNum() {
      console.log("computedNum: " + this.computedNum)
      return this.computedNum
    }
  }
}).$mount('#app')

우선 methodsNum을 변경시켜봅니다. 아래 결과를 보면 methodsNum에 대한 로그가 출력되므로, calcMethodsNum이 호출된다는 것을 확인할 수 있습니다.

출력 결과

img29

다음으로 computedNum을 변경시켜봅시다. 아래 결과를 보면 computedNum에 대한 로그 뿐만 아니라 methodsNum에 대한 로그도 함께 출력됩니다. 즉, calcComputedNum이 호출될 뿐만 아니라 calcMethodsNum도 호출됩니다.

출력 결과

img30

이것은 methodsNum에 변경이 없더라도 computedNum의 변경에 의해 View가 업데이트 되기 때문입니다.

이러한 동작방식의 차이로 인해 메소드를 바인딩해야 한다면 methods 보다는 computed로 메소드를 선언하여 바인딩 하는 것이 성능이 좋습니다.


computed를 통해 getter와 setter를 따로 선언할 수도 있습니다.

index.html
<!DOCTYPE html>
<html lang="ko">
<head>
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script defer src="./main.js"></script>
  <title>Vue 연습</title>
</head>
<body>
  <div id="app">
    <div>{{ calcNum }}</div>
    <button @click="num++">증가</button>
    <button @click="num--">감소</button>
  </div>
</body>
</html>
main.js
const mv = new Vue({
  data: {
    num: 0
  },
  computed: {
    calcNum: {
      set(num) {
        this.num = num;
      },
      get() {
        return 2 * this.num;
      }
    }
  }
}).$mount('#app')

computed에 calcNum의 getter와 setter를 따로 선언했습니다. getter의 경우 num 데이터에 2를 곱한 값을 리턴하며, setter의 경우 num 데이터를 파라미터 num값으로 변경합니다.

출력 결과

img31

View에 바인딩할 경우 getter를 통해 calcNum에 캐싱된 값을 반환하고, setter를 통해 calcNum을 설정하면 num 데이터가 설정값으로 변경되어 calNum의 getter를 호출하고 View를 업데이트합니다.

computred 속성을 getter와 setter로 나눌 경우 아직 설명하지 않은 v-model 디렉티브를 설정할 때 유용한데, 다음에 확인해보도록 하겠습니다.

watch

watch 옵션은 Vue 인스턴스의 프로퍼티 변화를 감지하여 특정 콜백 함수를 호출합니다. 해당 콜백 함수는 파라미터로 변경후 값(newVal)과 변경전 값(oldVal)을 받습니다.

index.html
<!DOCTYPE html>
<html lang="ko">
<head>
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script defer src="./main.js"></script>
  <title>Vue 연습</title>
</head>
<body>
  <div id="app"></div>
</body>
</html>
main.js
const mv = new Vue({
  data: {
    message: 0
  },
  watch: {
    message: function(newVal, oldVal) {
      console.log("newVal: " + newVal, "oldVal: " + oldVal)
    }
  }
}).$mount('#app')
출력 결과

img32

methods나 computed를 통해서도 데이터 변경을 감지할 수 있지만 데이터 타입이 Object인 경우 그 속성의 변화까지는 감지하지 못합니다. watch의 경우 deep옵션을 통해 Object 속성의 변화까지 감지할 수 있습니다.

main.js
const mv = new Vue({
  data: {
    obj: { a: 'aaa' }
  },
  watch: {
    obj: {
      handler(newObj) {
        console.log("변경값: " + newObj.a)
      },
      deep: true
    }
  }
}).$mount('#app')
출력 결과

img33