盤點(diǎn)Vu3那些有趣的API
從開始的Vue到Vue2再到現(xiàn)在的Vue3,前端開發(fā)人員一直被迫營(yíng)業(yè),永遠(yuǎn)也追不上尤大大寫代碼的腳步?? 。
今天我們放慢追趕的腳步,一起來(lái)看看尤大大在Vue3書寫了哪些有趣的API,有些可能說(shuō)不上哪里有趣,但是看起來(lái)就是比之前舒服一些(強(qiáng)迫癥在線發(fā)作...)。文末有尤大大連夜寫的新需求
data選項(xiàng)哪去了?
回想我們?cè)赩ue2中創(chuàng)建響應(yīng)式數(shù)據(jù)是這樣的:
...
data() {
return {
num:1
}
}
...
而Vue3會(huì) setup 組合式API這個(gè)選項(xiàng)了,為此引入了ref、reactive等響應(yīng)式API,來(lái)看看怎么寫的吧:
<template>
<div>num:{{num}}</div>
<div>refNum:{{refNum}}</div>
<div>state對(duì)象:名字:{{state.name}},年齡:{{state.age}}</div>
</template>
<script>
import { defineComponent, reactive, ref } from "vue"
export default defineComponent({
setup() {
const num = 1 //不具備響應(yīng)式
const refNum = ref(2)
const state = reactive({
name: '小黃',
age: 18
})
return {
num,
refNum,
state
}
}
})
</script>
你可以看到Vue2的data選項(xiàng)已經(jīng)被ref、reactive這樣的API給替換了。
那么在setup如何對(duì)ref、reactive聲明的數(shù)據(jù)修改操作呢?
...
setup() {
const count = ref(1)
console.log(count.value) // => 1
count.value++
console.log(count.value) // => 2
const str = ref('小黃')
console.log(str.value) // => 小黃
}
...
可以看到使用 ref 聲明的數(shù)據(jù)可以直接使用 .value 這種形式更新數(shù)據(jù)。但是你也許會(huì)疑問為什么在<tempalte></template>視圖中為什么不需要 .value ,其實(shí)是vue內(nèi)部做了操作。另外,打印一下可以看到ref聲明數(shù)據(jù):

reactive 也是響應(yīng)式的聲明,它是返回對(duì)象的響應(yīng)式副本
...
setup() {
const state = reactive({
name: '小黃',
age: 18
})
console.log(state) // =>
}
...
我們直接打印下state對(duì)象,它其實(shí)是一個(gè)Proxy對(duì)象:
...
setup() {
console.log(state.name) // => 小黃
console.log(state.age) // => 18
//對(duì)數(shù)據(jù)更改
setTimeout(() => {
state.age = 20
console.log(state.age) // => 20
}, 1000)
}
...
也許你已經(jīng)注意到了開頭舉的例子——所有聲明的變量都被 return 出去了,這看起來(lái)有什么好處呢?
可以更加清晰得知道,數(shù)據(jù)與視圖之前的關(guān)系。setup里對(duì)數(shù)據(jù)操作,視圖渲染return出來(lái)的數(shù)據(jù) 更好的保護(hù)組件獨(dú)有的數(shù)據(jù),不需要暴露給視圖的數(shù)據(jù)我就不寫在return里中
再者,你可能會(huì)討厭為什么通過(guò) reactive 聲明的數(shù)據(jù)在視圖中使用的時(shí)候又要 xx.屬性名 的方式才行,有辦法不寫嗎?有!尤大大滿足開發(fā)者的一切便捷編碼習(xí)慣。toRefs 就出來(lái)了,在結(jié)合ES9的擴(kuò)展運(yùn)算符 ... 就可以滿足要求了??????
<template>
//這里不再需要state包裹了,屬性已經(jīng)被展開return出來(lái)了
<div>state對(duì)象:名字:{{name}},年齡:{{age}}</div>
</template>
<script>
import {defineComponent, reactive, toRefs } from "vue"
export default defineComponent({
setup() {
const state = reactive({
name: '小黃',
age: 18
})
return {
...toRefs(state)
}
}
})
</script>
computed計(jì)算屬性更好看了?
計(jì)算屬性在使用上好像變得更加令人愉快了,先來(lái)看Vue2的寫法吧
...
data(){
return {
str:'小黃'
}
},
computed: {
newStr() { // 對(duì)data的數(shù)據(jù)重新處理
return this.str + 'hha'
},
list() { // 獲取Vuex中的數(shù)據(jù)(經(jīng)常用到)
return this.$store.state.btnMenu.btnPowerList
}
},
mounted(){}
...
中規(guī)中矩沒得說(shuō),這里computed是當(dāng)作分段式的組件內(nèi)部方法
重點(diǎn)看看Vue3中computed如何實(shí)現(xiàn)。
...
setup() {
const count = ref(3)
console.log(count.value) // => 3
const newCount = computed(() => count.value + 1)
console.log(newCount.value) // => 4
}
...
倘若需要在計(jì)算屬性中獲取Vuex的數(shù)據(jù)的話,那么可以使用Vuex提供的 useStore 模塊獲取到store的實(shí)例
import { computed, defineComponent } from "vue"
import { useStore } from 'vuex'
export default defineComponent({
setup() {
const store = useStore()
const list = computed(() => store.state.list)
return {
list
}
}
})
watch與watchEffect監(jiān)聽?
watch監(jiān)聽在Vue使用的場(chǎng)景也是比較多的。老規(guī)矩,先來(lái)看看Vue2是怎么寫的,有對(duì)比才有傷害?? ??
...
watch: {
bankName(newValue,oldValue) {
consoel.log(newValue,oldValue)
}
},
methods:{}
...
來(lái)看看Vue3怎么寫吧
<template>
<div>count:{{count}}</div>
</template>
<script>
import { defineComponent, ref, watch } from "vue"
export default defineComponent({
setup() {
const count = ref(10)
setTimeout(() => {
count.value = 20
}, 2000)
watch(count, (newValue, oldValue) => {
console.log(oldValue)
console.log(newValue)
})
return {
count
}
}
})
</script>
然后watch監(jiān)聽到count變化,預(yù)期地打印

當(dāng)我們想定義一個(gè)響應(yīng)式對(duì)象怎么辦呢?這里通常選用 reactive ,當(dāng)然ref也可以定義一個(gè)對(duì)象。
<template>
<div>person對(duì)象:名字:{{person.name}},年齡:{{person.age}}</div>
</template>
<script>
import { defineComponent, reactive, watch } from "vue"
export default defineComponent({
setup() {
const person = reactive({
name: '前端發(fā)現(xiàn)',
age: 18
})
setTimeout(() => {
person.name = '我是reactive定義的name屬性更改后的數(shù)據(jù)'
}, 2000)
watch(() => person.name, (newValue, oldValue) => {
console.log(oldValue)
console.log(newValue)
})
return {
person
}
}
})
</script>
...
可以看到,這里問采用了函數(shù)的形式返回了需要監(jiān)聽的數(shù)據(jù).() => person.name。來(lái)看看案例的效果:
以上都是監(jiān)聽單一的數(shù)據(jù),多個(gè)屬性怎么監(jiān)聽呢?直接上??
<template>
<div>count:{{count}}</div>
<div>person對(duì)象:名字:{{person.name}},年齡:{{person.age}}</div>
</template>
<script>
import { defineComponent, reactive, ref, watch } from "vue"
export default defineComponent({
setup() {
const count = ref(10)
const person = reactive({
name: '前端發(fā)現(xiàn)',
age: 18
})
setTimeout(() => {
count.value = 20
person.name = '我是reactive定義的name屬性更改后的數(shù)據(jù)'
person.age = 22
}, 2000)
watch([count, () => person.name, () => person.age], ([newCount, newName, newAge], [oldCount, oldName, oldAge
]) => {
console.log(oldCount, oldName, oldAge)
console.log(newCount, newName, newAge)
})
return {
count,
person
}
}
})
</script>
直接數(shù)組的形式監(jiān)聽多個(gè)數(shù)據(jù)

除了watch之外,Vue3還誕生出了 watchEffect ,看起來(lái)像是watch Plus(我暫且這樣理解吧),官方是怎么定義這個(gè)API的呢?
在響應(yīng)式地跟蹤其依賴項(xiàng)時(shí)立即運(yùn)行一個(gè)函數(shù),并在更改依賴項(xiàng)時(shí)重新運(yùn)行它。
//當(dāng)然這里是需要從vue導(dǎo)入模塊滴
import { watchEffect } from "vue"
...
const count = ref(10)
watchEffect(() => console.log(count.value)) // => 10
setTimeout(() => {
count.value = 20 立即執(zhí)行watchEffect方法,調(diào)出打印 // => 20
}, 1000)
先看效果:
可以看到使用 watchEffect 并沒有之前 watch 的更改前數(shù)據(jù),也不需要傳入監(jiān)聽的數(shù)據(jù)源 ,而是直接執(zhí)行一個(gè)函數(shù),可以獲取到更新后的數(shù)據(jù)。
同樣的,監(jiān)聽多個(gè)的話也是可以的
...
watchEffect(() => {
console.log(count.value)
console.log(person.name)
})
...
總結(jié):watch ?? watchEffect
watchEffect 不需要指定監(jiān)聽的屬性,他會(huì)自動(dòng)的收集依賴,只要在回調(diào)函數(shù)中引用到了響應(yīng)式的屬性,那么當(dāng)這些屬性變動(dòng)的時(shí)候,這個(gè)回調(diào)都會(huì)執(zhí)行,而 watch 只能監(jiān)聽指定的屬性而作出變動(dòng)(v3開始能夠同時(shí)指定多個(gè)) watch 能夠獲取到新值與舊值(更新前的值),而 watchEffect 是拿不到的 watchEffect 在組件初始化的時(shí)候就會(huì)執(zhí)行一次用以收集依賴,收集到的依賴發(fā)生變化時(shí)再執(zhí)行。而 watch 則是直接指定依賴項(xiàng)
片段是啥?
在 Vue 3 中,組件現(xiàn)在正式支持多根節(jié)點(diǎn)組件,即片段!
<template>
<div>我是其中一個(gè)div</div>
<header>...</header>
<footer>...</footer>
</template>
組件狀態(tài)驅(qū)動(dòng)的 CSS 變量真香?
聽起來(lái)就感覺非常逼格
,它其實(shí)就是將css用到的屬性值在組件內(nèi)定義一個(gè)變量去替換。目前這個(gè)API目前還在試驗(yàn)性階段!
<template>
<div class="myClass">我是SFC style CSS variable injection</div>
</template>
<script>
import { reactive, ref, toRefs } from "vue"
export default {
setup() {
const minColor = ref('red')
const styleJs = reactive({
color: 'blue',
fontSize: '20px',
fontWeight: '700',
textDecoration: 'underline'
})
return { // 這里不能忘記return出去
minColor,
...toRefs(styleJs),
}
}
}
</script>
<style lang="scss" scoped>
.myClass {
text-decoration: v-bind(textDecoration);
font-weight: v-bind(fontWeight);
color: v-bind(minColor);
font-size: v-bind(fontSize);
}
</style>
來(lái)看看效果:

可以看到 myClass 類名加上我們使用 v-bind 綁定的CSS屬性值
值得說(shuō)明的是,在return中,我使用了 ...toRefs() API,不使用的話那么在寫綁定styleJs對(duì)象的屬性值時(shí)就需要注意一下
.myClass {
text-decoration: v-bind('styleJs.textDecoration');
font-weight: v-bind('styleJs.fontWeight');
color: v-bind(minColor);
font-size: v-bind('styleJs.fontSize');
}
現(xiàn)在看到的樣式綁定上的屬性名上會(huì)有一點(diǎn)點(diǎn)的變化:

有興趣的朋友可以看看咱們尤大大對(duì) 組件狀態(tài)驅(qū)動(dòng)的 CSS 變量 的提案
點(diǎn)個(gè)關(guān)注一起學(xué)習(xí)前端知識(shí)。
