Vue3 在Vue2 的基础上做了许多的改进,主要有以下方面:1)性能提升,Vue3 对虚拟DOM进行了重写,引入了静态树和新的编译器。提高了渲染性能。2)响应式系统的改进,使用Proxy代理对象替代了Vue2中的Object.defineProperty。3)更好的TypeScript支持。
1 Vue3 基础
Vue的组件可以按两种不同的风格书写:选项式API和组合式API。组合式API通常会与<script setup>搭配使用(setup 是一个标识,告诉Vue需要在编译时进行一些处理)。
1.1 指令
指令时带有v-前缀的特殊attribute。例如v-bind 和 v-for等。指令参数是指指令标识冒号后面的表达式(或字符串)。v-bind:href, href 是指令参数,表示将值绑定到href属性上。指令参数有以下特点:
- 指令参数可以动态指定。
- 动态参数中的表达式的值应当是一个字符串,或者是null(表示显式移除该绑定)。且不能有引号。否则会有警告。
- 元素标签使用动态指令参数会有警告。
<script setup lang="ts">
import DirectiveView from "@/article/components/DirectiveView.vue";
import {ref} from "vue";
const directiveAtt = ref("title")
const aAtt = ref("href")
</script>
<template>
<div class="app-container">
<div @click="directiveAtt === 'title'? directiveAtt = 'otherInfo' : directiveAtt = 'title'">更换指令</div>
<directive-view :[directiveAtt]="'hello directive'"/>
<!-- 原始标签有警告-->
<a :[aAtt]="'www.baidu.com'">百度</a>
</div>
</template>
DirectiveView.vue
<script setup lang="ts">
import {defineProps} from "vue";
defineProps(["title","otherInfo"])
</script>
<template>
<div>title: {{ title }}</div>
<div>otherInfo: {{ otherInfo }}</div>
</template>
1.2 响应式基础
修改响应式状态时,DOM会被自动更新,当DOM更新不是同步的。Vue会在“next tick”更新周期中缓冲所有状态的修改。以确保不管你进行了多少次状态修改,每个组件都只会被更新一次。
nextTick() 是一个异步方法,用于等待下一次DOM更新刷新。
1.2.1 响应式状态
reactive 及 ref 都是用于声明响应式状态。reactive仅支持为对象声明响应状态,ref可以传入基本类型及对象(底层使用reactive)。
reactive |
将响应对象的原始类型属性结构为本地变量,或者将该属性传递给函数时,将丢失响应性连接。 |
ref |
|
表 reactive 及 ref 的特点
<script setup lang="ts">
import {nextTick, reactive, ref} from "vue";
let count = ref(0)
async function addCountHandler() {
const titleElement = document.querySelector("#title")
count.value++;
console.log("before nextTick",titleElement.textContent) // 0
await nextTick()
console.log("nextTick",titleElement.textContent) // 1
}
const sumRef = ref(0)
let obj = reactive({sum:sumRef,name: 'js'})
sumRef.value = 1;
console.log(obj) // {sum:RefImpl,name: 'js'}
obj.sum = 2;
console.log(sumRef.value) // 2
let { sum } = obj
console.log(sum) // 2
sum = 3
console.log(sum) // 3
console.log(obj.sum) // 2
console.log(sumRef.value) // 2
let tempObj = ref({num: 1})
let tempObj2 = {num: ref(99)}
function changeSum(sumVal) {
sumVal = 999
}
changeSum(obj.sum)
console.log(obj.sum) // 2
</script>
<template>
<div class="app-container">
<div id="title" @click="addCountHandler">{{ count }}</div>
<!-- 会被解包-->
<div>reactive解包:{{obj.sum + 1}} {{obj.sum}}</div>
<!-- 会被解包-->
<div>ref解包:{{tempObj.num + 1}} {{ tempObj.num }}</div>
<!--[object Object]1 99-->
<div>ref为非顶级属性: {{ tempObj2.num + 1}} {{ tempObj2.num }}</div>
</div>
</template>
1.3 Class与Style绑定
class 与 style 的表达式处理字符串外也可以是对象或数组。
<script setup lang="ts">
import {ref} from "vue";
const classObj = ref({title: true,red: true})
const classObj2 = ref({border: true})
const styleObj = {color: 'green','background-color': 'red'}
const styleObj2 = {'font-weight': 'bold'}
let tag = ref(true)
</script>
<template>
<div :class="[classObj,tag ? classObj2 : '']">
Hello Class
</div>
<div :style="[styleObj,styleObj2]">JS + TS + VUE</div>
</template>
<style scoped>
.title {
font-size: 17px;
font-weight: bold;
}
.red {
color: red;
}
.border {
border-bottom: solid 1px blue;
}
</style>
类与样式作用于组件时,如果组件有一个根标签,则类与样式都会被这个根标签基础。否则,需要在组件的标签中,指定需要继承的样式与类($attrs.class及 $attrs.style)。
<script setup lang="ts">
import StyleView from "@/article/components/StyleView.vue";
</script>
<template>
<style-view style="color: red;font-weight: bold" class="title"/>
</template>
StyleView.vue
<template>
<div>
<div>Hello StyleView</div>
<div :class="$attrs.class" :style="$attrs.style">TS</div>
</div>
</template>
1.4 侦听器
1)watch 的第一个参数可以是不同形式的数据源:ref(包括计算属性)、一个响应式对象、一个getter函数、或多个数据源组成的数组。
2)当有多个数据源时,可以使用watchEffect函数,它可以自动跟踪回调的响应式依赖。
3)如果想在侦听器回调中更访问被Vue更新之后的所属组件的DOM,需要指明flush: ‘POST’选项。
<script setup lang="ts" xmlns="http://www.w3.org/1999/html">
import {ref, watch, watchEffect} from "vue";
let count = ref(0)
let tag = ref(false)
watch([count,tag],([newCount,newTag],[oldCount,oldTag]) => {
console.log("-----------watch")
console.log("newCount:" + newCount + ";newTag:" + newTag + ";oldCount:" + oldCount + ";oldTag:" + oldTag)
console.log("watch,element:" + document.querySelector("#count")?.textContent)
console.log("-------------")
})
watchEffect(() => {
console.log("-----------watchEffect")
console.log("watchEffect, count:" + count.value + ",tag:" + tag.value)
console.log("watchEffect,element:" + document.querySelector("#count")?.textContent)
console.log("-------------")
})
watch(count,(newCount) => {
console.log("-----------watchPost")
console.log("newCount:" + newCount)
console.log("watch post,element:" + document.querySelector("#count")?.textContent)
console.log("-------------")
},{flush: "post"})
</script>
<template>
<div>
<button @click="count++" id="count">{{count}}</button>
<div/>
<button @click="tag = !tag">{{tag}}</button>
</div>
</template>