vue 기초(4) : computed, watch

김민지·2022년 9월 2일

프론트

목록 보기
6/13

간단한 todo app

  • 만약 id값이 없는 data가 들어온다면? 근데 그 data를 반복문 돌려야한다면?
  • 그리고 그 데이터가 반응성도 가지고 있어야한다면? -> computed를 사용하자
return {
  ...todo,
  id: index+1,
  done: false
}
return Object.assign(
  {}, todo, {
  id: index+1,
  done: false
})
  • Object.assign : 새로운 객체를 반환할 때 사용

  • 두번쨰 인자와 세번째 인자를 병합하여 return한다

  • 그리고 다음으로 done의 상태에 따라 check를 하는 input checkbox를 만든다

  • 양방향 binding은 v-model을 사용한다

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue Test</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
</head>
<body>
    <div id="app">
    <div 
        v-for="todo in computedTodos"
        :key="todo.id"> <!--밑에서 computed로 추가한 id를 사용한다-->
        <input type="checkbox"
            v-model="todo.done">
            {{todo.title}}
    </div>
    </div>
    <script>
        const vm = new Vue({
            el: '#app',
            data: {
                todos: [
                    {title: '아침먹기'},
                    {title: '점심먹기'},
                    {title: '저녁먹기'},
                ]
            },
            computed: {
                computedTodos(){
                    return this.todos.map((todo, index) =>{

                        return {
                            ...todo,
                            id: index+1,
                            done: false
                        }
                    })
                }
            }
        })
    </script>
</body>
</html>
  • console에 "vm.todos.push({id:4, done:false, title:'야식'})" or "vm.todos.push({title:'야식등등'})" 을 추가하면 브라우저에도 반영이 된다
    -> 원시데이터를 바꿈에 따라 computed에 정의된 data들도 값이 바뀌게 되고, 갱신된 결과에 렌더링이 일어났다

computed 캐싱

vue에있는 data를 methods를 사용하여 거꾸로 출력해보자

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue Test</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
</head>
<body>
    <div id="app">
    <div>{{reverseMsg()}}</div>
    <div>{{reverseMsg()}}</div>
    <div>{{reverseMsg()}}</div>
    <div>ㅡㅡㅡㅡㅡㅡㅡㅡ</div>
    <div>{{reversedMsg}}</div>
    <div>{{reversedMsg}}</div>
    <div>{{reversedMsg}}</div>
    </div>
    <script>
        const vm = new Vue({
            el: '#app',
            data: {
                msg : "hello world"
            },
            computed: {
                reversedMsg(){
                    return this.msg.split('').reverse().join('')
                }
            },
            methods: {
                reverseMsg(){
                    return this.msg.split('').reverse().join('')
                }
            }
        })
    </script>
</body>
</html>
  • computed는 함수처럼 정의하지만 data를 return한다
  • computed로 연산해서 return -> 캐싱 개념이 적용됨
  • 함수, 표현식을 직접적으로 사용하는거 보다는 computed를 사용하자

computed getter setter

  • 위에 작성했던 computed는 getter이다. 하지만 우리는 getter와 setter를 분리하여 작성할 수 있어야한다
  • 만약 setter도 필요하다면 아래와 같이 코드를 짜면 된다
 computed: {
                reversedMsg(){
                    return this.msg.split('').reverse().join('')
                },
                reversedMsg: {
                    get(){
                        return this.msg.split('').reverse().join('')
                    }
                }
            }
  • 두 메서드는 같다
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue Test</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
</head>
<body>
    <div id="app">
        <div>{{reversedMsg}}</div>
    </div>
    <script>
        const vm = new Vue({
            el: '#app',
            data: {
                msg : "hello world"
            },
            computed: {
                reversedMsg: {
                    get(){
                        return this.msg.split('').reverse().join('')
                    },
                    set(val){
                        this.msg = val
                    }
                }
            }
        })
    </script>
</body>
</html>
  • 원본인 msg를 변경한다.
  • 반응성이 있으니 msg가 변경되면 reversedMsg도 변경된다

watch

data가 언제 바뀌는건지 정확하지 않고 메세지가 바꼈을 때 특정한 로직을 실행해야한다면, 바뀌는 시점을 감시할 수 있어야한다

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue Test</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
</head>
<body>
    <div id = "app">
        <diV>{{msg}}</diV>
        <div>{{reversedMsg}}</div>
    </div>
    <script>
        const vm = new Vue({
            el: '#app',
            data: {
                msg : 'hello bue!~!~'
            },
            computed: {
                reversedMsg(){
                    return this.msg.split('').reverse().join('')
                }
            },
            watch: {
                msg(newMsg){ //msg가 변경되면 이렇게 됨
                    console.log('변경되었습니다!! : ' + newMsg)
                },
                reversedMsg(newMsg){
                    console.log('reversedMsg 변경되었스니다!! : ' + newMsg)
                }
                
            }
        })
    </script>
</body>
</html>
  • data뿐만아니라 computed도 감시할 수 있다
  • 비동기처리에도 유용하게 사용될 수 있다

클래스와 스타일바인딩

특정한 html 속성에 data를 연결하려면 v-bind를 사용했었다
특정한 class를 동적으로 바인딩하기 위해서 v-bind를 사용한다

1. 인라인방식으로 객체데이터를 html template에 적기

<div
  class="static"
  v-bind:class="{ active: isActive, 'text-danger': hasError }"
></div>
  • active라는 class가 isActive가 true일때는 active가 붙어지고 , 그리고 hasError가 true일때 사용자정의속성인 text-danger이 붙어지게 된다
    << 가 아니라 isActive, hasError가 모두 true일때만 active랑 text-danger이 붙는다.. 라는 얘기인건가요?
  • {}안에 들어간다는 것은 객체데이터임을 의미한다


    2. data 바인딩(active, isActive 모두 data에...)
data() {
  return {
    classObject: {
      active: true,
      'text-danger': false
    }
  }
}
<div v-bind:class="classObject"></div>
  • 이때 이유는 모르겠지만 data에서 정의한 변수를 data에서 참조하는게 안되는것같다. 그래서 이 예제에서도 active: isActive가 아닌 true로 넣어주었다.

3. data와 computed를 활용한 바인딩

data() {
  return {
    isActive: true,
    error: null
  }
},
computed: {
  classObject() {
    return {
      active: this.isActive && !this.error,
      'text-danger': this.error && this.error.type === 'fatal'
    }
  }
}

배열 바인딩

배열을 v-bind:class에 전달할 수 있다
v-bind:class="[ ... ]"

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue Test</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
</head>
<body>
    <style>
        .active { 
            background-color: red;
        }
        .text-danger{
            font-size: 100px;
        }
    </style>
    <div id = "app" :class="[classes]">
        안녕
    </div>
    <script>
        const vm = new Vue({
            el: '#app',
            data: {
                isActive : true,
                classes : [
                    'active', 'text-danger']
            
            },
            computed: {
                classObject(){
                return{
                    'text-danger': true,
                    active : this.isText
                }
                }
            }
        })
    </script>
</body>
</html>

class binding with 삼항연산자

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue Test</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
</head>
<body>
    <style>
        .active { 
            background-color: red;
        }
        .text-danger{
            font-size: 100px;
        }
    </style>
    <div id = "app" v-bind:class="[isActive ? class1 : '', errorClass]">
        안녕
    </div>
    <script>
        const vm = new Vue({
            el: '#app',
            data: {
                isActive : true,
                errorClass : 'text-danger'
            
            },
            computed: {
                class1(){
                return{
                    'text-danger': true,
                    active : true
                }

                }
            }
        })
    </script>
</body>
</html>

class Binding with component

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue Test</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
</head>
<body>
    <style>
        .active { 
             font-size: 100px;
        }
        .one{
            margin-left: 200px;
        }
        .bar{
            background-color: blue;
        }
        .two{
            color: cadetblue;
        }
    </style>
    <div id = "app">
        <component1 class ="one two">

        </component1>
    </div>
    <script>
        Vue.component('component1', {
            template: '<p class="active bar">HIHI</p>'
        })
        const vm = new Vue({
            el: '#app',
            data: {
                isActive : true,
                bar: 'bar',
            }
            
        })
    </script>
</body>
</html>
  • 여러개의 데이터는 띄어쓰기로 구분한다

  • template의 최상위 element와 compoent1을정의했을때의 최상위 element는 같다 즉, 위의 코드는 아래의 코드와 같다

<component1 :class ="one, two, active, bar"></component1>

인라인 스타일 바인딩

v-bind : vue에 있는 data를 연결하는 디렉티브

  • 여기서의 문법과 css의 문법은 약간 차이가 있다
  • 객체 데이터이기때문에 카멜표기법으로 써야한다. 단 작은따옴표 안에 -를 포함한 표기법으로 작성할 수 있다. 다음과 같다
    'font-size': 23px
<!DOCTYPE html>
<html lang="ko">
<body>
    <div id = "app" v-bind:style="style1">
        야옹야옹멍멍
    </div>
    <script>
        const vm = new Vue({
            el: '#app',
            data: {
                style1:{
                    color: 'red',
                    fontSize: '150px'
                }
            }
            
        })
    </script>
</body>
</html>
<!DOCTYPE html>
<html lang="ko">
<body>
    <div id = "app" v-bind:style="{color: redColor, fontSize: '100px'}">
        야옹야옹멍멍
    </div>
    <script>
        const vm = new Vue({
            el: '#app',
            data: {
                redColor: 'red',
            }
            
        })
    </script>
</body>
</html>

배열과 스타일

  • 배열 요소는 뒤로갈수록 덮어쓰게 된다
<!DOCTYPE html>
<html lang="ko">
<body>
    <div id = "app" v-bind:style="[style1, style2]">
        야옹야옹멍멍
    </div>
    <script>
        const vm = new Vue({
            el: '#app',
            data: {
                style1:{
                    color: 'red',
                    fontSize: '100px'
                },
                style2:{
                    color: 'blue'
                }
            }
            
        })
    </script>
</body>
</html>

벤더 프리픽스

  • 웹브라우저에 따라 적용되지 않는 CSS 신기능이 있을 때 대체할 스타일을 명시하는데 사용됩니다.
    모든 브라우저에서 원하는 모습대로 웹사이트를 구동하려면 꼭 해야하는 작업입니다.
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>

조건부 렌더링 if, else

colorState라는 변수를 둬서 빨간색이면 빨간 박스를, 파란색이면 파란박스를, 나머지색이면 회색박스를 출력해보자

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue Test</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
</head>
<body>
    <style>
        .box{
            width: 100px;
            height: 100px;
        }
        .red-box{
            background-color: red;
        }
        .gray-box{
            background-color: gray;
        }
        .blue-box{
            background-color: blue;
        }
    </style>
    <div id = "app" v-bind:style="[style1, style2]">
        <div v-if="colorState==='red'" class ="box red-box"></div>
        <div v-else-if="colorState==='blue'" class="box blue-box"></div>
         <div v-else class="box gray-box"></div>
    </div>
    <script>
        const vm = new Vue({
            el: '#app',
            data: {
                colorState:'white'
            }
        })
    </script>
</body>
</html>

show를 활용한 조건부 렌더링

v-if를 사용하면 매번 요소를 생성·삭제해야한다

  • v-show는 매번 생성·삭제하는 것이 아니라 생성 해놓고 숨기고 표시하는 방식이다
  • v-if는 토글비용이 높다
  • 반면 v-show는 초기 렌더링 비용이 높다. 당장 필요없는 부분도 일단 구현해놓고 숨기는 방식인거니까
    -> 일반적으로는 v-if 쓰고 진짜 자주바뀌는 경우만 v-show사용

이벤트 핸들링

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue Test</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
</head>
<body>
    <div id = "app">
        <ul>
            <li v-for="(todo, index) in todos"
                :key="index"
                :class="'item' + (index+1)"
                @click="click1(todo.title, $event)">
            {{todo.title}}</li>
        </ul>
    </div>
    <script>
        const vm = new Vue({
            el: '#app',
            data: {
                todos:[
                    {title: '아침먹기'},
                    {title: '점심먹기'},
                    {title: '저녁먹기'},
                ]
            },
            methods: {
                click1(title, event){
                    console.log(title)
                    console.log(event.currentTarget.className)
                }
            }
            
        })
    </script>
</body>
</html>
  • method호출시 괄호 안에 변수를 넣어줌으로써 함수호출하듯이 매개변수를 넣어줄 수 있다
  • 매개변수도 넣고 이벤트객체도 받고 싶다면? -> $ 사용
  • index별로 이벤트를 출력하고 싶다 -> 위의 코드 참고

여러 이벤트 핸들러를 등록하고 싶다면?

@click="click1(todo.title, $event); click2()"
  • 중간에 세미콜론이 들어간다
  • 여러개 메서드를 넣을때는 괄호를 생략하면 안된다

이벤트 수식어

PreventDefault

  • 몇몇 태그들은 특정 이벤트 기능을 가지고 있습니다. 그 기능들을 막아주는 역할을 합니다
  • 다음은 그 예시입니다
<script>
  $(document).ready(function() {
      $('#aTag').on('click', function(e) {
          e.preventDefault();	// 이벤트 작동 X
          console.log("https://plitche.github.io")
      })
  });
</script>

stopPrepagation

  • 사용자가 마우스로 웹페이지 내의 버튼을 클릭했을때 대부분의 사람들은 버튼만 반응했다고 생각하게 됩니다. 하지만 웹페이 내부에서는 버튼을 감싸고 있는 부모 태그들 또한 클릭 이벤트에 반응하게 됩니다. 이것을 Bubble Up(버블업)이라고 합니다.
  • 이때 stopPrepagation은 부모로의 이벤트 전파를 막습니다.

capturing

  • 이벤트 캡쳐는 이벤트 버블링과 반대 방향으로 진행되는 이벤트 전파 방식입니다.

  • .stop : 이벤트 버블링을 막아준다(event.stopPrepagation)

  • .prevent : event.preventDefault

  • .capture : 캡처링

  • .self : 이벤트가 붙어있는 딱 그 공간이어야만 작동된다

  • 이벤트 뒤에 수식어를 붙여 사용한다

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue Test</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
</head>
<body>
    <style>
        .parent{
            width: 200px;
            height: 200px;
            background-color: blue;
        }
        .child{
            width: 100px;
            height: 100px;
            background-color: red;
        }
    </style>
    <div id = "app" class="parent" @click="click1">
        <div class="child" @click="click1"></div>
    </div>
    <script>
        const vm = new Vue({
            el: '#app',
            data: {
            },
            methods: {
                click1(event){
                    console.log(event.currentTarget.className)
                }
            }
            
        })
    </script>
</body>
</html>
  • 이벤트 버블링이 발생하고 있다
  • 자식 이벤트를 적용하는 블럭에다가 @click.stop을 적용해주면 child박스를 클릭했을때 부모에게까지 이벤트 전파가 되는것을 막을 수 있다
<div id = "app" class="parent" @click.self="click1">
        <div class="child" ></div>
</div>

키 수식어

사용자의 키 입력이 들어올 때 수식어를 설정할 수 있다

  • 키 수식어 사용 전
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue Test</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
</head>
<body>
    <div id = "app">
        <input type="text" @KeyDown="keyHandler">
    </div>
    <script>
        const vm = new Vue({
            el: '#app',
            data: {
            },
            methods: {
                keyHandler(e){
                    if(e.keyCode===13) console.log("done!")
                }
            }
            
        })
    </script>
</body>
</html>
  • 키 수식어 사용 후
<input type="text" @KeyDown.enter="keyHandler">

키 수식어 목록

  • .meta : window 키

키는 chain으로 참조하여 사용할 수 있다

<input type="text" @KeyDown.ctrl.enter="keyHandler">
  • ctrl을 누른상태에서 enter를 눌렀을 때 이벤트가 발생한다
  • 키 합성 시 사용한다

폼 입력 바인딩

form : input, text area, select ...
이러한 양식요소들을 사용하기위해서 vue에서는 v-model이라는 디렉티브를 사용한다

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue Test</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
</head>
<body>
    <div id = "app">
        <input type="text" v-bind:value="msg"> <!-- 기본 값-->
    </div>
    <script>
        const vm = new Vue({
            el: '#app',
            data: {
                msg: 'hello~'
            },
            methods: {
                
            }
            
        })
    </script>
</body>
</html>
  • v-bind: 즉, :는 단방향이다. 뷰에 있는 데이터가 들어가긴하는데 내가 입력한 값으로 업데이트 되진 않는다
  • 양방향을 쓰기 위해서는 v-bind:가 아니라 v-model 이라는 디렉티브를 사용해야한다
<input type="text" v-model="msg">

단, 한글은 글자하나의 입력이 완료 되어야지 출력된다


  • -> input이라는 event를 사용하면 이러한 한계를 해결할 수 있다
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue Test</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
</head>
<body>
    <div id = "app">
        <input type="text" v-on:input="bindMessage" :value="msg">
        {{msg}}
    </div>
    <script>
        const vm = new Vue({
            el: '#app',
            data: {
                msg: 'hello~'
            },
            methods: {
                bindMessage(event){
                    this.msg = event.target.value
                }
            }
            
        })
    </script>
</body>
</html>
  • value를 통해 단방향 연결을 하고, 그 외에 input이라는 행위가 일어났을 때 특정한 메서드를 동작시키게 해서 양방향처럼 쓴다

아래와 같이도 가능

<input type="text" v-on:input="msg = $event.target.value">
  • $event : 해당 요소의 event객체

v-model 수식어

실시간으로 탐지하는게 아니라 enter를 눌렀을때만 변경감지가 되도록, 또한 양방향이도록 헤보자

  • change : focus가 풀리거나 했을 때 메서드를 지정해줄 수 있다
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue Test</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
</head>
<body>
    <div id = "app">
        <input type="text" v-on:change="changeMessage" :value="msg">
        {{msg}}
    </div>
    <script>
        const vm = new Vue({
            el: '#app',
            data: {
                msg: 'hello~'
            },
            methods: {
                changeMessage(event){
                    this.msg = event.target.value
                }
            }
        })
    </script>
</body>
</html>

위 작업에 해당하는 수식어가 있다

 <input type="text" v-model.lazy="msg" :value="msg">

trim : 문자 앞 뒤에 있는 공백을 제거해준다

원래는 trim이라는 method를 제공해주는데 매번 method 사용 없이 수식어로 사용이 가능하다

 <input type="text" v-model.trim="msg" :value="msg">

number : string -> int로

input에 있는 data를 string이 아닌 숫자로 변환해준다

<div id = "app">
        <input type="text" v-model.number="msg" :value="msg">
        {{msg}}
        <div>{{typeof msg}}</div>
</div>

컴포넌트

ui의 그룹(덩어리)

  • 2번이상 사용되거나 개념적으로 분류힐 필요가 있을 때

전역 컴포넌트 · 지역컴포넌트

  • 전역컴포넌트
Vue.component('my-component1', 
        {
            template: '<div class="me">{{msg}}</div>',
            data(){
                return{
                    msg: 'hello vue'
                }
               
            }
        })
  • 지역컴포넌트 : 해당하는 특정한 인스턴스 내부에서만 쓸 수 있는
<body>
    <div id = "app1">
        <my-component/>
    </div>
    <div id = "app2">
        <my-component/>
    </div>
    <script>
        const myCompo = {
            template: '<div class="me">{{msg}}</div>',
            data(){
                return{
                    msg: 'hello vue'
                }
               
            }
        }
        const vm1 = new Vue({
            el: '#app1',
            components: {
                'my-component': myCompo
            }
            
        })
        const vm2 = new Vue({
            el: '#app2'
        })
    </script>
</body>

생략

<body>
    <div id = "app1">
        <my-compo/>
    </div>
    <div id = "app2">
    </div>
    <script>
        const myCompo = {
            template: '<div class="me">{{msg}}</div>',
            data(){
                return{
                    msg: 'hello vue'
                }
               
            }
        }
        const vm1 = new Vue({
            el: '#app1',
            components: {
                myCompo
            }
            
        })
        const vm2 = new Vue({
            el: '#app2'
        })
    </script>
</body>
  • 위와 같이 정의한 변수이름을 그대로 사용하면 두가지 인자를 넣지 않아도 된다

component - data 가 함수로 만들어져야하는 이유

  • component는 재사용을 위해 사용하는 것이다
  • data에 기존에 했던 방식대로 함수가 아닌 객체데이터를 넣는 형식으로 생성하게 된다면 데이터는 불변성을 가지지 않는 참조형태가 될 것이다
  • 해당 component 객체들이 그 data를 참조해서 같이 쓰게되는 문제점이 발생한다
  • 이런 문제를 막기위해 즉, 하나의 data를 여러개의 component가 공유하는것을 막기 위해 data를 함수로 선언한다

immutable

  • 원시데이터는 같은곳을 참조하지 않는다.
  • 하지만 object data는 같은곳을 참조한다

props

  • 원래는 props를 배열 데이터로 선언할 수 있는데 그렇게 선언하게 되면 배열 원소 하나하나에 설정을 해주지 못한다
Vue.component('my-component', {
            template: '<div>{{msg}}</div>',
            props : ['msg']
        })
Vue.component('my-component', {
            template: '<div>{{msg}}</div>',
            props : {
                msg : String //msg 가 string이 아니면 error!
            }
        })
  • msg로 data가 안넘어오면 기본값으로 "default value"을 쓰겠다
<body>
    <div id = "app">
    <my-component ></my-component>
    </div>
    <script>
        Vue.component('my-component', {
            template: '<div>{{msg}}</div>',
            props : {
                msg : {
                    type: String,
                    default: "default value"
                }
            }
        })
        const vm = new Vue({
            el: '#app',
            data(){
                return{
                    "msg" : "msg"
                }
            }
            
        })
    </script>
</body>
  • 원래는 component를 사용하면서 props를 같이 정의해주어야하는데 정의해주지 않았을때 default 값이 출력된다
 msg : {
                    type: [String, Number],
                    default: "default value",
                    required: true //msg라는 속성을 정의해주지 않을떄 error
                }

validator - 유효성 검증 함수

<script>
        Vue.component('my-component', {
            template: '<div>{{msg}}</div>',
            props : {
                msg : {
                    type: [String, Number],
                    default: "default value",
                    required: true,
                    validator(val){ // true가 return되면 유효성검사를 통과한 것!
                        return val ==='hello'
                    }
            
                }
            }
        })
        const vm = new Vue({
            el: '#app',
            data(){
                return{
                    "msg" : "hello"
                }
            }
            
        })
    </script>

html은 기본적으로 케밥케이스 사용, 자바스크립트는 카멜케이스

<my-component :my-msg="msg"></my-component>
 Vue.component('my-component', {
            template: '<div>{{myMsg}}</div>',
            props : {
                myMsg : {
                    type: [String, Number],
                    default: "default value",
                    required: true,
                    validator(val){ // true가 return되면 유효성검사를 통과한 것!
                        return val ==='hello'
                    }
            
                }
            }
        })

사용자 지정 이벤트

  • 부모->자식으로 data를 넘겨줄때는 props를 사용한다
  • Emit event : 자식에게 발생한 data의 이벤트를 부모에게 전달 -> 부모에게 영향을 끼침
<body>
    <div id = "app">
    <my-component :my-msg="msg"></my-component>
    </div>
    <script>
        Vue.component('my-component', {
            template: '<div @click="updateMsg">{{myMsg}}</div>',
            props:{
                myMsg: String
            },
            methods:{
                updateMsg(){
                    this.myMsg = "goood"
                }
            }

        })
        const vm = new Vue({
            el: '#app',
            data(){
                return{
                    "msg" : "hello"
                }
            }
            
        })
    </script>
</body>
  • my-msg는 부모에게 받은 msg를 출력하고 있다
  • 자식 컴포넌트에서 직접적으로 부모 컴포넌트의 값을 바꾸는건 안된다
  • 이벤트만 부모에게 전달해주고 이벤트가 발생했을 때 부모에서 특정한 행위를 해서 값을 바꿔라
  1. 자식컴포넌트를 click했을 때 특정한 click event가 발생하게 되고
  2. click event에서 부모 컴포넌트의 my-event라는 이벤트를 발생시킨다
  3. my-event가 발생했을 때 부모에서는 특정한 메서드(updateMessage)를 호출하게 된다
  4. 그 메서드는 부모컴포넌트의 value를 바꾼다

출처
https://cucat.tistory.com/entry/CSS-벤더프리픽스-예시-자동-접두사-라이브러리
https://ismydream.tistory.com/98
https://joshua1988.github.io/web-development/javascript/event-propagation-delegation/

profile
안녕하세요!

0개의 댓글