[Vue] Vue.js instance 문법

작성:    

업데이트:

카테고리:

태그: ,

Vue.js의 기본 문법

Vue instance

  • 모든 Vue 앱은 Vue 함수로 새 인스턴스를 만드는 것부터 시작
  • Vue 인스턴스를 생성할 때는 Options 객체 전달
  • 여러 Options들을 사용하여 원하는 동작 구현
  • Vue Instance === Vue Component
const app = new Vue({

})


Options/DOM

Vue Instance의 Options 객체에서 DOM을 구성하는 요소들을 살펴보자.


가. el

  • Vue 인스턴스에 연결(마운트)할 기존 DOM 요소 필요
  • CSS 선택자 문자열 or HTML Element로 작성
  • new를 이용한 인스턴스 생성 때만 사용
const app = new Vue({
  el: '#app'
})


나. data와 methods

  • data : Vue 인스턴스의 데이터 객체, Vue 인스턴스의 상태 데이터 정의
  • methods : Vue 인스턴스에 추가할 메서드

  • Vue template에서 interpolation을 통해 접근 가능
  • v-bind, v-on과 같은 directive에서도 사용 가능
  • Vue 객체 내 다른 함수에서 this 키워드를 통해 접근 가능(this === 해당 vue 인스턴스)
const app = new Vue({
  el: '#app',
  data: {
    message: 'Hello',
  },
  methods: {
    greeting: function () {
      console.log('hello')
    }
  }
})


주의📢 화살표 함수를 메서드 정의에 사용하지 않는다.

화살표 함수가 부모 context를 binding하기 때문에 ‘this’가 Vue instance가 아니게 된다.


Template 문법

  • 렌더링된 DOM을 기본 Vue 인스턴스의 데이터에 선언적으로 바인딩할 수 있는 HTML 기반 템플릿 구문 사용
  1. Interpolation
  2. Directive


가. Text
<span>메시지: {{ msg }}</span>

나. Raw HTML
<span v-html="rawHtml"></span>

다. Attribute
<div v-bind:id="dynamicId"></div>

라. JS 표현식
{{ number + 1 }}
{{ message.split('').reverse().join('') }}


Directive

  • v- 접두사가 있는 특수 속성
  • 속성 값은 단일 JS 표현식이 됨 (v-for은 예외)
  • 표현식의 값이 변경될 때 반응적으로 DOM에 적용하는 역할


전달인자(Arguments)

  • :(콜론) 을 통해 전달인자를 받을 수 있음
<a v-bind:href="url">...</a>
<a v-on:click="dosomething">...</a>


수식어(Modifiers)

  • .(점) 으로 표시되는 특수 접미사
  • directive를 특별한 방법으로 바인딩해야함을 의미
<form v-on:submit.prevent="onSubmit"> ... </form>


가. v-text

  • element의 textContent를 업데이트
  • 내부적으로 interpolation 문법이 v-text로 컴파일
<p v-text="message"></p>
<p>{{ message }}</p>

위의 두 코드는 같다.


나. v-html

  • element의 innerHTML을 업데이트
  • XSS 공격에 취약
  • 임의로 사용자로부터 입력 받은 내용은 v-html에 절대 사용 금지
<div v-html="myHtml"></div>
...
data: {
  myHtml: '<b>Hello</b>'
}


다. v-show

  • 조건부 렌더링 중 하나
  • 속성값은 boolean type data, true이면 display, false이면 display:none
  • 요소는 항상 렌더링, DOM에 있음
  • 단순히 element에 display 속성을 toggle
<div id="app">
  <p v-show="isTrue">
    true
  </p>
  <p v-show="isFalse">
    false
  </p>
</div>


라. v-if, v-else-if, v-else

  • 조건부 렌더링 중 하나
  • directive의 표현식이 false라면 렌더링 자체가 되지 않음
  • element 및 포함된 directive는 toggle하는 동안 삭제되고 다시 작성
<div id="app">
  <div v-if="myType === 'A'">
    A
  </div>
  <div v-else-if="myType === 'B'">
    B
  </div>
  <div v-else-if="myType === 'C'">
    C
  </div>
  <div v-else>
    Not A/B/C
  </div>
</div>


v-show와 v-if

v-show (Expensive initial load, cheap toggle)

  • CSS display 속성을 hidden으로 만들어 toggle
  • 실제로 렌더링은 되지만 눈에 보이지 않음
  • 딱 한 번만 렌더링이 되는 경우 v-if에 비해 상대적으로 높은 렌더링 비용
  • 자주 변경되는 요소의 경우 한 번 렌더링된 이후 토글만 하면 되므로 비용 감소


v-if (Cheap initial load, expensive toggle)

  • 전달인자가 false인 경우 렌더링 자체가 되지 않으므로 렌더링 비용 낮음
  • 자주 변경되는 요소의 경우 매번 다시 렌더링 해야하므로 비용 증가


결론

  • 자주 변경될 거라면 v-show, 한 번만 쓸 거라면 v-if를 쓰자.
  • v-show는 안 보여도 렌더링, v-if는 안 보이면 렌더링 X


마. v-for

  • 원본 데이터를 기반으로 element 또는 템플릿 블록을 여러 번 렌더링
  • item in items 구문 사용
  • item 위치의 변수를 각 요소에서 사용 가능
  • 객체의 경우 key

  • v-for 사용 시 반드시 key 속성을 각 요소에 작성
  • v-if와 함께 사용하는 경우 v-for가 우선순위가 더 높음
  • 단, 되도록 v-if와 v-for은 동시 사용 지양


<body>
  <div id="app">
    <h2>String</h2>
    <div v-for="char in myStr">
      {{ char }}
    </div>

    <h2>Array</h2>
    <div v-for="fruit in fruits">
      {{ fruit }}
    </div>

    <div v-for="(fruit, idx) in fruits">
      {{ idx }} => {{ fruit }}
    </div>

    <div v-for="todo in todos" :key="todo.id">
      <p>{{ todo.title }} => {{ todo.completed }}</p>
    </div>
    
    <h2>Object</h2>
    <!-- value, key 순서임에 주의한다. -->
    <div v-for="(value, key) in myObj">
      {{ key }} => {{ value }}
    </div>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        myStr: 'Hello World!',
        fruits: ['apple', 'banana', 'coconut'],
        todos: [
          { id: 1, title: 'todo1', completed: true},
          { id: 2, title: 'todo2', completed: false},
          { id: 3, title: 'todo3', completed: true},
        ],
        myObj: {
          name: 'Smith',
          age: 25,
        }
      }
    })
  </script>
</body>


바. v-on

  • element에 EventListener 연결
  • 이벤트 유형은 전달인자로 표시
  • 특정 이벤트가 발생했을 때, 주어진 코드 실행
  • shorthand
    • @
    • v-on:click → @click


<body>
  <div id="app">
    <!-- 메서드 핸들러 -->
    <!-- v-on:의 축약 : @ -->
    <button v-on:click="alertHello">Button</button>
    <button @click="alertHello">Button</button>

    <!-- 기본 동작 방지 -->
    <!-- @submit.prevent -->
    <form action="" @submit.prevent="alertHello">
      <button>GoGo</button>
    </form>

    <!-- 키 별칭을 이용한 키 입력 수식어 -->
    <!-- space, enter 등을 keyup 했을 때에만 함수 실행 -->
    <input type="text" @keyup.enter="log">

    <!-- callback 함수에서의 특수문법 () -->
    <!-- 'a'를 인자로 넣어 바로 실행한다. 라는 의미가 아닌, --> 
    <!-- 실행할 때 1번 인자를 'a'로 넣겠다는 의미 --><input type="text" @keyup.enter="log('a')">

    <p>{{ message }}</p>
    <button @click="changeMessage">change message</button>
  </div>
  
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        message: 'Hello Vue',
      },
      methods: {
        alertHello: function () {
          alert('hello')
        },
        log: function (event) {
          console.log(event)
        },
        changeMessage() {
          this.message = new Date()
        }
      }
    })
  </script>
</body>


사. v-bind

  • HTML 요소의 속성에 Vue의 상태 데이터를 값으로 할당
  • Object 형태로 사용하면 value가 true인 key가 class 바인딩 값으로 할당
  • shorthand
    • :(colon)
    • v-bind:href => :href
<head>
  ...
  <style>
    .active {
      color: red;           
    }

    .my-background-color {
      background-color: yellow;
    }
  </style>
</head>
<body>
  <!-- v-bind : 기본 속성이라 v- 안되는 속성들을 v- 엮어주는 방법 -->
  <div id="app">

    <!-- 속성 바인딩 -->
    <!-- <img v-bind:src="imageSrc" alt=""> -->
    <img :src="imageSrc" alt="">
    <hr>

    <!-- 클래스 바인딩 -->
    <!-- isRed가 true일 때만 active 클래스 활성화하겠다. -->
    <div :class="{ active: isRed }">
      클래스 바인딩
    </div>
    <hr>

    <h3 :class="[activeRed, myBackground]">
      hello vue
    </h3>

    <!-- 스타일 바인딩 -->
    <p :style="{ fontSize: 20px }">
      this is 
    </p>
  </div>
  
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        fontSize: 16,
        altMsg: 'this is image',
        imageSrc: 'https://picsum.photos/id/310/200/300',
        isRed: true,
        activeRed: 'active',
        myBackground: 'my-background-color',
      }
    })
  </script>


아. v-model

  • HTML form 요소의 값과 data를 양방향 바인딩
  • 수식어
    • .lazy : input 대신 change 이벤트 이후에 동기화
    • .number : 문자열 → 숫자
    • .trim : 입력에 대한 trim 진행
<body>
  <div id="app">
    <h2>Input -> Data 단방향</h2>
    <p>{{ msg1 }}</p>
    <input type="text" @input="onInputChange">
    <hr>

    <h2>Input <-> Data 양방향</h2>
    <p>{{ msg2 }}</p>
    <input type="text" v-model="msg2">

    <hr>

    ! <input id="box" type="checkbox" v-model="checked">
    <label for="box">{{ checked }}</label>




  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        msg1: '111',
        msg2: '222',
        checked: true,
      },
      methods: {
        onInputChange (event) {
          this.msg1 = event.target.value
        },
      },
    })
  </script>
</body>


Options/Data

computed

  • 데이터를 기반으로 하는 계산된 속성
  • 함수의 형태로 정의 BUT 함수의 반환값이 바인딩
  • computed 구성을 위해 종속된 데이터에 따라 저장(캐싱)
  • 종속된 데이터가 변경될 때만 함수 실행
  • 어떤 데이터에도 의존하지 않는 computed 속성의 경우 불변
  • 반드시 반환값이 있어야 한다.
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        r: 2,
        // area: 2*2*3.14,
        // perim: 2*2*3.14,
      },
      computed: {
        area: function () {
          return this.r ** 2 * 3.14
        },
        perim: function () {
          return 2 * this.r * 3.14
        },
      }
      // watch: {
      //   area: function () {
      //     this.area = this.r * this.r * 3.14
      //   }
      //   perim: function () {
      //     this.perim = 2 * this.r * 3.14
      //   }
      // }
    })
  </script>


computed와 methods

  • computed 속성 대신 methods에 함수를 정의 가능
  • 최종 결과에 대해 두 가지 접근 방식은 서로 동일
  • 차이점은 computed 속성은 종속 대상을 따라 저장(캐싱)

  • computed는 종속된 대상이 변경되지 않는 한 computed에 작성된 함수를 여러 번 호출해도 계산을 다시 하지 않고 계산되어 있던 결과 반환
  • ⭐ 요약 : 변경 없이 여러 번 호출 → methods는 매번 함수 실행, computed는 재계산 없이 캐시값 반환
<script>
  const app = new Vue({
    el: '#app',
    data: {
      sessionId: '',
      message: 'Original'
    },
    // render될 때마다 함수를 재실행
    // -> data를 바꾸는 로직 위주 (setter)
    methods: {
      reverseMessage () {
        return this.message.split('').reverse().join('')
      }
    },
    // (data에 의존하는) 계산된 값
    // 종속 대상을 따라 저장(캐싱)된다.
    // data를 통해 값을 얻는 경우 사용 (getter)
    computed: {
      reversedMessage () {
        return this.message.split('').reverse().join('')
      },
      isLoggedIn () {
        // sessionId의 변화가 없다면 언제 호출되어도 캐시값 반환
        return this.sessionId ? true : false
      }
    },
  })
</script>


watch

  • 데이터를 감시
  • 데이터에 변화가 일어났을 때 실행되는 함수
const app = new Vue({
  el: '#app',
  data: {
    num: 2,
  },
  watch: {
    num: function () {
      console.log(`${this.num}이 변경되었습니다.`)
    }
  },
})
  • this.num(내부 종속 대상)이 바뀌면 num 함수 실행
  • react의 useEffect와 같다.


computed와 watch

computed

  • 특정 데이터를 직접적으로 사용/가공 → 다른 값으로 만들 때 사용
  • 종속 데이터의 원본 값은 바꾸지 않고 새로운 값을 ‘계산’하는 것
  • 속성은 계산해야 하는 목표 데이터를 정의하는 방식
  • “A 값이 A2로 변하면 이전에 A로부터 계산됐던 B 값을 다시 계산해 B2 값을 만들어 보여준다.”
  • 선언형 프로그래밍 방식 → “계산해야 하는 목표 데이터 정의”


watch

  • 특정 데이터의 변화 상황에 맞춰 다른 data 등이 바뀌어야 할 때 사용
  • 감시할 데이터를 지정, 해당 데이터가 바뀌면 특정 함수 실행
  • “A 값이 변하면 B 함수를 실행해 작업한다.”
  • 특정 대상이 변경되었을 때 콜백 함수를 실행시키기 위한 트리거
  • 명령형 프로그래밍 방식 → “데이터가 바뀌면 특정 함수 실행해!”


filter

  • 텍스트 형식화를 적용할 수 있는 필터
  • interpolation 혹은 v-bind를 이용할 때 사용 가능
  • JS 표현식의 마지막에 ** **(pipe)와 함께 추가
  • chaining 가능
<body>
  <div id="app">
    <div>{{ numbers }}</div>
    <div>{{ numbers | getOddNumbers }}</div>
    <div>{{ numbers | getOddNumbers | getUnderTen }}</div>
    <div>{{ getOddAndUnderTen }}</div>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        numbers: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
      },
      filters: {
        getOddNumbers(array) {
          return array.filter(num => num % 2)
        },
        getUnderTen(array) {
          return array.filter(num => num < 10)
        }
      },
      computed: {
        getOddAndUnderTen() {
          return this.numbers.filter(num => num%2 && num<10)
        }
      }
    })
  </script>
</body>

댓글남기기