Vue3
学习 vue3 用于提升前端竞争力和拓展技能
组合式 API 的好处

V3 对比 V2

基础
setup
- 创造时间在
beforeCreate前 - 没有 this
- 有语法糖的写法 开发上效率提升 底层还是会编译成正常写法
语法糖
<script setup></script>
reactive 与 ref
我们希望有响应式的数据 就需要使用到他们
reactive 它的作用就是接收对象类型为参数传入返回一个响应式对象
<script setup>
// Reactive: 接受一个对象类型的数据 返回一个响应式对象
import {reactive} from "vue";
const state = reactive({count: 1000})
const setCount = () => {
state.count++
}
</script>
<template>
<div>
<div>{{ state.count }}</div>
<button @click="setCount">+1</button>
</div>
</template>
<style scoped>
</style>
ref使用来处理简单类型的数据响应式的
- 本质: 是在原有传入数据的基础上 外包了一层对象
- 底层: 包成复杂类型之后借助 Reactive 实现响应式
- 访问数据, js 区域操作需要 .value 模版中直接使用就好
<script setup>
// ref 接受简单类型 或复杂类型, 返回一个响应式对象
// 本质: 是在原有传入数据的基础上 外包了一层对象
// 底层: 包成复杂类型之后借助Reactive实现响应式
import {ref} from "vue";
const count = ref(0);
const setCount = () => {
count.value++;
}
</script>
<template>
<div>
<div>{{ count }}</div>
<button @click="setCount">+1</button>
</div>
</template>
<style scoped>
</style>
推荐声明的时候都是用 ref即可
计算属性
基本思想和 vue2 完全一样 只是修改了代码的写法
- 导入函数
conputed - 执行函数 在回调参数中 return 基于响应式数据做计算的值
小案例
<script setup>
import {computed, ref} from "vue";
const list = ref([1, 2, 3, 4, 5, 6, 7, 8])
// 基于 list 计算属性 需要过滤出 >2的数字
const computedList = computed(() => {
return list.value.filter(item => item > 2)
})
</script>
<template>
<div>{{ list }}</div>
<div>计算新数据{{ computedList }}</div>
</template>
<style scoped>
</style>
需要注意的地方:
- 计算属性不应该关心其他操作 就只做对数据的计算和操作
- 避免直接修改计算属性的值
watch
和 vue2 也是基本一致只是修改了代码的写法
作用: 监听数据的变化 并且执行设置好的回调函数
额外参数:
immediate立即执行deep深度监听
监听单个值
<script setup>
import {ref, watch} from "vue";
const count = ref(0);
const nickname = ref('张三');
const changeCount = () => {
count.value++
}
const changeNickname = () => {
nickname.value = '李四'
}
// 监视单个数据的变化
watch(count, (newVal, oldVal) => {
console.log('旧的' + oldVal, '新的' + newVal);
});
</script>
<template>
<div>{{ count }}</div>
<button @click="changeCount">改数字</button>
<div>{{ nickname }}</div>
<button @click="changeNickname">改名字</button>
</template>
<style scoped>
</style>

监听多个
// 监视多个数据
watch([count, nickname], (newArr, oldArr) => {
console.log("旧的" + oldArr, "新的" + newArr);
});

可选项
immediate立刻执行一次 , 刷新页面就可以看到打印
// 监视多个数据
watch(
[count, nickname],
(newArr, oldArr) => {
console.log("旧的" + oldArr, "新的" + newArr);
},
{
immediate: true,
}
);
deep 深度监视 默认的 watch 是浅层监视
实际上默认的监视 是无法检测到复杂类型内部的变化的 所以需要开始 deep 深度监视
const userInfo = ref({
name: '王五',
age: 18,
})
const setUserInfo = () => {
userInfo.value.age++
}
// 监视单个数据的变化
watch(userInfo, (newVal, oldVal) => {
console.log('旧的' + oldVal, '新的' + newVal);
});
<template>
<div>{{ userInfo }}</div>
<button @click="setUserInfo">plus</button>
</template>
按照这个数据形式 我们点击按钮 watch 是没有打印的 这个时候就需要深度监视了
// 监视单个数据的变化
watch(
userInfo,
(newVal, oldVal) => {
console.log("旧的" + oldVal, "新的" + newVal);
},
{
deep: true,
}
);
但是还是有个问题 就是我们这个方式只能监听到对象全部, 我们需要精确到属性的时候就需要按照以下的写法 实现监听具体的属性
watch(
() => userInfo.value.age,
(newVal, oldVal) => {
console.log("旧的" + oldVal, "新的" + newVal);
}
);

生命周期函数
Vue2 对比 Vue3 钩子函数

组件父子通信
和 vue2 的父子通信是完全一致的,主要还是语法的变化
- 由于写了 setup 无法直接配置 props 这里需要借助宏函数
const props = defineProps({
car: String,
});
在模版中 可以直接使用属性 而不需要 props.car
defineProps原理:
本质上就是编译阶段的一个标识, 实际的编辑结果会转换成配置 props
父传子
父组件
<script setup>
//父组件
import SonComponent from "@/son-component.vue";
import {ref} from "vue";
// 传递响应式数据使用 v-bind 简写 :
const money = ref(100);
</script>
<template>
<div>我是父组件</div>
<SonComponent :money="money" car="宝马"></SonComponent>
</template>
<style scoped>
</style>
子组件
<script setup>
//由于 setup 这里我们需要用宏函数接受子组件
const props = defineProps({
car: String,
money: Number
});
</script>
<template>
<div class="son">我是子组件</div>
<div>我爸爸有一辆 {{ car }} 他还给我了 {{ money }}的零花钱</div>
</template>
<style scoped>
.son {
color: aqua;
}
</style>
效果
子传父
- 父组件给子组件绑定事件
- 子组件通过 emit 方法触发事件传递
子组件
<script setup>
//由于 setup 这里我们需要用宏函数接受子组件
const props = defineProps({
car: String,
money: Number
});
const emits = defineEmits('changeMoney');
const buy = () => {
emits('changeMoney', 5)
}
</script>
<template>
<div class="son">我是子组件</div>
<div>我爸爸有一辆 {{ car }} 他还给我了 {{ money }}的零花钱</div>
<button @click="buy">花钱</button>
</template>
<style scoped>
.son {
color: aqua;
}
</style>
父组件
<script setup>
//父组件
import SonComponent from "@/son-component.vue";
import {ref} from "vue";
// 子传父
// 传递响应式数据使用 v-bind 简写 :
const money = ref(100);
const changeMoneyFn = (decMoney) => {
money.value = money.value - decMoney
};
</script>
<template>
<div>我是父组件</div>
<SonComponent
:money="money"
car="宝马" @changeMoney="changeMoneyFn"></SonComponent>
</template>
<style scoped>
</style>
模版引用
- 调用 ref 函数生成一个 ref 对象
- 在标签中用 ref 绑定对象
- 通过 ref.value 访问到绑定的元素(必须是渲染完成的)
<script setup>
import {onMounted, ref} from "vue";
import TextCom from "@/模版引用/text-com.vue";
const inp = ref(null)
onMounted(() => {
console.log(inp.value)
})
const cliFn = () => {
inp.value.focus()
}
</script>
<template>
<input ref="inp" type="text">
<button @click="cliFn">点击让输入框聚焦</button>
<text-com/>
</template>
<style scoped>
</style>
defineExpose 默认 setup 情况下组件内部的属性和方法是不对外暴露的
可以通过这个宏函数指定暴露
const message = ref("this is msg");
defineExpose({
message,
});
跨层传递
顶层组件可以向任意的组件传递数据和方法 实现跨层组件通信
- 顶层函数通过
PROVIDE函数提供数据 - 底层组件 通过
INJECT函数获取数据 - 我们可能有很多层传递 对于数据 我们主张谁的数据谁维护 所以会传递对应数据的修改方法一并到其他层
顶层
<script setup>
import CenterCom from "@/跨层传递/center-com.vue";
import BottomCom from "@/跨层传递/bottom-com.vue";
import {provide, ref} from "vue";
provide('theme-color', 'pink')
const count = ref(100);
provide('count', count)
provide('changeCount', (newCount) => {
count.value = newCount;
})
</script>
<template>
<h1>我是顶层组件</h1>
<center-com></center-com>
<bottom-com></bottom-com>
</template>
<style scoped>
</style>
中层
<script setup>
import {inject} from "vue";
const themeColor = inject('theme-color')
const changeCount = inject('changeCount')
</script>
<template>
<h2>我是中间层组件 顶层给我的 {{ themeColor }}</h2>
<button @click="changeCount(10)">修改count</button>
</template>
<style scoped>
</style>
底层
<script setup>
import {inject} from "vue";
const count = inject('count');
const changeCount = inject('changeCount')
</script>
<template>
<h3>我是底层组件 顶层给我的 {{ count }}</h3>
<button @click="changeCount(10111)">修改count</button>
</template>
<style scoped>
</style>
效果

defineOptions
我们使用了 setup 之后 与其平级的属性都无法添加了 比如 props emit 等等
如果我们要定义组件的 name 或者其他属性 又得重新添加一个 普通的标签
这里 vue3.3 更新了defineOptions来让我们可以继续设置这些属性
defineModel
vue3.3 用于解决 v3 的 v-model 和 v2 不同的地方
在 v3 中在自定义组件上使用 v-model 相当于 传递一个 modelvalue 属性同事触发一个UPDATE:MODELVALUE事件 如果想要子组件同步数据的话就需要
- 定义 props
- 使用 emit 拿到
UPDATE:MODELVALUE事件 - 然后手动调这个函数
很麻烦
Pinia
简介
是 vue 最新的状态管理工具 是 vuex 的替代品 是 redux 的竞争对手哈哈哈
他对比 vuex:
- 更简单的 api
- 符合组合式的编写风格
- 去掉了 modules 的概念 每个 store 都是独立模块
- 配合 TS 更加友好
入门
添加 pinia 到项目中
导入依赖: npm i pinia
需要在 MAIN.JS中使用 pinia
import { createApp } from "vue";
import App from "./App.vue";
import { createPinia } from "pinia";
const pinia = createPinia();
createApp(App).use(pinia).mount("#app");