父传子
1.父组件中给子组件绑定属性
2.子组件内部通过props选项接收
子传父
1.父组件中给子组件标签通过@绑定事件
2.子组件内部通过emit方法触发事件
子组件
<script setup>
// 由于写setup,所以无法直接配置props选项
// 所以需要借助"编译器宏"函数接收父组件传递的数据
const props = defineProps({
car: String,
money: Number,
addMoney: Function
})
console.log(props.car)
//子传父
const emit = defineEmits(['change-Money'])
const buy = () => {
emit('change-Money', 5)
}
</script>
<template>
<div class="son">
<!-- 子组件可以直接使用传递过来的数据 -->
我是子组件 - {{ car }} - {{ money }} - <button @click="addMoney">+1</button>
<div> <button @click="buy">-Money</button></div>
</div>
</template>
<style>
.son {
border: 1px solid #000;
padding: 30px;
}
</style>
父组件
<script setup>
//父传子
// 1.给子组件,添加属性方式传值
// 2.子子组件,通过props接收
import mychild from '@/components/mychild.vue'
// 传递动态值
import { ref } from 'vue';
const money = ref(100)
const addMoney = () => {
money.value++
}
//子传父
// 1.在子组件内部,emit触发事件
// 2.在父组件通过@监听
const changeFn = (m) => {
money.value -= m
}
</script>
<template>
<div>
父组件 - {{ money }} - <button @click="addMoney">+1</button>
<mychild car="我是父组件传过来的参数" :money="money" :addMoney="addMoney" @change-Money="changeFn"></mychild>
</div>
</template>
defineProps原理: 就是编译阶段的一个标识,实际编译器解析时,遇到后就会进行编译转换
模板引用
通过ref标识获取真实的dom对象或者组件实例对象
1.调用ref函数生成一个ref对象
2.通过ref表示绑定ref对象到标签
defineExpose()
默认情况下<script setup>语法糖下组件的属性和方法是不开放给父组件的
可以用过defineExpose编译宏指定哪些属性和方法允许访问
子组件
<script setup>
const count = 999
const sayHi = () => {
console.log("你好")
}
defineExpose({
count,
sayHi
})
</script>
<template>
<div>
我是子组件 - {{ 999 }}
</div>
</template>
父组件
<script setup>
import myRefChild from '@/components/my-ref-child.vue'
import { ref, onMounted } from 'vue';
// 模板引用,可以获取dom,也可以获取组件
// 1.调用ref函数,生成一个ref对象
// 2.通过ref标识进行标定
// 3.通过ref对象.value即可访问到绑定的元素(必须渲染后才能拿到)
const myInput = ref(null)
onMounted(() => {
console.log("myInput")
// myInput.value.focus()
})
const clickFn = () => {
myInput.value.focus()
}
const myChild = ref(null)
const getMyDom = () => {
console.log(myChild.value)
myChild.value.sayHi()
}
onMounted(() => {
console.log(myChild.value)
})
</script>
<template>
<div>
<input ref="myInput" type="text" />
<button @click="clickFn">点击输入聚焦</button>
<div>
<myRefChild ref="myChild"></myRefChild>
<button @click="getMyDom">获取组件信息</button>
</div>
</div>
</template>
provide和inject
顶层组件向任意的底层组件传递数据和方法,实现跨层组件通信
底层
<script setup>
import { inject } from 'vue';
const themeColor = inject('theme-color')
const count = inject('count')
const changeCount = inject('changeCount')
const changeFn = () => {
changeCount(1000)
}
</script>
<template>
<h3>我是底层组件 - {{ themeColor }} - {{ count }}</h3>
<button @click="changeFn">更新count</button>
</template>
中间
<script setup>
import childBottom from '@/components/child-bottom.vue'
</script>
<template>
<h2>我是中间组件</h2>
<childBottom></childBottom>
</template>
顶层
<script setup>
import childCenter from '@/components/child-center.vue'
import { provide, ref } from 'vue';
// 跨层传递普通数据
provide('theme-color', 'pink')
// 跨层传递响应式数据
const count = ref(100)
provide('count', count)
setTimeout(() => {
count.value = 200
}, 2000);
//跨层级传递函数,方便子组件修改父组件的数据,提供方法进行修改
provide('changeCount', (newCount) => {
count.value = newCount
})
</script>
<template>
<h1>我是顶层组件 - {{ count }}</h1>
<childCenter></childCenter>
</template>
defineOptions(vue 3.3)
有<script setup>之前,如果要定义 props,emits 可以轻而易举地添加一个与 setup 平级的属性
但是用了<script setup> 后,就没法这么干了 setup 属性已经没有了,自然无法添加与其平级的属性
为了解决这一问题,引入了 defineProps 与 defineEmits 这两个宏。
但这只解决了 props 与emits 这两个属性如果我们要定义组件的 name 或其他自定义的属性,
还是得回到最原始的用法一再添加一个普通的<script>标签这样就会存在两个<script> 标签。
让人无法接受
所以在 Vue 3.3 中新引入了 defineOptions 宏。
顾名思义,主要是用来定义 Options API 的选项。可以用define0ptions定义任意的选项,props,emits,expose, slots 除外(因为这些可以使用 defineXXX来做到)
// <script>
// export default {
// name: "test"
// }
// </script>
<script setup>
defineOptions({
name: 'test'
})
</script>
<template>
<div>Hello</div>
</template>
defineModel
在Vue3中,自定义组件上使用v-model,相当于传递一个modelValue属性,同时触发 update:modelvalue 事件<Child v-model="isVisible">
相当于
<Child :modelValue="isVisible" @update:modelValue="isVisible=$event">
我们需要先定义props,
再定义emits。其中有许多重复的代码。
如果需要修改此值,还需要手动调用 emit 函数
子组件
<script setup>
import { defineModel } from 'vue';
const modelValue = defineModel()
const changeFn = () => {
modelValue.value += 1
}
</script>
<template>
<div>
<input type="text" :value="modelValue" @input="e => modelValue = e.target.value" />
{{ modelValue }}
<button @click="changeFn">子组件更新</button>
</div>
</template>
父组件
<script setup>
import { ref } from 'vue';
import myInput from '@/components/my-input.vue'
const count = ref(100)
const changeFn = () => {
count.value += 1
}
</script>
<template>
<div>
<myInput v-model="count"></myInput>
<div>{{ count }} - <button @click="changeFn">父组件更新</button></div>
</div>
</template>
Pinia
Pinia是Vue 的最新状态管理工具,是Vuex的替代品
特点
1.提供更加简单的API (去掉了 mutation )
2.提供符合,组合式风格的API (和Vue3 新语法统一)
3.去掉了 modules 的概念,每一个 store 都是一个独立的模块
4.配合TypeScript 更加友好,提供可靠的类型推断
配置
手动添加Pinia到Vue项目
在实际开发项目的时候,关于pinia的配置,可以在项目创建时自动添加现在我们初次学习,从零开始:
1使用 Vite 创建一个空的 Vue3 项目
npm create vue@latest
2按照官方文档安装 pinia 到项目中
Pinia基础使用 - 计数器案例
1.定义store
2.组件使用store
实现步骤
引入
import { createApp } from 'vue'
import persist from 'pinia-plugin-persistedstate'
import { createPinia } from 'pinia'
const pinia = createPinia(); //创建pinia实例
//导入pinia持久化插件
pinia.use(persist)
import App from './App.vue'
createApp(App).use(pinia).mount('#app')
定义
import { defineStore } from "pinia";
import { ref } from "vue";
import { computed } from 'vue';
//定义store
// defineStore(仓库唯一标识,()=>{...})
export const useCounterStore = defineStore("counter", () => {
//声明数据 state - count
const count = ref(100)
//声明操作数据的方法 action(普通函数)
const addCount = () => {
count.value++
}
const subCount = () => {
count.value--
}
//声明基于数据派生的计算属性 getters(计算属性computed)
const double = computed(() => count.value * 2)
//声明数据 state - msg
const msg = ref("hello pinia")
return {
count,
msg,
addCount,
subCount,
double
}
}, {
//persist: true //开启持久化
persist:{
key:'自定义持久化key',
paths:['count'] //定义某个变量持久化
}
)
使用
<script setup>
import Son1 from './components/Son1.vue';
import Son2 from './components/Son2.vue';
import { useCounterStore } from '@/store/counter.js'
const counterStore = useCounterStore();
console.log(counterStore)
</script>
<template>
<h1>{{ counterStore.count }}</h1>
<h1>{{ counterStore.msg }}</h1>
<son-1></son-1>
<son-2></son-2>
</template>
<style scoped></style>