Vue.js

SongE·2022년 5월 24일
2

(KDT FE2) Study Note 🧾

목록 보기
8/10
post-thumbnail

1. Vue 시작하기

Vue3_Wabpack GitHub


2. Vue 문법

2.1 인스턴스와 라이프사이클


App.vue

<template>
  <h1>{{ count }}</h1>
</template>

<script>
export default {
  data() {
    return {
      count: 2
    }
  },
  // 컴포넌트 생성 전
  beforeCreate() {
    console.log(this.count) // undefined
  },
  // 컴포넌트 생성 후
  created() {
    console.log(this.count) // 2
  },
  // 렌더링 전
  beforeMount() {
    console.log(document.querySelector('h1')) // null
  },
  // 렌더링 후
  mounted() {
    console.log(document.querySelector('h1')) // <h2>2</h2>
  }
}
</script>

2.2 템플릿 문법


  • v-once : 일회성 보간
  • {{ }} : 일반 텍스트로 해석
  • v-html : 실제 HTML로 해석
  • v-bind : HTML 속성 사용
  • v-bind 약어 : :
  • v-on 약어 : @

예1)
App.vue

<template>
  <h1 
    v-once 
    @click='add'> 
    {{ msg }} 
  </h1>
  <h1 v-html="msg"></h1>
</template>

<script>
  export default {
    data() {
      return {
        msg: '<div style="color: red;";>Hello!!</div>'
      }
    },
    methods: {
      add() {
        this.msg += '!'
      }
    }
  }
</script>

예2)
App.vue

<template>
  <h1 :[attr]="'active'"
  @[event]="add">
    {{ msg }}
  </h1>
</template>
<script>
  export default {
    data() {
      return {
        msg: 'active',
        attr: 'class',
        event: 'click'
      }
    },
    methods: {
      add() {
        this.msg += '!'
      }
    }
  }
</script>

<style scoped>
  .active {
    color: royalblue;
    font-size: 100px;
  }
</style>

2.3 Computed


App.vue

<template>
  <Fruits />
</template>

<script>
import Fruits from '~/components/Fruits'

export default {
  components: {
    Fruits
  }
}
</script>

Fruits.vue

<template>
  <section v-if="hasFruit">
    <h1>Fruits</h1>
    <ul>
      <li
        v-for="fruit in fruits"
        :key="fruit">
        {{ fruit }}
      </li>
    </ul>
  </section>
  <section>
    <h1>Reverse Fruits</h1>
    <ul>
      <li 
        v-for="fruit in reverseFruits"
        :key="fruit">
        {{ fruit }}
      </li>
    </ul>
  </section>
</template>

<script>
export default {
  data() {
    return {
      fruits: [
        'Apple', 'Banana', 'Cherry'
      ]
    }
  },
  computed: {
    hasFruit() {
      return this.fruits.length > 0
    },
    reverseFruits() {
      return this.fruits.map(fruit => {
        // 'Apple' => ['A','p','p','l','e']
        // => ['e','l','p','p','A'] => 'elppA'
        return fruit.split('').reverse().join('')
      })
    }
  }
}
</script>

2.4 Computed 캐싱


App.vue

<template>
  <h1>{{ reversedMessage }}</h1>
  <h1>{{ reversedMessage }}</h1>
  <h1>{{ reversedMessage }}</h1>
  <h1>{{ reversedMessage }}</h1>
</template>

<script>
export default {
  data() {
    return {
      msg: 'Hello Computed!'
    }
  },
  computed: {
    reversedMessage() {
      return this.msg.split('').reverse().join('')
    }
  }
}
</script>

2.5 Getter, Setter


App.vue

<template>
  <button @click="add">
  	ADD
  </button>
  <h1>{{ reversedMessage }}</h1>
  <h1>{{ reversedMessage }}</h1>
  <h1>{{ reversedMessage }}</h1>
  <h1>{{ reversedMessage }}</h1>
</template>

<script>
export default {
  data() {
    return {
      // Getter, Setter
      msg: 'Hello Computed!'
    }
  },
  computed: {
    // Getter
    // reversedMessage() {
    //   return this.msg.split('').reverse().join('')
    // }
    // Getter, Setter
    reversedMessage: {
      get() {
        return this.msg.split('').reverse().join('')
      },
      set(value) {
        this.msg = value // !detupmoC olleH
      }
    }
  },
  methods: {
    add() {
      this.reversedMessage += '!?'
    }
  }
}
</script>

2.6 Watch


App.vue

<template>
  <h1 @click="changeMessage">
    {{ msg }}
  </h1>
  <h1>{{ reversedMessage }}</h1>
</template>

<script>
export default {
  data() {
    return {
      msg: 'Hello?'
    }
  },
  computed: {
    reversedMessage() {
        return this.msg.split('').reverse().join('')
    }
  },
  watch: {
    msg() {
      console.log('msg:', this.msg) // msg: Good!
    },
    reversedMessage() {
      console.log('reversedMessage:', this.reversedMessage) // reversedMessage: !dooG
    }
  },
  methods: {
    changeMessage() {
      this.msg = 'Good!'
    }
  }
}
</script>

2.7 클래스와 스타일 바인딩


클래스 바인딩 )
App.vue

<template>
  <h1 
    :class="{active: isActive}"
    @click="activate">
    Hello?!({{ isActive }})
  </h1>
</template>

<script>
export default {
  data() {
    return {
      isActive: false
    }
  },
  methods: {
    activate() {
      this.isActive = true
    }
  }
}
</script>

<style scoped>
  .active {
    color: red;
    font-weight: bold;
  }
</style>

스타일 바인딩 )
App.vue

<template>
  <h1 
    :style="[fontStyle, backgroundStyle]"
    @click="changeStyle">
    Hello?!
  </h1>
</template>

<script>
export default {
  data() {
    return {
      fontStyle: {
        color: 'orange',
        fontSize: '30px'
      },
      backgroundStyle: {
        backgroundColor: 'black'
      }
    }
  },
  methods: {
    changeStyle() {
      this.fontStyle.color = 'red'
      this.fontStyle.fontSize = '50px'
    }
  }
}
</script>

2.8 조건부 렌더링


예1)
App.vue

<template>
<button @click="handler">
  Click me!
</button>
  <h1 v-if="isShow">
    Hello?!
  </h1>
  <h1 v-else-if="count > 3">
    Count > 3
  </h1>
  <h1 v-else>
    Good~
  </h1>
</template>

<script>
export default {
  data() {
    return {
      isShow: true,
      count: 0
    }
  },
  methods: {
    handler() {
      this.isShow = !this.isShow
      this.count += 1
    }
  }
}
</script>

  • <template> :
    • 렌더링되지 않는다.
    • 최상위 <template>에는 v-if를 쓰면 안된다.

예2)

<template>
<button @click="handler">
  Click me!
</button>
  <template v-if="isShow">
    <h1>Title</h1>
    <p>Paragraph 1</p>
    <p>Paragraph 2</p>
  </template>
</template>

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

예3)

  • v-if : 조건이 false면 렌더링이 안된다.
  • v-show :
    • 조건이 false면 렌더링이 된후 display: none 속성을 적용한다.
    • <template> 엘리먼트를 지원하지 않는다.
    • v-else와 함께 쓸 수 없다.
<template>
<button @click="handler">
  Click me!
</button>
  <h1 v-show="isShow">
    Hello?!
  </h1>
</template>

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

2.9 리스트 렌더링


예1)
App.vue

<template>
  <ul>
    <li
      v-for="(f, i) in fruits"
      :key='f'>
      {{ f }}-{{ i + 1 }}
    </li>
  </ul>
</template>

<script>
export default {
  data() {
    return {
      fruits: ['Apple', 'Banana', 'Cherry']
    }
  },
  computed: {
    newFruits() {
      
    }
  }
}
</script>

예2)
App.vue

<template>
  <ul>
    <li
      v-for="fruit in newFruits"
      :key="fruit.id">
      {{ fruit.name }}
    </li>
  </ul>
</template>

<script>
export default {
  data() {
    return {
      fruits: ['Apple', 'Banana', 'Cherry']
    }
  },
  computed: {
    newFruits() {
      return this.fruits.map((fruit, index) => {
        return {
          id: index,
          name: fruit
        }
      })
    }
  }
}
</script>

예3) $ npm i -D shortid
App.vue

<template>
  <ul>
    <li
      v-for="fruit in newFruits"
      :key="fruit.id">
      {{ fruit.name }}-{{ fruit.id }}
    </li>
  </ul>
</template>

<script>
import shortid from 'shortid'

export default {
  data() {
    return {
      fruits: ['Apple', 'Banana', 'Cherry']
    }
  },
  computed: {
    newFruits() {
      return this.fruits.map(fruit => ({
          id: shortid.generate(),
          name: fruit
        }))
    }
  }
}
</script>

예4) 객체구조분해로 간소화
App.vue

<template>
  <ul>
    <li
      v-for="{ id, name } in newFruits"
      :key="id">
      {{ name }}-{{ id }}
    </li>
  </ul>
</template>

<script>
import shortid from 'shortid'

export default {
  data() {
    return {
      fruits: ['Apple', 'Banana', 'Cherry']
    }
  },
  computed: {
    newFruits() {
      return this.fruits.map(fruit => ({
          id: shortid.generate(),
          name: fruit
        }))
    }
  }
}
</script>

예5) 반응성
App.vue

<template>
<button @click="handler">
  Click me!
</button>
  <ul>
    <li
      v-for="{ id, name } in newFruits"
      :key="id">
      {{ name }}-{{ id }}
    </li>
  </ul>
</template>

<script>
import shortid from 'shortid'

export default {
  data() {
    return {
      fruits: ['Apple', 'Banana', 'Cherry']
    }
  },
  computed: {
    newFruits() {
      return this.fruits.map(fruit => ({
          id: shortid.generate(),
          name: fruit
        }))
    }
  },
  methods: {
    handler() {
      this.fruits.push('Orange')
    }
  }
}
</script>

2.10 이벤트 핸들링


예1) 메소드에 인수가 없는 경우
App.vue

<template>
  <button @click="handler">
    Click me!
  </button>
</template>  

<script>
  export default {
    methods: {
      handler(event) {
        console.log(event) // PointerEvent { ... }
        console.log(event.target) // <button> Click me! </button>
        console.log(event.target.textContent) // Click me!
      }
    }
  }
</script>         

예2) 메소드에 인수가 있는 경우
App.vue

<template>
  <button @click="handler('hi', $event)">
    Click 1
  </button>
  <button @click="handler('what', $event)">
    Click 2
  </button>
</template>  

<script>
  export default {
    methods: {
      handler(msg, event) {
        console.log(msg) // Click 1 => hi, Click 2 => what
        console.log(event) // PointerEvent { ... }
      }
    }
  }
</script>  

예3) 메소드가 2개이상일 경우
App.vue

<template>
  <button @click="handlerA(), handlerB()">
    Click me!
  </button>
</template>  

<script>
  export default {
    methods: {
      handlerA() {
        console.log('A')
      },
      handlerB() {
        console.log('B')
      }
    }
  }
</script>                

2.10.1 이벤트 수식어


1) .prevent : 기본기능 동작 방지

<template>
  <a 
    href="https://naver.com"
    target="_blank"
    @click.prevent="handler">
    NAVER
  </a>
</template>  

<script>
  export default {
    methods: {
      handler() {
        console.log('ABC!')
      }
    }
  }
</script>  

2) .once : 메소드 기능을 한번만 동작시킴

<template>
  <a 
    href="https://naver.com"
    target="_blank"
    @click.once="handler">
    NAVER
  </a>
</template>  

<script>
  export default {
    methods: {
      handler() {
        console.log('ABC!')
      }
    }
  }
</script>  

3) 메소드 체이닝 구조도 가능하다.

<template>
  <a 
    href="https://naver.com"
    target="_blank"
    @click.prevent.once="handler">
    NAVER
  </a>
</template>

<script>
  export default {
    methods: {
      handler() {
        console.log('ABC!')
      }
    }
  }
</script>  

4) .stop : 이벤트 버블링(자 > 부 순서) 방지

<template>
  <div 
    class="parent"
    @click="handlerA">
    <div 
      class="child"
      @click.stop="handlerB"></div>
  </div>
</template>  

<script>
export default {
  methods: {
    handlerA() {
      console.log('A')
    },
    handlerB() {
      console.log('B')
    }
  }
}
</script>         

<style scoped lang="scss">
.parent {
  width: 200px;
  height: 100px;
  background-color: royalblue;
  margin: 10px;
  padding: 10px;
  .child {
    width: 100px;
    height: 100px;
    background-color: orange;
  }
}
</style>

5) .capture : 이벤트 버블링의 반대 개념(부 > 자 순서)

<template>
  <div 
    class="parent"
    @click.capture="handlerA">
    <div 
      class="child"
      @click="handlerB"></div>
  </div>
</template>  

<script>
export default {
  methods: {
    handlerA() {
      console.log('A')
    },
    handlerB() {
      console.log('B')
    }
  }
}
</script>         

<style scoped lang="scss">
.parent {
  width: 200px;
  height: 100px;
  background-color: royalblue;
  margin: 10px;
  padding: 10px;
  .child {
    width: 100px;
    height: 100px;
    background-color: orange;
  }
}
</style>

6) .self : 선택한 부분(target)과 선택한 부분과 직접 연결된 메소드 부분(currentTarget)이 같을때만 동작

App.vue

<template>
  <div 
    class="parent"
    @click.self="handlerA">
    <div 
      class="child"></div>
  </div>
</template>  

<script>
export default {
  methods: {
    handlerA() {
      console.log('A')
    },
    handlerB() {
      console.log('B')
    }
  }
}
</script>         

<style scoped lang="scss">
.parent {
  width: 200px;
  height: 100px;
  background-color: royalblue;
  margin: 10px;
  padding: 10px;
  .child {
    width: 100px;
    height: 100px;
    background-color: orange;
  }
}
</style>

7) .passive : 화면처리와 로직처리를 분리시킴(5배이상 속도가 빨라질 수 있다.)

App.vue

<template>
  <div 
    class="parent"
    @wheel.passive="handler">
    <div 
      class="child"></div>
  </div>
</template>  

<script>
export default {
  methods: {
    handler(event) {
      for (let i = 0; i < 10000; i += 1) {
        console.log(event)
      }
    },
  }
}
</script>         

<style scoped lang="scss">
.parent {
  width: 200px;
  height: 100px;
  background-color: royalblue;
  margin: 10px;
  padding: 10px;
  overflow: auto;
  .child {
    width: 100px;
    height: 2000px;
    background-color: orange;
  }
}
</style>

2.10.2 키 수식어


@keydown.키값(케밥케이스)

App.vue

<template>
  <input 
    type="text"
    @keydown.ctrl.shift.enter="handler" />
</template>  

<script>
export default {
  methods: {
    handler() {
      console.log('Eter!!')
    }
  }
}
</script>  

2.11 폼 입력 바인딩


예1) 양방향 바인딩
App.vue

<template>
<h1>{{ msg }}</h1>
  <input 
    type="text"
    :value="msg" 
    @input="handler"/>
</template>  

<script>
export default {
  data() {
    return {
      msg: 'Hello world!'
    }
  },
  methods: {
    handler(event) {
      console.log(event.target.value)
      this.msg = event.target.value
    }
  }
}
</script>

예2) 예1) 간소화
App.vue

<template>
<h1>{{ msg }}</h1>
  <input 
    type="text"
    :value="msg" 
    @input="msg = $event.target.value"/>
</template>  

<script>
export default {
  data() {
    return {
      msg: 'Hello world!'
    }
  }
}
</script>   

예3) 한글은 한박자 늦게 바인딩되기 때문에 예2)로 쓰는것이 좋다.
App.vue

<template>
<h1>{{ msg }}</h1>
  <input 
    type="text"
    :value="msg" 
    v-model="msg"/>
    <h1>{{ checked }}</h1>
  <input 
    type="checkbox"
    v-model="checked" />
</template>  

<script>
export default {
  data() {
    return {
      msg: 'Hello world!',
      checked: false
    }
  }
}
</script>  

2.12 v-model 수식어


예1) 데이터 변경이 다 끝나고 동작 시킴
App.vue

<template>
<h1>{{ msg }}</h1>
  <input 
    type="text"
    :value="msg" 
    v-model.lazy="msg"/>

</template>  

<script>
export default {
  data() {
    return {
      msg: 'Hello world!',
    }
  },
}
</script>

예2) 양방향 바인딩 데이터 숫자로 유지
App.vue

<template>
  <h1>{{ msg }}</h1>
  <input 
    type="text"
    v-model.number="msg"/>
</template>  

<script>
export default {
  data() {
    return {
      msg: 123,
    }
  },
  watch: {
    msg() {
      console.log(typeof this.msg)
    }
  }
}
</script>     

예3) 띄어쓰기 무시
App.vue

<template>
  <h1>{{ msg }}</h1>
  <input 
    type="text"
    v-model.trim="msg"/>
</template>  

<script>
export default {
  data() {
    return {
      msg: 'Hello world!',
    }
  },
  watch: {
    msg() {
      console.log(this.msg)
    }
  }
}
</script>

2.13 컴포넌트

2.13.1 기초


App.vue

<template>
  <MyBtn>Banana</MyBtn>
  <MyBtn :color="color">
    <span style="color: red;">Banana</span>
  </MyBtn>
  <MyBtn 
    large 
    color="royalblue">
    Apple
  </MyBtn>
  <MyBtn>Cherry</MyBtn>
</template>  

<script>
import MyBtn from '~/components/MyBtn'

export default {
  components: {
    MyBtn
  },
  data() {
    return {
      color: '#000'
    }
  }
}
</script>

MyBtn.vue

<template>
  <div 
    :class="{ large }"
    :style="{ backgroundColor: color }"
    class="btn">
    <slot></slot>
  </div>
</template>

<script>
export default {
  props: {
    color: {
      type: String,
      default: 'gray'
    },
    large: {
      type: Boolean,
      default: false
    },
  }
}
</script>

<style scoped lang="scss">
  .btn {
    display: inline-block;
    margin: 4px;
    padding: 6px 12px;
    border-radius: 4px;
    background-color: gray;
    color: white;
    cursor: pointer;
    &.large {
    font-size: 20px;
    padding: 10px 20px;
    }
  }
</style>

2.13.2 속성 상속


  • 최상위 요소가 하나일때만 상속이 적용된다.

예1) 요소가 두 개 이상일때 상속하기
App.vue

<template>
  <MyBtn 
    class="heropy"
    style="color: red;"
    title="Hello world!">
    Banana
  </MyBtn>
</template>  

<script>
import MyBtn from '~/components/MyBtn'

export default {
  components: {
    MyBtn
  },
}
</script>

MyBtn.vue

<template>
  <div class="btn">
    <slot></slot>
  </div>
  <div v-bind="$attrs"></div>
</template>

<script>
export default {
  inheritAttrs: false,
  created() {
    console.log(this.$attrs)
  }
}
</script>

<style scoped lang="scss">
  .btn {
    display: inline-block;
    margin: 4px;
    padding: 6px 12px;
    border-radius: 4px;
    background-color: gray;
    color: white;
    cursor: pointer;
  }
</style>

2.13.3 Emit


App.vue

<template>
  <MyBtn 
    @heropy="log" 
    @change-msg="logMsg">
    Banana
  </MyBtn>
</template>  

<script>
import MyBtn from '~/components/MyBtn'

export default {
  components: {
    MyBtn
  },
  methods: {
    log(event) {
      console.log('Click!!')
      console.log(event)
    },
    logMsg(msg) {
      console.log(msg)
    }
  }
}
</script>

MyBtn.vue

<template>
  <div class="btn">
    <slot></slot>
  </div>
  <h1 @dblclick="$emit('heropy', $event)">
    ABC
  </h1>
  <input 
    type="text" 
    v-model="msg" />
</template>

<script>
export default {
  emits: [
    'heropy',
    'changeMsg'
  ],
  data() {
    return {
      msg: ''
    }
  },
  watch: {
    msg() {
      this.$emit('changeMsg', this.msg)
    }
  }
}
</script>

<style scoped lang="scss">
  .btn {
    display: inline-block;
    margin: 4px;
    padding: 6px 12px;
    border-radius: 4px;
    background-color: gray;
    color: white;
    cursor: pointer;
  }
</style>

2.13.4 Slot


App.vue

<template>
  <MyBtn>
    <template #icon>
      <span>(B)</span>
    </template>
    <template #text>
      <span>Banana</span>
    </template>
  </MyBtn>
  <MyBtn></MyBtn>
</template>  

<script>
import MyBtn from '~/components/MyBtn'

export default {
  components: {
    MyBtn
  },
}
</script>

MyBtn.vue

<template>
  <div class="btn">
    <slot name="icon"></slot>
    <slot name="text">Apple</slot>
  </div>
</template>

<style scoped lang="scss">
  .btn {
    display: inline-block;
    margin: 4px;
    padding: 6px 12px;
    border-radius: 4px;
    background-color: gray;
    color: white;
    cursor: pointer;
  }
</style>

2.13.5 Provide, Inject


  • props는 직손자식에게만 데이터를 내려줄수있다.
  • 조상요소에서 후손요소로 데이터를 바로 내려주려면 Provide, Inject를 써야한다.
  • 하지만 반응성이 적용이 안된다. 추가작업이 필요!!

App.vue

<template>
  <Parent />
</template>  

<script>
import Parent from '~/components/Parent'

export default {
  components: {
    Parent
  },
  data() {
    return {
      message: 'Hello world!'
    }
  },
  provide() {
    return {
      msg: this.message
    }
  }
}
</script>

Parent.vue

<template>
  <Child />
</template>  

<script>
import Child from '~/components/Child'

export default {
  components: {
    Child
  }
}
</script>

Child.vue

<template>
  <div>
    Child: {{ msg }}
  </div>
</template>  

<script>
export default {
  inject: ['msg']
}
</script>

반응성을 적용해 주는 예)
App.vue

<template>
<button @click="message = 'Good?'">
  Click!
</button>
<h1>App: {{ message }}</h1>
  <Parent />
</template>  

<script>
import Parent from '~/components/Parent'
import { computed } from 'vue'

export default {
  components: {
    Parent
  },
  data() {
    return {
      message: 'Hello world!'
    }
  },
  provide() {
    return {
      msg: computed(() => this.message)
    }
  }
}
</script>

Parent.vue

<template>
  <Child />
</template>  

<script>
import Child from '~/components/Child'

export default {
  components: {
    Child
  }
}
</script>

Child.vue

<template>
  <div>
    Child: {{ msg.value }}
  </div>
</template>  

<script>
export default {
  inject: ['msg']
}
</script>

2.13.6 Refs


예1)
App/vue

<template>
  <h1 ref="hello">
    Hello world!
  </h1>
</template>  

<script>
export default {
  created() {
    console.log(this.$refs.hello) // undefined
  },
  mounted() {
    console.log(this.$refs.hello) // <h1> Hello world! </h1>
  }
}
</script>

예2) 컴포넌트를 연결할때
App.vue

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

<script>
import Hello from '~/components/Hello'

export default {
  components: {
    Hello
  },
  mounted() {
    console.log(this.$refs.hello.$el) // <h1> Hello~ </h1>
  }
}
</script>

Hello.vue

<template>
  <h1>Hello~</h1>
</template>  

예3) 컴포넌트의 최상위 요소가 2개 이상일때
App.vue

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

<script>
import Hello from '~/components/Hello'

export default {
  components: {
    Hello
  },
  mounted() {
    console.log(this.$refs.hello.$refs.good) // <h1> Good? </h1>
  }
}
</script>

Hello.vue

<template>
  <h1>Hello~</h1>
  <h1 ref="good">
    Good?
  </h1>
</template>

3. 컴포지션API


3.1 반응형 데이터(반응성)


App.vue

<template>
  <div @click="increase">
    {{ count }}
  </div>
</template>  

<script>
import { ref } from 'vue'

export default {
  setup() {
    let count = ref(0)
    function increase() {
      count.value += 1
    }

    return {
      count,
      increase
    }
  }
}
</script>

3.2 기본 옵션과 라이프사이클


  • 라이프사이클 훅
Option APIsetup 내부의 훅
created필요하지 않음*
mountedonMounted

App.vue

<template>
  <h1 @click="increase">
    {{ count }} / {{ doubleCount }}
  </h1>
  <h1 @click="changeMessage"> 
    {{ message }} / {{ reversedMessage }}
  </h1>
</template>  

<script>
import { ref, computed, watch, onMounted } from 'vue'

export default {
  setup() {
    const count = ref(0)
    const doubleCount = computed(() => {
      return count.value * 2
    })
    function increase() {
      count.value += 1
    }

    const message = ref('Hello world!')
    const reversedMessage = computed(() => {
      return message.value.split('').reverse().join('')
    })
    watch(message, newValue => {
      console.log(newValue)
    })
    function changeMessage() {
      message.value = 'Good?!'
    }
    console.log(message.value)

    onMounted(() => {
      console.log(count.value)
    })

    return {
      count,
      doubleCount,
      increase,
      message,
      changeMessage,
      reversedMessage
    }
  }
}
</script>

3.3 props, context


App.vue

<template>
  <MyBtn
    class="heropy"
    style="color: red;"
    color="#ff0000"
    @hello='log'>
    Apple
  </MyBtn>
</template>  

<script>
import MyBtn from '~/components/MyBtn'

export default {
  components: {
    MyBtn
  },
  methods: {
    log() {
      console.log('Hello world!')
    }
  }
}
</script>

MyBtn.vue

<template>
  <div
    v-bind="$attrs"
    class="btn"
    @click="hello">
    <slot></slot>
  </div>
</template>

<script>
import { onMounted } from 'vue'

export default {
  inheritAttrs: false,
  props: {
    color: {
      type: String,
      default: 'gray'
    }
  },
  emits: ['hello'],
  setup(props, context) {
    function hello() {
      context.emit('hello')
    }
    onMounted(() => {
      console.log(props.color)
      console.log(context.attrs)
    })

    return {
      hello
    }
  }

  
}
</script>
profile
front-end developer dreamer

0개의 댓글