
👉 기존에 사용하던 props / emits는 각 컴포넌트간에 바로 상위 바로 하위컴포넌트끼리 전달을 할 수 있어서 여러단계 아래의 컴포넌트에게 전달하기 위해선 각각의 컴포넌트에 선언을 해야한다는 불편함이 존재했다.
👉 이러한 불편함을 줄이기위해 하나의 컴포넌트에서 선언할시 여러 컴포넌트에서 해당 데이터를 사용할 수 있는 기능이 Provide와 Inject이다.
👉 props와 대응되는 기능으로 부모컴포넌트에서 선언하는것이다.
const data = ref({num:10})
provide('data',data); //부모 컴포넌트에서 data를 provide
👉 emits와 대응되는 기능으로 자식컴포넌트에서 선언하는것이다.
<h1>{{ data.num }}</h1>
const data = inject('data') //자식 컴포넌트에서 data를 inject
👉 쿠키와 비슷한 개념으로 브라우저에 값을 저장하는 LocalStorage가 있다.
👉 하지만 쿠키는 시간이 지나면 소멸해버리는 기능이 있어 개인정보와 관련된 로그인에 특화되어있지만, 로컬스토리지는 안전이 필요한 데이터를 저장하기엔 무리가 있다.
localStorage.setItem('count', JSON.stringify(data.value))
// data.value라는 값을 count라는 이름으로 로컬스토리지에 저장하는 코드이다
// 하지만 데이터를 넣기 위해선 json문자열로 변경이 필요하므로 stringify를 통해 형변환을 거친다
onMounted(() => {
console.log("mounted...............")
const str = localStorage.getItem('count')
// 스토리지에 저장된 count 값을 str변수에 넣기
if(!str){
return
}
const obj = JSON.parse(str) // json문자열에서 일반문자열로 변경
data.value = obj
})
👉 해당 코드처럼 onMounted라는 함수를 사용하는데, 이는 마운트가 완료되면 내부 함수가 실행되는 코드이다.
👉 마운트는 쉽게 말해 해당 컴포넌트가 화면에 표시될때를 의미한다.
<template>
<div>
<CountDisplay></CountDisplay>
<CountButtons></CountButtons>
</div>
</template>
<script setup>
import { onMounted, provide, ref } from 'vue';
import CountDisplay from './CountDisplay.vue';
import CountButtons from './CountButtons.vue';
const data = ref({num:10})
provide('data',data);
const changeNum = (amount) => {
data.value.num += amount
localStorage.setItem('count', JSON.stringify(data.value))
// 값이 변경될때마다 로컬스토리지에 저장하기
// data.value를 count라는 이름으로 스토리지에 저장하는데, json 문자열 형태로 저장해야하므로 형변환
}
provide('changeNum',changeNum)
// 컴포넌트가 마운트 되면 함수 실행
// 마운트뜻은 컴포넌트가 화면에 실제로 표시되는 과정
onMounted(() => {
console.log("mounted...............")
const str = localStorage.getItem('count')
// 스토리지에 저장된 count 값을 str변수에 넣기
if(!str){
return
}
const obj = JSON.parse(str) // json문자열에서 일반문자열로 변경
data.value = obj
})
</script>
<style lang="scss" scoped>
</style>
<template>
<div><!--버튼의 다양한 종류를 만들기 위한 컨테이너-->
<CustomButton :label="'PLUS'" :amount="1"></CustomButton>
<CustomButton :label="'MINUS'" :amount="-1"></CustomButton>
</div>
</template>
<script setup>
import CustomButton from './CustomButton.vue';
</script>
<style lang="scss" scoped>
</style>
<template>
<div>
<h1>{{ data.num }}</h1>
</div>
</template>
<script setup>
import { inject } from 'vue';
const data = inject('data')
</script>
<style lang="scss" scoped>
</style>
<template>
<div>
<button @click="()=>changeNum(props.amount)">
{{ props.label }}
</button>
</div>
</template>
<script setup>
import { inject } from 'vue';
const changeNum = inject('changeNum')
const props = defineProps(['label','amount']) // countButtons에서 만든 속성값들을 props를 통해서 받기
</script>
<style lang="scss" scoped>
</style>
<template>
<div>
<TodoList></TodoList>
</div>
</template>
<script setup>
import { provide, ref } from 'vue';
import { v4 as uuidv4 } from 'uuid';
import TodoList from './TodoList.vue';
const todoList = ref([])
const addTodo = (title, content) => {
const todo = { // 해당todoo는 addTodo 내부에 위치해야 아래 todoList의 값이 동일하게 변경된다.
tno: uuidv4(),
title: title,
content: content
}
todoList.value = [...todoList.value, todo]
}
const removeTodos = (tnoArr) => { // 선택한 tnoArr에 포함된 tno가 있는 todolist를 제거
todoList.value = todoList.value.filter(todo => !tnoArr.includes(todo.tno))
}
// 더미데이터 생성
addTodo('aaa','aaaa')
addTodo('bbb','bbbb')
addTodo('fff','ffff')
addTodo('aaa','aaaa')
addTodo('bbb','bbbb')
addTodo('fff','ffff')
addTodo('aaa','aaaa')
addTodo('bbb','bbbb')
addTodo('fff','ffff')
// provide를 통해 하위 컴포넌트에 전달
provide('addTodo',addTodo)
provide('todoList',todoList)
provide('removeTodos', removeTodos)
</script>
<style lang="scss" scoped>
</style>
<template>
<div>
<div><input type="checkbox" :value="'ALL'" v-model="selectAll"> SelectAll</div>
<ul>
<li v-for="todo in todoList" :key="todo.tno">
<input type="checkbox" :value="todo.tno" v-model="selectList"> {{ todo.tno }} -- {{ todo.title }}
<!--v-model은 양방향으로 여기서 체크하면 아래 script태그의 selectList의 배열에도 포함되도록-->
</li>
</ul>
<button @click="handleClickRemove">REMOVE</button>
</div>
</template>
<script setup>
import { inject, ref, watch } from 'vue';
const selectList = ref([])
const selectAll = ref([])
const todoList = inject('todoList')
const removeTodos = inject('removeTodos')
const handleClickRemove = () => {
removeTodos(selectList.value)
}
watch(selectAll,(newValue) => { // watch는 상태가 변경됐는지 안됐는지 감시하는 역할
if(!newValue || newValue == '') { // todoList
console.log("uncheck")
selectList.value.length = 0
} else if(newValue == 'ALL')
console.log("check")
todoList.value.forEach(t => {
selectList.value.push(t.tno)
})
})
</script>
<style lang="scss" scoped>
</style>
promise : 비동기작업의 완료를 나타내는 객체, promise는 then을 이용하여 결과처리가 가능
async : async함수는 항상 promise를 반환하는 함수
await : promise가 해결될떄까지 실행을 중단. 하고 promise의 결과값 반환
then : promise가 성공적으로 완료되었을때 실행할 콜백 함수를 지정
<template>
<div>
<ul>
<li v-for="t in todos" :key="[t.id](http://t.id/)"> {{ t }}</li>
</ul>
</template>
<script setup>
import { ref } from 'vue';
const todos = ref([]) // 비동기를 통해 정보가 나중에 도착하므로 도착했을때 데이터를 실시간으로 변경해주려면 ref사용
const getData = async() => { // async는 함수를 비동기적으로 실행한다는뜻, 함수앞에 async가 있으면 항상 promise를 반환
const res = await fetch('https://jsonplaceholder.typicode.com/todos') // fetch를 이용하여 외부 API데이터를 가져오는 비동기 작업을 하는데
// fetch를 사용하면 promise를 반환하는데 await를 이용하여 promise가 해결될때까지 기다림
const jsonData = await res.json()
// fetch로 받은 값을 json형태의 데이터로 변경
return jsonData
}
getData().then(arr => todos.value = arr)
// getData는 promise를 반환하므로 then을 사용하여 then메서드의 콜백함수가 실행됨
</script>
<style lang="scss" scoped>
</style>
<template>
<div>
<ul>
<li v-for="todo in todos" :key="todo.id"> {{ todo }}</li>
</ul>
</div>
</template>
<script setup>
import axios from 'axios';
import { ref } from 'vue';
const todos = ref([])
// 비동기처리를 할건데, 비동기 처리는 시간이 좀 지난뒤 갱신이 되므로 반응형으로 처리
// async를 사용하면 비동기로 실행됨, return값은 promise
const getData = async() => {
const res = await axios.get('https://jsonplaceholder.typicode.com/todos')
// fetch를 이용하여 외부 API 데이터를 가져오는 비동기 작업
// await를 사용하면 비동기 작업이 완료될 때까지 기다린 후 코드가 실행됨.
return res.data
// res.data는 promise
}
getData().then(arr => todos.value = arr)
// getData는 결국 promise를 반환하므로 then에서는 콜백함수가 실행됨.
// arr은 getData에서 반환된 데이터 -> API에서의 todos목록
// then = 앞에꺼 끝났으면 이거 해줘
</script>
<style lang="scss" scoped>
</style>
<template>
<div>
<ul>
<li v-for="i in arr" :key="i.sno">
{{ i }}
</li>
</ul>
<div>
<button @click="() => handleClick('s1.json')">s1</button>
<button @click="() => handleClick('s2.json')">s2</button>
</div>
</div>
</template>
<script setup>
import axios from 'axios';
import { ref } from 'vue';
const getStores = async(fileName) => { // fileName (json파일)을 매개변수로
const res = await axios.get(`http://localhost:5173/${fileName}`)
console.log(res)
return res.data // res 로그내부에 data라는 속성값을 반환한다.
}
const arr = ref([])
const handleClick = (file) => { // 클릭하는 이벤트를 만드는데, 매개변수로 file이름을 받는다.
getStores(file).then( result => { //getStores의 promise가 return되면 then 내부가 실행되어 ref인 arr이 result값(file값)이 적용된다.
arr.value = result
} )
// getStores의 return값은 promise로 주는데, return값인 res.data를 받은 이후 then이 실행됨.
}
</script>
<style lang="scss" scoped>
</style>