一、Vue3初体验
1.1 Vue3与Vue2的语法区别
由于选项式API——Vue2的代码是按区域去划分代码块的,比如:methods方法区域,computed计算区域,watch观察区域。但是由于代码量增加,Vue2的情况下写的代码数据与逻辑并不能很好的归并到一起,为增加代码的可读性,所以我们使用组合式API——Vue3代替。
二、语法区别
1.1 setup
- setup()在组件被创建之前执行,这里不需要使用this,this不会指向实例。
- 在vue3中舍弃了**beforeCreate()与created()方法,在这两个方法内写的方法都可以在setup()**中执行。
- setup()会将内部的参数通过【return】暴露给其他组件。
- 通过【ref】可以实现响应式变量
- 通过【reactive】可以实现响应式对象
- 通过【…】解构出来的数据不具备响应式,但是我们可以通过【toRefs】把它变成响应式
<template>
<div>
<div>
{{ msg }}
<button @click="updateMsg">改变msg</button>
</div>
<div>
<!-- 模板会自动解析value值 -->
{{ counter }}
<button @click="updateCounter">改变counter</button>
</div>
<div>
<!-- 模板会自动解析value值 -->
{{ obj }}
<button @click="updateObj">改变obj</button>
</div>
<div>
{{ name }}
{{ age }}
{{children}}
<button @click="updateObj">改变obj</button>
</div>
</div>
</template>
<script>
// 引入响应式
import {ref, reactive,toRefs} from "vue";
export default {
name: "App",
setup() {
// 定义变量 【非响应式】
let msg = "MSG"
// 定义方法
function updateMsg() {
msg = "msg"
console.log("msg", msg)
}
// 通过ref定义响应式变量
const counter = ref(0); // ref()返回的是一个带 value的对象
function updateCounter() {
counter.value++;
console.log("counter", counter);
}
// 通过reactive定义引用类型的数据
const obj = reactive({
name: "张三",
age: 18,
children:{
name:"张三丰",
age:0
}
})
function updateObj() {
obj.name = "李四";
obj.age = 19;
}
// 暴露参数
return {
msg,
updateMsg,
counter,
updateCounter,
obj,
updateObj,
...obj,// 解构出来的变量无响应式
...toRefs(obj), // 使用toRefs则有响应式
}
}
}
</script>
<style scoped>
</style>
1.1.1 props与context
<template>
<div>
<v ref="v" :message="msg" :class="c" @updatePropsMessage="updateMessage" @updateClass="updateClass"></v>
<button @click="updateMessage('点击了父组件按钮')">父组件</button>
<button @click="updateCount">父组件修改子组件</button>
</div>
</template>
<script>
// 引入响应式
import {ref, reactive, onMounted} from "vue";
import V from "@/components/v.vue";
export default {
name: "App",
components: {V},
setup(props,context) {
const msg = ref("最初的msg");
// 子组件通过context.emit调用父组件
function updateMessage(value) {
if (value) {
msg.value = value
}
console.log("msg", msg.value);
}
// 子组件使用context.attrs
const c = ref("pink")
function updateClass(value) {
c.value = value
}
// 父组件调用子组件方法方式
const v=ref(null)
function updateCount(){
v.value.updateCount()
}
return {
msg,
updateMessage,
c,
updateClass,
updateCount,
v
}
}
}
</script>
<style scoped>
.pink {
background-color: pink;
}
.green {
background-color: green;
}
</style>
子组件
<template>
<div>
我是V组件,我得到的数值是 {{ message }}
<button @click="updateMessage">子组件</button>
<span>子组件计数:{{count}}</span>
</div>
</template>
<script>
import {ref, reactive, toRefs} from "vue";
export default {
props: {
message: {
type: String,
default: "子组件"
}
},
setup(props,context) {
// 子传父
function updateMessage(){
// console.log("slots",context.slots)
// console.log("emit",context.emit)
// console.log("expose",context.expose)
context.emit("updatePropsMessage","点击了子组件按钮")
if (context.attrs.class === 'pink') {
context.emit("updateClass", "green");
} else {
context.emit("updateClass", "pink");
}
}
const count=ref(0)
function updateCount(){
count.value++;
}
context.expose({
updateCount
})
return {
updateMessage,
count,
updateCount
}
}
}
</script>
<style scoped>
</style>
1.2 watch
- 需要导入【watch,watchEffect】方法
- 【watch】:watch(侦听响应式引用,回调函数),只能监听指定的属性
- 【watchEffect】:对象类型深度监听,自动收集方法内需要监听的变量。
<template>
<div>
<div>
{{ count }}
<button @click="updateCount">改变</button>
</div>
<div>
{{ user }}
<button @click="updateUser">改变</button>
</div>
</div>
</template>
<script>
// 引入响应式
import {ref, reactive, toRefs, watch,watchEffect} from "vue";
export default {
name: "App",
setup() {
/**
* count start
*/
const count = ref(0)
function updateCount() {
count.value++;
}
// watch(侦听响应式引用,回调函数),只能监听指定的属性
watch(count,(newValue,oldValue)=>{
console.log("oldValue",oldValue)
console.log("newValue",newValue)
})
/**
* count end
*/
/**
* user start
*/
const user = reactive({
name: "张三",
age: 18
})
function updateUser() {
user.name = "李四"
user.age = 14
}
// 对象类型深度监听,自动收集方法内需要监听的变量。
watchEffect(()=>{
console.log("user.name",user.name);
console.log("user.age",user.age);
// 可以同时监听多个
console.log("count",count.value);
})
/**
* user end
*/
return {
count,
updateCount,
user,
updateUser
}
}
}
</script>
<style scoped>
</style>
1.3 computed
- 【computed】会返回一个方法对象
<template>
<div>
<div>
{{ a }}
<button @click="updateA">改变</button>
</div>
<div>
{{ b }}
<button @click="updateB">改变</button>
</div>
<div>
{{ add }}
</div>
</div>
</template>
<script>
// 引入响应式
import {ref, reactive,computed} from "vue";
export default {
name: "App",
setup() {
const a=ref(0)
function updateA(){
a.value++;
}
const b=ref(0)
function updateB(){
b.value++;
}
// 创建为一个函数
const add=computed(()=>{
return a.value+b.value;
})
return {
a,
updateA,
b,
updateB,
add
}
}
}
</script>
<style scoped>
</style>
1.4 provide 与 inject
- provide 用于传递数据
- **inject **用于接收数据
父组件
<template>
<div>
<v></v>
<button @click="update">改变</button>
</div>
</template>
<script>
// 引入响应式
import {provide, ref, reactive} from "vue";
import V from "@/components/v.vue";
export default {
name: "App",
components: {V},
setup(props, context) {
const name = ref("张三");
provide('name', name)
function update() {
name.value = "王五"
}
return {
update
}
}
}
</script>
<style scoped>
</style>
子组件
<template>
<div>
{{name}}
</div>
</template>
<script>
import {inject} from "vue";
export default {
setup(props,context) {
// 接收字段,默认值
const name=inject('name','李四')
return {
name
}
}
}
</script>
<style scoped>
</style>
三、生命周期
1.1 Vue2 与Vue3生命周期对比
周期 | Vue2 | Vue3 |
---|---|---|
实例创建开始 | beforeCreate | 没有,setup代替了 |
实例创建完成 | created | 没有,setup代替了 |
方法创建开始 | beforeMount | onBeforeMount |
方法创建完成 | mounted | onMounted |
页面更新开始 | beforeUpdate | onBeforeUpdate |
页面更新完成 | updated | onUpdated |
组件卸载开始 | beforeUnmount | onBeforeUnmount |
组件卸载结束 | unmounted | onUnmounted |
错误捕获 | errorCaptured | onErrorCaptured |
追踪到响应式依赖 | renderTracked | onRenderTracked |
变更触发了组件渲染 | renderTriggered | onRenderTriggered |
组件被插入到 DOM | activated | onActivated |
组件从 DOM 中被移除 | deactivated | onDeactivated |
组件实例在服务器上被渲 | onServerPrefetch |
1.2生命周期使用示例
- 钩子使用需要引入。
- 可以分多个区域,多次执行生命周期函数。
<template>
<div>
<div>
{{ a }}
<button @click="updateA">改变</button>
</div>
<div>
{{ b }}
<button @click="updateB">改变</button>
</div>
<div>
{{ add }}
</div>
</div>
</template>
<script>
// 引入响应式
import {ref, reactive,onMounted} from "vue";
export default {
name: "App",
setup() {
const a=ref(0)
function updateA(){
a.value++;
}
onMounted(()=>{
updateA();
})
const b=ref(0)
function updateB(){
b.value++;
}
onMounted(()=>{
updateB();
})
onMounted(()=>{
updateA();
updateB();
})
return {
a,
updateA,
b,
updateB
}
}
}
</script>
<style scoped>
</style>
四、Vu3语法糖
在setup里面写代码需要引入并暴露变量、方法,使用起来比较麻烦,这里我们使用****标记,代码可以简介很多。
示例:
父组件
<template>
<div>
<v></v>
<button @click="update">改变</button>
</div>
</template>
<script setup>
import {provide,ref} from "vue";
import V from "@/components/v.vue";
const name = ref("张三");
provide('name', name)
function update() {
name.value = "王五"
}
</script>
<style scoped>
</style>
子组件
<template>
<div>
{{name}}
</div>
</template>
<script setup>
import {inject} from "vue";
const name=inject('name','李四')
</script>
<style scoped>
</style>