文章來(lái)源于公眾號(hào):前端有的玩
最近在工作之余一直學(xué)習(xí) Vue3.0 相關(guān)知識(shí),雖然 Vue3.0 至今還是 rc 版,但這并不影響我們?nèi)W(xué)習(xí)。今天這篇文章主要講解了以下內(nèi)容:
Vue3.0
中使用watch
Vue3.0
中使用計(jì)算屬性Vue3.0
中使用vue-router
Vue3.0
中使用vuex
Vue3.0
中使用watch
watch
在Vue3.0
中并不是一個(gè)新的概念,在使用Vue2.x
的時(shí)候,我們經(jīng)常會(huì)使用watch
來(lái)監(jiān)聽Vue
實(shí)例上面的一個(gè)表達(dá)式或者一個(gè)函數(shù)計(jì)算結(jié)果的變化。
回顧Vue2.0
中的watch
在 Vue2.0 中,我們使用watch
可以通過(guò)下面多種方式去監(jiān)聽Vue
實(shí)例上面的表達(dá)式或者函數(shù)計(jì)算結(jié)果的變化,如下羅列了其中的幾種
- 最常規(guī)使用方式
export default {
data() {
return {
name: '子君',
info: {
gzh: '前端有的玩'
}
}
},
watch: {
name(newValue, oldValue) {
console.log(newValue, oldValue)
},
'info.gzh': {
handler(newValue, oldValue) {
console.log(newValue, oldValue)
},
// 配置immediate會(huì)在watch之后立即執(zhí)行
immediate: true
}
}
}
我們可以在watch
屬性里面配置要監(jiān)聽的Vue
實(shí)例上面的屬性,也可以通過(guò).
鍵路徑去監(jiān)聽對(duì)象中的某一個(gè)屬性的變化,也可以通過(guò)配置immediate
在監(jiān)聽后立即觸發(fā),配置deep
去深度監(jiān)聽對(duì)象里面的屬性,不論嵌套層級(jí)有多深。
- 使用
$watch
監(jiān)聽
除了常規(guī)的watch
對(duì)象寫法之外,Vue
實(shí)例上面提供了$watch
方法,可以通過(guò)$watch
更靈活的去監(jiān)聽某一個(gè)屬性的變化。比如這樣一個(gè)場(chǎng)景,我們有一個(gè)表單,然后希望在用戶修改表單之后可以監(jiān)聽到表單的數(shù)據(jù)變化。但是有一個(gè)特殊的場(chǎng)景,就是表單的回填數(shù)據(jù)是異步請(qǐng)求過(guò)來(lái)的,這時(shí)候我們希望在后臺(tái)請(qǐng)求完數(shù)據(jù)之后再去監(jiān)聽變化,這時(shí)候就可以使用$watch
。如下代碼所示:
export default {
methods: {
loadData() {
fetch().then(data => {
this.formData = data
this.$watch(
'formData',
() => {
// formData數(shù)據(jù)發(fā)生變化后會(huì)進(jìn)入此回調(diào)函數(shù)
},
{
deep: true
}
)
})
}
}
}
- 監(jiān)聽函數(shù)表達(dá)式
除了監(jiān)聽字符串之外,$watch
的第一個(gè)參數(shù)也可以是一個(gè)函數(shù),當(dāng)函數(shù)的返回值發(fā)生變化之后,觸發(fā)回調(diào)函數(shù)
this.$watch(() => this.name, () => {
// 函數(shù)的返回值發(fā)生變化,進(jìn)入此回調(diào)函數(shù)
})
上文中就是 Vue2.0 中我們使用watch
的一些常用寫法,對(duì)于Vue3.0
,因?yàn)槠鋵?duì) Vue2.0 做了部分的向下兼容,所以上面的用法在Vue3.0
中基本都可以使用,但是Vue3.0
一個(gè)很大的亮點(diǎn)就是composition API
,所以除了 Vue2.0 中的寫法之外,也可以使用componsition api
中提供的watch
在Vue3.0
中使用watch
在Vue3.0
中,除了 Vue2.0 的寫法之外,有兩個(gè)api
可以對(duì)數(shù)據(jù)變化進(jìn)行監(jiān)聽,第一種是watch
,第二種是watchEffect
。對(duì)于watch
,其與 Vue2.0 中的$watch
用法基本是一模一樣的,而watchEffect
是Vue3.0
新提供的api
watch的用法
下面的示例演示了如何使用watch
import { watch, ref, reactive } from 'vue'
export default {
setup() {
const name = ref('子君')
const otherName = reactive({
firstName: '王',
lastName: '二狗'
})
watch(name, (newValue, oldValue) => {
// 輸出 前端有的玩 子君
console.log(newValue, oldValue)
})
// watch 可以監(jiān)聽一個(gè)函數(shù)的返回值
watch(
() => {
return otherName.firstName + otherName.lastName
},
value => {
// 當(dāng)otherName中的 firstName或者lastName發(fā)生變化時(shí),都會(huì)進(jìn)入這個(gè)函數(shù)
console.log(`我叫${value}`)
}
)
setTimeout(() => {
name.value = '前端有的玩'
otherName.firstName = '李'
}, 3000)
}
}
watch
除了可以監(jiān)聽單個(gè)值或者函數(shù)返回值之外,也可以同時(shí)監(jiān)聽多個(gè)數(shù)據(jù)源,比如下面代碼所示:
export default {
setup() {
const name = ref('子君')
const gzh = ref('前端有的玩')
watch([name, gzh], ([name, gzh], [prevName, prevGzh]) => {
console.log(prevName, name)
console.log(prevGzh, gzh)
})
setTimeout(() => {
name.value = '前端有的玩'
gzh.value = '關(guān)注我,一起玩前端'
}, 3000)
}
}
watchEffect的用法
watchEffect
的用法與watch
有所不同,watchEffect
會(huì)傳入一個(gè)函數(shù),然后立即執(zhí)行這個(gè)函數(shù),對(duì)于函數(shù)里面的響應(yīng)式依賴會(huì)進(jìn)行監(jiān)聽,然后當(dāng)依賴發(fā)生變化時(shí),會(huì)重新調(diào)用傳入的函數(shù),如下代碼所示:
import { ref, watchEffect } from 'vue'
export default {
setup() {
const id = ref('0')
watchEffect(() => {
// 先輸出 0 然后兩秒后輸出 1
console.log(id.value)
})
setTimeout(() => {
id.value = '1'
}, 2000)
}
}
- 停止執(zhí)行
Vue2.0 中的$watch
會(huì)在調(diào)用的時(shí)候返回一個(gè)函數(shù),執(zhí)行這個(gè)函數(shù)可以停止watch
,如下代碼所示
const unwatch = this.$watch('name',() => {})
// 兩秒后停止監(jiān)聽
setTimeout(()=> {
unwatch()
}, 2000)
在Vue3.0
中,watch
與watchEffect
同樣也會(huì)返回一個(gè)unwatch
函數(shù),用于取消執(zhí)行,比如下面代碼所示
export default {
setup() {
const id = ref('0')
const unwatch = watchEffect(() => {
// 僅僅輸出0
console.log(id.value)
})
setTimeout(() => {
id.value = '1'
}, 2000)
// 1秒后取消watch,所以上面的代碼只會(huì)輸出0
setTimeout(() => {
unwatch()
}, 1000)
}
}
- 清除副作用
想象一下這樣的一個(gè)場(chǎng)景,界面上面有兩個(gè)下拉框,第二個(gè)下拉框的數(shù)據(jù)是根據(jù)第一個(gè)下拉框的數(shù)據(jù)聯(lián)動(dòng)的,當(dāng)?shù)谝粋€(gè)下拉框數(shù)據(jù)發(fā)生變化后,第二個(gè)下拉框的數(shù)據(jù)會(huì)通過(guò)發(fā)送一個(gè)網(wǎng)絡(luò)請(qǐng)求進(jìn)行獲取。這時(shí)候我們可以通過(guò)watchEffect
來(lái)實(shí)現(xiàn)這個(gè)功能,比如像下面代碼這樣
import { ref, watchEffect } from 'vue'
function loadData(id) {
return new Promise(resolve => {
setTimeout(() => {
resolve(
new Array(10).fill(0).map(() => {
return id.value + Math.random()
})
)
}, 2000)
})
}
export default {
setup() {
// 下拉框1 選中的數(shù)據(jù)
const select1Id = ref(0)
// 下拉框2的數(shù)據(jù)
const select2List = ref([])
watchEffect(() => {
// 模擬請(qǐng)求
loadData(select1Id).then(data => {
select2List.value = data
console.log(data)
})
})
// 模擬數(shù)據(jù)發(fā)生變化
setInterval(() => {
select1Id.value = 1
}, 3000)
}
}
現(xiàn)在假如我切換了一下第一個(gè)下拉框的數(shù)據(jù)之后,這時(shí)候數(shù)據(jù)請(qǐng)求已經(jīng)發(fā)出,然后我將這個(gè)頁(yè)面切換到另一個(gè)頁(yè)面,因?yàn)檎?qǐng)求已經(jīng)發(fā)出,所以我希望在頁(yè)面離開的時(shí)候,可以結(jié)束這個(gè)請(qǐng)求,防止數(shù)據(jù)返回后出現(xiàn)異常,這時(shí)候就可以使用watchEffect
為第一個(gè)回調(diào)函數(shù)傳入的入?yún)?lái)處理這個(gè)情況,如下代碼所示
function loadData(id, cb) {
return new Promise(resolve => {
const timer = setTimeout(() => {
resolve(
new Array(10).fill(0).map(() => {
return id.value + Math.random()
})
)
}, 2000)
cb(() => {
clearTimeout(timer)
})
})
}
export default {
setup() {
// 下拉框1 選中的數(shù)據(jù)
const select1Id = ref(0)
// 下拉框2的數(shù)據(jù)
const select2List = ref([])
watchEffect(onInvalidate => {
// 模擬請(qǐng)求
let cancel = undefined
// 第一個(gè)參數(shù)是一個(gè)回調(diào)函數(shù),用于模擬取消請(qǐng)求,關(guān)于取消請(qǐng)求,可以了解axios的CancelToken
loadData(select1Id, cb => {
cancel = cb
}).then(data => {
select2List.value = data
console.log(data)
})
onInvalidate(() => {
cancel && cancel()
})
})
}
}
Vue3.0
中使用計(jì)算屬性
想一想在 Vue2.0 中我們一般會(huì)用計(jì)算屬性做什么操作呢?我想最常見的就是當(dāng)模板中有一個(gè)復(fù)雜計(jì)算的時(shí)候,可以先使用計(jì)算屬性進(jìn)行計(jì)算,然后再在模板中使用,實(shí)際上,Vue3.0
中的計(jì)算屬性的作用和 Vue2.0 的作用基本是一樣的。
- 在
Vue2.0
中使用計(jì)算屬性
computed: {
getName() {
const { firstName, lastName } = this.info
return firstName + lastName
}
},
- 在
Vue3.0
中使用計(jì)算屬性
<template>
<div class="about">
<h1>{{ name }}</h1>
</div>
</template>
<script>
import { computed, reactive } from 'vue'
export default {
setup() {
const info = reactive({
firstName: '王',
lastName: '二狗'
})
const name = computed(() => info.firstName + info.lastName)
return {
name
}
}
}
</script>
和 Vue2.0 一樣,Vue3.0
的計(jì)算屬性也可以設(shè)置getter
和setter
,比如上面代碼中的計(jì)算屬性,只設(shè)置了getter
,即加入cumputed
傳入的參數(shù)是一個(gè)函數(shù),那么這個(gè)就是getter
,假如要設(shè)置setter
,需要像下面這樣去寫
export default {
setup() {
const info = reactive({
firstName: '王',
lastName: '二狗'
})
const name = computed({
get: () => info.firstName + '-' + info.lastName,
set(val) {
const names = val.split('-')
info.firstName = names[0]
info.lastName = names[1]
}
})
return {
name
}
}
}
Vue3.0
中使用vue-router
初始化vue-router
在 Vue2.0 中我們使用vue-router
的時(shí)候,會(huì)通過(guò)new VueRouter
的方式去實(shí)現(xiàn)一個(gè)VueRouter
實(shí)例,就像下面代碼這樣
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const router = new VueRouter({
mode: 'history',
routes: []
})
export default router
但到了Vue3.0
,我們創(chuàng)建VueRouter
的實(shí)例發(fā)生了一點(diǎn)點(diǎn)變化,就像Vue3.0
在main.js
中初始化Vue
實(shí)例需要像下面寫法一樣
import { createApp } from 'vue'
createApp(App).$mount('#app')
vue-router
也修改為了這種函數(shù)的聲明方式
import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
// vue-router有hash和history兩種路由模式,可以通過(guò)createWebHashHistory和createWebHistory來(lái)指定
history: createWebHashHistory(),
routes
})
router.beforeEach(() => {
})
router.afterEach(() => {
})
export default router
然后在main.js
中,通過(guò)
createApp(App).use(router)
來(lái)引用到Vue
中
在setup
中使用vue-router
在 Vue2.0 中,我們通過(guò)this.$route
可以獲取到當(dāng)前的路由,然后通過(guò)this.$router
來(lái)獲取到路由實(shí)例來(lái)進(jìn)行路由跳轉(zhuǎn),但是在setup
中,我們是無(wú)法拿到this
的,這也意味著我們不能像 Vue2.0 那樣去使用vue-router
, 此時(shí)就需要像下面這樣去使用
import { useRoute, useRouter} from 'vue-router'
export default {
setup() {
// 獲取當(dāng)前路由
const route = useRoute()
// 獲取路由實(shí)例
const router = useRouter()
// 當(dāng)當(dāng)前路由發(fā)生變化時(shí),調(diào)用回調(diào)函數(shù)
watch(() => route, () => {
// 回調(diào)函數(shù)
}, {
immediate: true,
deep: true
})
// 路由跳轉(zhuǎn)
function getHome() {
router.push({
path: '/home'
})
}
return {
getHome()
}
}
}
上面代碼我們使用watch
來(lái)監(jiān)聽路由是否發(fā)生變化,除了watch
之外,我們也可以使用vue-router
提供的生命周期函數(shù)
import { onBeforeRouteUpdate, useRoute } from 'vue-router'
export default {
setup() {
onBeforeRouteUpdate(() => {
// 當(dāng)當(dāng)前路由發(fā)生變化時(shí),調(diào)用回調(diào)函數(shù)
})
}
}
除了onBeforeRouteUpdate
之外,vue-router
在路由離開的時(shí)候也提供了一個(gè)生命周期鉤子函數(shù)
onBeforeRouteLeave(() => {
console.log('當(dāng)當(dāng)前頁(yè)面路由離開的時(shí)候調(diào)用')
})
Vue3.0
中使用vuex
其實(shí)vuex
在Vue3.0
中的使用方式和vue-router
基本是一致的
初始化vuex
首先新建store/index.js
,然后添加如下代碼
import { createStore } from 'vuex'
export default createStore({
state: {},
mutations: {},
actions: {}
})
然后在main.js
中,通過(guò)以下方式使用
createApp(App).use(store)
在setup
中使用vuex
和useRouter
一樣,vuex
也提供了useStore
供調(diào)用時(shí)使用,比如下面這段代碼
import router from '@/router'
import store from '@/store'
router.beforeEach(async (to, from, next) => {
if (
to.path !== '/login' &&
store.getters['permission/getRoleMenus'].length === 0
) {
await store.dispatch('permission/loadRoleMenus')
next()
} else {
next()
}
})
其余的使用方式基本和Vue2.0
中的用法是一致的,大家具體可以參考vuex
官方文檔
Vue3.0中的生命周期鉤子函數(shù)
在前文中,我們說(shuō)到Vue3.0
是兼容一部分 Vue2.0 的,所以對(duì)于 Vue2.0 的組件寫法,生命周期鉤子函數(shù)并未發(fā)生變化,但是假如你使用的是componsition api
,那么就需要做一部分調(diào)整
- 取消
beforeCreate
與created
在使用componsition api
的時(shí)候,其實(shí)setup
就代替了beforeCreate
與created
,因?yàn)?code>setup就是組件的實(shí)際入口函數(shù)。
beforeDestroy
與destroyed
改名了
在setup
中,beforeDestroy
更名為onBeforeUnmount
,destroyed
更名為onUnmounted
- 將生命周期函數(shù)名稱變?yōu)?code>on+XXX,比如
mounted
變成了onMounted
,updated
變成了onUpdated
在setup
中使用生命周期函數(shù)的方式
setup() {
onMounted(() => {
console.log('mounted!')
})
onUpdated(() => {
console.log('updated!')
})
onUnmounted(() => {
console.log('unmounted!')
})
}
實(shí)際用法與 Vue2.0 基本是一致的,只是寫法發(fā)生了變化,所以學(xué)習(xí)成本是很低的。
以上就是W3Cschool編程獅
關(guān)于使用Vue3.0,我收獲了哪些知識(shí)點(diǎn)(二)的相關(guān)介紹了,希望對(duì)大家有所幫助。