4일차 : vue 문법

해피데빙·2022년 9월 6일
0

vue

목록 보기
5/9
  • script : js 등 로직
  • template : html
  • style :css

html 제외하고 모두 import해서 사용 가능

declarative rendering

script
export default{
	data(){ 
    	return { 
        //여기에서 선언한 값들은 모두 그냥 변수가 아니라 reactive state
       //reactive : state that can trigger updates when changed
       //data component 안에서 사용된다
        }
    }
}
template
<h1>{{ message }}</h1>
<h1>{{ message.split('').reverse().join('') }}</h1>

{{}} 안에 사용할 수 있는 것들

  • 변수
  • path
  • js 문법

Attribute Bindings

  • 태그에 정적인 속성을 쓰고 싶을 때 : <태그이름 속성='속성이름'/>
    ex. h1 class='className'
  • 동적인 속성을 쓰고 싶을 때 : v-bind를 통해 바인딩을 시킨다. 즉 속성이 state같은 상태 변수일 때
    ex. div v-bind:id="id이름"

cf. directive
v-접두사를 가진 특별한 속성
Vue의 template 문법
text interpolation({{}})와 마찬가지로 컴포넌트의 상태에 접근할 수 있는 자바스크립트 문법들
ex. v-bind, v-for 등

<script>
export default {
  data() {
    return {
      titleClass: 'title'
    }
  }
}
</script>

<template>
  <h1 :class="title">Make me red</h1> 
  <!-- add dynamic class binding here -->
  <h1 class='name'>
    name
  </h1>
</template>

<style>
.title {
  color: red;
}
  .name{ 
    color: blue;
  }
</style>

Event Listener

v-on:event ="method이름"
@event="method이름"

click의 경우 아래 둘 다 가능

<button v-on:click="increment">{{ count }}</button>
<button @click="increment">{{ count }}</button

이렇게 method함수들은 script 태그 안의 export default{}안의 methods:{}를 사용한다
methods안에 정의한 함수들은 this를 이용해 data 안에 정의한 값들에 접근한다

export default {
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      // update component state
      this.count++ 
    }
  }
}

form binding

  • v-model : v-bind + v-on
    syncs input value with the bound state
    text input, checkbox, radio buttons, select dropdowns 등 다른 형태의 input에도 잘 된다

Conditional Rendering

<h1 v-if="awesome">Vue is awesome!</h1>
  • will be rendered only if the value of awesome is truthy.
    If awesome changes to a falsy value, it will be removed from the DOM.

  • We can also use v-else and v-else-if to denote other branches of the condition:

<script>
export default {
  data() {
    return {
      awesome: true
    }
  },
  methods: {
    toggle() {
     this.awesome = !this.awesome
    }
  }
}
</script>

<template>
  <button @click="toggle">toggle</button>
  <h1 v-if="awesome">Vue is awesome!</h1>
  <h1 v-else>Oh no 😢</h1>
</template>

List Rendering

  • v-for directive : render a list of elements based on a source array:
  • variable representing the array element currently being iterated on. It's only accessible on or inside the v-for element, similar to a function scope.
<ul>
  <li v-for="todo in todos" :key="todo.id"> //unique id, binded with key attribute(v-bind:key) 
    {{ todo.text }}
  </li>
</ul>

how to update the list

  • call mutating methods(ex.push) on the source array
this.todos.push(newTodo)
  • replace the array with a new one
this.todos = this.todos.filter(~)

Computed property

  • computed: {다른 속성들에 반응하는 속성, 함수들을 설정한다}
    We can declare a property that is reactively computed from other properties using the computed option:
    즉, 직접 computed의 메서드를 사용하지
<script>
let id = 0

export default {
  data() {
    return {
      newTodo: '',
      hideCompleted: false,
      todos: [
        { id: id++, text: 'Learn HTML', done: true },
        { id: id++, text: 'Learn JavaScript', done: true },
        { id: id++, text: 'Learn Vue', done: false }
      ]
    }
  },
  computed: {
    filteredTodos(){
      return this.hideCompleted? 
        this.todos :
      this.todos.filter(el => el.done === false)
      
    }
  },
  methods: {
    addTodo() {
      this.todos.push({ id: id++, text: this.newTodo, done: false })
      this.newTodo = ''
    },
    removeTodo(todo) {
      this.todos = this.todos.filter((t) => t !== todo)
    }
  }
}
</script>

<template>
  <form @submit.prevent="addTodo">
    <input v-model="newTodo" />
    <button>Add Todo</button>
  </form>
  <ul>
    <li v-for="todo in filteredTodos" :key="todo.id">
      <input type="checkbox" v-model="todo.done">
      <span :class="{ done: todo.done }">{{ todo.text }}</span>
      <button @click="removeTodo(todo)">X</button>
    </li>
  </ul>
  <button @click="hideCompleted = !hideCompleted">
    {{ hideCompleted ? 'Show all' : 'Hide completed' }}
  </button>
</template>

<style>
.done {
  text-decoration: line-through;
}
</style>
  • v-for : todo in todos 를 통해 iterable
  • v-bind:class = {done: todo.done}을 이용해서 class이름을 done으로, 값을 todo.done으로 할 수 있다
    ->속성의 값을 하나하나 동적으로 줄 수도 있지만 이렇게 여러 개를 동시에 동적으로 줄 수도 있다.
  • @click에 대한 이벤트 핸들러를 바로 ""안에 자바스크립트 문법으로 쓸 수 있다
    -computed{}안에 속성/메서드를 넣으면 data와는 다르게

두 코드 모두 구조가 비슷하지만

  • method는 랜더링을 할때마다 항상 메서드를 호출해야하지만,
  • computed는 관련된 값이 변했을 경우만 다시 랜더링 한다
    즉 관련된 값이 변하지 않았을 때 재호출을 할 경우 이전의 연산한 값을 리턴한다.

Lifecycle and Template Refs

Vue는 DOM의 업데이트를 다 알아서 해준다.
하지만 직접 조작해야 하는 경우도 있다
이때는 template에서 ref를 써주면 된다
이는 html 코드에 대해 참조한다
script에서는 this.$refs.태그이름 으로 접근할 수 있다
하지만 앱이 mount가 된 다음에 접근할 수 있다
그리고 위처럼 접근하기 위해서는 script내에서 mounted(){}를 통해 접근해야 한다

이를 lifecycle hook라고 한다. 이는 컴포넌트의 특정한 라이프사이클에서 콜백 함수를 등록할 수 있게 한다. created, updated 같은 다른 hook들도 있다

<script>
export default {
 mounted(){ 
   this.$refs.p.textContent = 'this'
 }
}
</script>

<template>
  <p ref="p">hello</p>
</template>

Watchers

side effect(순수 함수 외의 동작들)에 반응해야 할 때 사용한다
trigger a function whenever a reactive property changes

export default {
 data() {
   return {
     count: 0
   }
 },
 watch: {
   count(newCount) {
     // yes, console.log() is a side effect
     // count라는 reactive property에 대한 함수
     console.log(`new count is: ${newCount}`)
   }
 }
}
<script>
export default {
  data() {
    return {
      todoId: 1,
      todoData: null
    }
  },
  methods: {
    async fetchData() {
      this.todoData = null
      const res = await fetch(
        `https://jsonplaceholder.typicode.com/todos/${this.todoId}`
      )
      this.todoData = await res.json()
    }
  },
  watch: { 
    todoId(newId){ 
      //todoId를 가지고 이에 맞는 todo를 가져온다 
      //newId에 바뀐 todoId가 들어간다
      //또 mount할 때만 fetchData해오니까 여기서도
     ~~ this.todoId = newId ~~
      //이건 필요 없음. 아래 클릭할 때마다 ++을 직접해주고 있기 때문에
      this.fetchData()
    }
  },
  mounted() {
    this.fetchData()
  }
}
</script>

<template>
  <p>Todo id: {{ todoId }}</p>
  <button @click="todoId++">Fetch next todo</button>
  <p v-if="!todoData">Loading...</p>
  <pre v-else>{{ todoData }}</pre>
</template>

Components

  • nested components
  • 부모 컴포넌트는 자신의 template에 자식 컴포넌트를 렌더링할 수 있다
  • 먼저 import해와야 한다
  • components라는 옵션을 사용해서 부모 컴포넌트에 등록한다
script
import ChildComp from './ChildComp.vue'
export default {
  components: {
    ChildComp
  }
}

이제 template에서 사용할 수 있다

<ChildComp />

Props

  • 자식 컴포넌트는 props로 부모에게서 데이터를 받을 수 있다
  1. 받을 Props를 선언한다
// in child component
export default {
  props: {
    msg: String
  }
}

2.이후 child component에서 받을 때 사용할 수 있다
동적인 속성을 사용할 때는 부모 컴포넌트에서 v-bind로, 정적인 속성은 그냥 그대로

<ChildComp :msg="greeting" />

Emits

  • react와 달리 vue는 자식이 부모에게 이벤트를 보낼 수 있다
  • this.$emit()의 첫번째 매개변수는 이벤트 이름, 이외 매개변수들은 event listener를 통해 전달된다
  • 부모는 v-on을 이용해 자식이 emit한 이벤트들을 받을 수 있다
  • handler는 자식 컴포넌트에서 받은 argument를 이용해 로컬 state에 배정한다
부모 컴포넌트
<script>
import ChildComp from './ChildComp.vue'

export default {
  components: {
    ChildComp
  },
  data() {
    return {
      childMsg: 'No child msg yet'
    }
  }
}
</script>

<template>
  <ChildComp @response='(emit)=>childMsg =emit' />
  //response가 이벤트 이름
//childMsg라는 부모가 설정해둔 변수로 이름이 들어간다
  <p>{{ childMsg }}</p>
</template>
ChildComp
export default {
  // declare emitted events
  emits: ['response']
  created() {
    // emit with argument
    this.$emit('response', 'hello from child')
    //이벤트 이름, 나머지는 부모 컴포넌트의 이벤트 리스너로 넘어간다 (즉 아래 msg)
  }
}

Slot

  • props : data를 자식에게 넘길 때 사용
  • slots : template 조각을 자식에게 넘길 때 사용

자식 컴포넌트의 children 값으로 넣은 텍스트가 ChildComp에서 보여진다

부모 컴포넌트
<template>
  <ChildComp>slot content</ChildComp>
</template>
자식 컴포넌트 
<template>
  <slot></slot>
</template>

화면에 slot content라고 나온다

profile
노션 : https://garrulous-gander-3f2.notion.site/c488d337791c4c4cb6d93cb9fcc26f17

0개의 댓글