前言
Vue3已經(jīng)發(fā)布很長(zhǎng)一段時(shí)間了,雖然早就用上了框架,但是很多人依舊保持著Vue2的思維習(xí)慣,導(dǎo)致大家在實(shí)際開(kāi)發(fā)中并沒(méi)有感覺(jué)到提升,屬實(shí)是新瓶裝舊酒。我們應(yīng)該意識(shí)到這并不僅僅是一個(gè)數(shù)字大版本的迭代,而是一次全新的開(kāi)發(fā)體驗(yàn)。
讓我們一起看看在使用Vue3開(kāi)發(fā)時(shí),應(yīng)該在哪些地方做出改變?
正文
使用<script setup>
如果是從Vue2轉(zhuǎn)到Vue3,我們很熟悉的一種寫(xiě)法是選項(xiàng)式API寫(xiě)法。
?<template>
<div :class="$style.container">
<div class="title">{{ title }}</div>
<CompA />
</div>
</template>
<script>
import CompA from './CompA.vue'
export default {
components: { CompA },
setup (props, context) {
return {}
},
methods: {
doSomething () {
// do something
},
},
}
</script>
<style lang="scss" module>
</style>
通過(guò)export default
導(dǎo)出一個(gè)對(duì)象,內(nèi)部的data
,methods
,watch
都可以使用,this
依然可以保留,并指向vue
,setup
中如果想用props
、emit
等,通過(guò)參數(shù)傳遞。在setup
中可以直接使用新寫(xiě)法,組件通過(guò)components
進(jìn)行引入??梢詷O大的還原Vue2的用法,如果團(tuán)隊(duì)的組件庫(kù)是使用Vue2寫(xiě)的,可以用很小的成本就改造完成。
為了更好的類(lèi)型推導(dǎo),vue
還提供了defineComponent
方法。
export default defineComponent({
components: { CompA },
setup (props, context) {
// do something
},
})
但其實(shí)官方并不推薦這種寫(xiě)法,這種寫(xiě)法僅僅是為了兼容舊代碼,這也是你感覺(jué)Vue3沒(méi)有提升的很大一方面因素。就像是iPhone更新,當(dāng)外觀有變化時(shí)你才會(huì)覺(jué)得是大更新,系統(tǒng)升級(jí)個(gè)IOS18,你覺(jué)得卵用沒(méi)用。所以更好的方式應(yīng)該是<script setup>
標(biāo)簽對(duì)的寫(xiě)法。
<script setup lang="ts">
import { onMounted, ref } from 'vue'
const title = ref<string>('')
onMounted(() => {
title.value = 'Demo'
})
</script>
<template>
<div :class="$style.container">{{ title }}</div>
</template>
<style lang="scss" module>
</style>
你會(huì)發(fā)現(xiàn)有很多核心的變化,首先不再需要export
導(dǎo)出了,標(biāo)簽對(duì)內(nèi)直接就是一個(gè)setup
環(huán)境。ref
可以直接寫(xiě),也沒(méi)有了methods
,你寫(xiě)一個(gè)就是一個(gè)方法,直接就可以綁定。為什么呢?官方不是說(shuō)所有的值都需要return
出去嗎?放心,@vue/compiler-sfc
幫你解決了這些煩惱。
其次這種寫(xiě)法是去this
化的,比如以往我們調(diào)用router
都是this.$router
這么使用,而現(xiàn)在你需要引入useRouter
,可以更好的分辨來(lái)源。對(duì)ts
也更友好。
import { useRouter } from 'vue-router'
const router = useRouter()
組件使用也更方便,直接引入即可。
<script setup lang="ts">
import CompA from './CompA.vue'
</script>
<template>
<div :class="$style.container">
<CompA />
</div>
</template>
同名簡(jiǎn)寫(xiě)
以往我們綁定一個(gè)值需要這樣:
<template>
<div :id="id">
<Comp :title="title" />
</div>
</template>
<script>
export default {
data () {
return {
id: 'container',
title: '標(biāo)題',
}
},
}
</script>
而現(xiàn)在變得極其簡(jiǎn)單,尤其是Vue升級(jí)到v3.4.x
以上之后,因?yàn)樗黾恿送?jiǎn)寫(xiě)。
<script setup lang="ts">
const id = ref('container')
const title = ref('標(biāo)題')
</script>
<template>
<div :id>
<CompA :title />
</div>
</template>
怎么樣,有沒(méi)有覺(jué)得非常優(yōu)雅,不過(guò)比較可惜的是這個(gè)寫(xiě)法esLint
目前還不支持,會(huì)報(bào)異常,需要在.eslintrc
中忽略一下。
"rules": {
"vue/valid-v-bind": "off"
}
拒絕mixins
我們之前Vue2的模版中有很多的mixins,而且不乏有全局引入的mixins,在遷移模板時(shí),也需要一起處理,我看到官方也有案例,有mixins
的,還有extends
的。
const mixin = {
created() { console.log(1) }
}
createApp({
created() { console.log(2) },
mixins: [mixin]
})
但是均都對(duì)組合式API不友好,因?yàn)?code style="-webkit-tap-highlight-color: transparent; margin: 0px 2px; padding: 2px 4px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; border-radius: 4px; background-color: rgba(27, 31, 35, 0.05); font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace; word-break: break-all;">mixin內(nèi)部有不少調(diào)用this
內(nèi)部環(huán)境的地方,很難在<script setup>
中使用,而且mixins
最大的問(wèn)題就是,你無(wú)法溯源,別人在某個(gè)犄角旮旯引入一個(gè)全局mixins
,你根本找不到,而且也對(duì)類(lèi)型推導(dǎo)極其不友好。所以建議使用組合式函數(shù)代替。比如我們之前有一個(gè)NavBar
的mixins
,里面處理了很多邏輯,就可以用組合式函數(shù)進(jìn)行封裝。
import { onMounted, ref } from 'vue'
const commonProps = {}
const useNavbar = () => {
const navbarProps = ref<any>({})
const setNavbar = (newProps?: any) => {
navbarProps.value = {
...navbarProps.value,
...newProps || {},
...commonProps,
}
if (navbarProps.value.title && typeof document !== 'undefined') {
document.title = navbarProps.value.title
}
}
onMounted(() => {
// init
})
return {
navbarProps,
setNavbar,
}
}
export default useNavbar
在使用時(shí)引用進(jìn)來(lái)即可,而且只要你調(diào)用了useNavbar
,內(nèi)部的onMounted
也會(huì)執(zhí)行,非常方便。
import useNavbar from './useNavbar'
const { navbarProps, setNavbar} = useNavbar()
減少全局變量
之前在Vue2時(shí),我們經(jīng)常會(huì)將一些常用屬性掛載在Vue.prototype
原型上,方便內(nèi)部用this.xxx
使用。比如我們會(huì)把Request
掛載上去,Vue.prototype.$request = Request
。我們發(fā)送請(qǐng)求時(shí)直接this.$request
即可,很方便。其實(shí)很多Vue2的依賴(lài)庫(kù)都是這么寫(xiě)的,比如vue-router
,就是在install
中將$router
寫(xiě)為了全局變量,在我們使用Vue.use(Router)
后,方便我們使用。
而在Vue3中也有替代方案,app.config.globalProperties.$request = Request
。但是在使用時(shí)就比較麻煩了,因?yàn)闆](méi)有this
環(huán)境,需要從實(shí)例上取。
import { getCurrentInstance } from 'vue'
const $this = getCurrentInstance()?.appContext.config.globalProperties
$this?.$request.post('/url', {})
很深的API,既然這么深,我想我封裝一下吧。
// vueThis.ts
import { getCurrentInstance } from 'vue'
export default getCurrentInstance()?.appContext.config.globalProperties
// 使用時(shí)
import vueThis from './vueThis'
vueThis.$request.post('/url',{})
但是你說(shuō)氣人不,getCurrentInstance
還要識(shí)別調(diào)用的時(shí)機(jī),你直接賦值,相當(dāng)于引入時(shí)就運(yùn)行了,這個(gè)時(shí)候還沒(méi)實(shí)例,你還得閉包包一下,調(diào)用也不好看。
// vueThis.ts
export default () => {
return getCurrentInstance()?.appContext.config.globalProperties
}
// 使用時(shí)
import vueThis from './vueThis'
const $this = vueThis()
$this.$request.post('/url',{})
而且不光如此,你掛載全局變量,想要有類(lèi)型推導(dǎo),你還要在vue-runtime-core.d.ts
把類(lèi)型告訴人家,才好用。特別不優(yōu)雅。
import request from '@host/request'
declare module '@vue/runtime-core' {
interface ComponentCustomProperties {
$request: typeof request
}
}
所以依舊建議使用組合式函數(shù)進(jìn)行封裝,清晰又明了。
// useRequest.ts
export default () => {
const get = async (uri: string, params: any = {}) => {
return await Request.get(uri, params)
}
const post = async (uri: string, params: any = {}) => {
return await Request.post(uri, params)
}
return {
get,
post,
}
}
// 使用時(shí)
import useRequest from './useRequest'
const { post } = useRequest()
post('/url',{})
一個(gè)use只辦一件事
Vue2始終是以頁(yè)面為單位進(jìn)行思考的,即一個(gè)vue只辦一件事,至于提供的mixin
也好,props
也好,emit
也好,都是為了服務(wù)這個(gè)vue本身的,所有也是為什么簡(jiǎn)單頁(yè)面vue最好用。
但是伴隨著一個(gè)vue的功能越來(lái)越多,代碼也就越來(lái)越復(fù)雜,就變成了左圖Options API
的樣子,再加上全局屬性的亂加,mixins的亂用,組件的亂引,整體也變得越來(lái)越冗余,最終變成了大家吐槽的對(duì)象。
Vue3推出的組合式函數(shù)的概念,借鑒了React Hooks
的寫(xiě)法,將原本一個(gè)vue一件事抽象成一個(gè)vue幾件事,再用函數(shù)進(jìn)行打包。最終就是Composition API
的樣子。所以我們開(kāi)發(fā)時(shí)就應(yīng)該順應(yīng)Vue3的思維:「一個(gè)use只辦一件事」。