theme: cyanosis
highlight: atom-one-dark
本文為稀土掘金技術社區首發簽約文章,14天內禁止轉載,14天后未獲授權禁止轉載,侵權必究!
相信有很多這樣的兄弟,學了 Vue3 的各種 API 和新特性,但公司項目依然使用的是 Vue2 ,也不知道自己的水平能否上手 Vue3 項目。其實你學的是零碎的知識點,缺少真實的使用場景。
今天就把實戰過程中遇到的十個場景分享給大家,結合尤大大推薦的 <script setup>
,希望你能從 Vue2 絲滑過渡到 Vue3!
場景一:父子組件數據傳遞
父組件數據傳遞到子組件
Vue3 中父組件同樣是通過屬性傳遞數據,但子組件接受數據的方式和 Vue2 不同。在 <script setup>
中,props 需要使用 defineProps()
這個宏函數來進行聲明,它的參數和 Vue2 props 選項的值是一樣的。
```js
```
```js
``
注意:
defineProps、
defineEmits、
defineExpose和
withDefaults這四個宏函數只能在
```
```js
```
父組件使用子組件數據
在 <script setup>
中,組件的屬性和方法默認都是私有的。父組件無法訪問到子組件中的任何東西,除非子組件通過 defineExpose
顯式的暴露出去:
```js
```
```js
```
場景二:組件之間雙向綁定
大家都知道 Vue2 中組件的雙向綁定採用的是 v-model
或 .snyc
修飾符,兩種寫法多少顯得有點重複,於是在 Vue3 中合成了一種。Vue3 統一使用 v-model
進行處理,並且可以和多個數據進行綁定,如 v-model:foo
、v-model:bar
。
v-model
等價於 :model-value="someValue"
和 @update:model-value="someValue = $event"
v-model:foo
等價於 :foo="someValue"
和 @update:foo="someValue = $event"
下面就是一個父子組件之間雙向綁定的例子:
```js
```
```js
``
子組件可以結合
input` 使用:
```js
``
如果你覺得上面的模板比較繁瑣,也可以結合
computed` 一起使用:
```js
```
場景三:路由跳轉,獲取路由參數
在 Vue2 中我們通常是使用 this.$router
或 this.$route
來進行路由的跳轉和參數獲取,但在 <script-setup>
中,是這些方法無法使用的。我們可以使用 vue-router
提供的 useRouter
方法,來進行路由跳轉:
```
``
當我們要獲取路由參數時,可以使用
vue-router提供的
useRoute`方法:
```
```
場景四:獲取上下文對象
Vue3 的 setup
中無法使用 this
這個上下文對象。可能剛接觸 Vue3 的兄弟會有點懵,我想使用 this
上的屬性和方法應該怎麼辦呢。雖然不推薦這樣使用,但依然可以通過 getCurrentInstance
方法獲取上下文對象:
```js
``
這樣我們就可以使用
$parent、
$refs` 等,幹自己想幹的事情了,下面是我打印出來的完整屬性。

場景五:插槽的使用
在 Vue2 的中一般是通過 slot
屬性指定模板的位置,通過 slot-scope
獲取作用域插槽的數據,如:
```html
```
```html
```
在 Vue3 中則是通過 `v-slot` 這個指令來指定模板的位置,同時獲取作用域插槽的數據,如:
```html
```
```html
```
注意:`v-slot` 在 Vue2 中也可以使用,但必須是 Vue2.6+ 的版本。
## 場景六:緩存路由組件
緩存一般的動態組件,Vue3 和 Vue2 的用法是一樣的,都是使用 `KeepAlive` 包裹 `Component`。但緩存路由組件,Vue3 需要結合插槽一起使用:
```js
// Vue2 中緩存路由組件
```
```js
// Vue3 中緩存路由組件
```
一個持續存在的組件可以通過 `onActivated()` 和 `onDeactivated()` 兩個生命週期鈎子注入相應的邏輯:
```
```
## 場景七:邏輯複用
Vue2 中邏輯複用主要是採用 `mixin`,但 `mixin` 會使數據來源不明,同時會引起命名衝突。所以 Vue3 更推薦的是全新的 `Composition Api`。
下面是鼠標跟蹤的例子,我們可以把邏輯提取出來:
```js
// mouse.js
import { ref, onMounted, onUnmounted } from 'vue'
// 按照慣例,組合式函數名以 use 開頭
export function useMouse() {
// 組合式函數管理的數據
const x = ref(0)
const y = ref(0)
function update(event) {
x.value = event.pageX
y.value = event.pageY
}
// 組合式函數可以掛靠在所屬組件的生命週期上,來啟動和卸載副作用
onMounted(() => window.addEventListener('mousemove', update))
onUnmounted(() => window.removeEventListener('mousemove', update))
// 通過返回值暴露所管理的數據
return { x, y }
}
```
這時候在組件中我們就可以直接使用 `mouse.js` 暴露的數據了。
```js
```
我們還可以在一個組件中引入多個組合式函數,或者在一個組合式函數中引入其他的組合式函數,這個比較簡單,我就不演示了。接下來,我們看看使用異步方法的組合式函數。
在做異步數據請求時,我們通常需要處理三個不同的狀態:加載中、加載成功和加載失敗。獲取這些狀態的邏輯是通用的,我們可以把它提取出來:
```js
// request.js
import { ref } from 'vue'
export function useRequest(url) {
const data = ref(null)
const error = ref(null)
axios.get(url)
.then((res) => (data.value = res.data))
.catch((err) => (error.value = err))
return { data, error }
}
```
現在我們在組件中只需要:
```js
```
任何組件都可以使用上面這個邏輯,這就是邏輯複用。是不是可以節省很多重複的代碼,感覺摸魚時間又要增加了~
## 場景八:生命週期
Vue3 的生命週期和 Vue2 相比,有以下改動:
- `Vue3` 生命週期鈎子都以 `on` 開頭,並且需要在組件中手動導入。
```
```
- Vue3 取消了 `beforeCreate` 和 `created` 鈎子。如果需要在組件創建前注入邏輯,直接在 `
```
Vue3 中其他的全局 API,如 `directive` 、`component` 等,跟 Vue2 的用法都差不多,只不過一個是在 Vue 上調用,一個是在 `app` 實例上調用:
```js
// main.js
// 全局自定義指令
app.directive('focus', {
mounted(el) {
el.focus()
}
})
// 全局自定義組件
import CustomComp from './components/CustomComp.vue'
app.component('CustomComp', CustomComp)
```
需要注意的是,Vue3 廢棄了 `filter` 這個方法,因為通過函數或 `computed` 可以實現一樣的功能。
## 常見十:與 TypeScript 結合使用
與 `TypeScript` 結合使用,我們只需要在 `
```
這被稱為 `運行時聲明` ,因為傳遞給 `defineProps()` 的參數會作為運行時的 props 選項使用。
- 基於類型的聲明。我們還可以通過泛型參數來定義 props 的類型,這種方式更加常用:
```ts
```
這被稱為 `基於類型的聲明` ,編譯器會盡可能地嘗試根據類型參數推導出等價的運行時選項。這種方式的不足之處在於,失去了定義 props 默認值的能力。為了解決這個問題,我們可以使用 `withDefaults` 宏函數:
```ts
```
#### 為 ref() 標註類型
- 默認推導類型。ref 會根據初始化時的值自動推導其類型:
```ts
import { ref } from 'vue'
const year = ref(2022)
year.value = '2022' // TS Error: 不能將類型 string 分配給類型 number
```
- 通過接口指定類型。有時我們可能想為 ref 內的值指定一個更復雜的類型,可以使用 `Ref` 這個接口:
```ts
import { ref } from 'vue'
import type { Ref } from 'vue'
const year: Ref = ref('2022')
year.value = 2022 // 成功!
```
- 通過泛型指定類型。我們也可以在調用 `ref()` 時傳入一個泛型參數,來覆蓋默認的推導行為:
```ts
const year = ref('2022')
year.value = 2022 // 成功!
```
#### 為 reactive() 標註類型
- 默認推導類型。`reactive()` 也會隱式地從它的參數中推導類型:
```ts
import { reactive } from 'vue'
const book = reactive({ title: 'Vue 3 指引' })
book.year = 2022 // TS Error: 類型 { title: string; } 上不存在屬性 year
```
- 通過接口指定類型。要顯式地指定一個 `reactive` 變量的類型,我們可以使用接口:
```ts
import { reactive } from 'vue'
interface Book {
title: string
year?: number
}
const book: Book = reactive({ title: 'Vue 3 指引' })
book.year = 2022 // 成功!
```
其他 API 與 `TypeScript` 結合使用的方法和上面大同小異,這裏我就不一一列舉了。具體可以參考這篇文章:[如何為 Vue3 組件標註 TS 類型,看這個就夠了!](http://juejin.cn/post/7129130323148800031)。
## 小結
以上就是我在 Vue3 項目中遇到最多的場景,如果你掌握了 Vue3 常用的 API 和今天這些場景,相信參與 Vue3 項目的開發是沒有問題了。當然如果要用好 Vue3 ,可能還需要對 `Pinia` 、 `Vite` 等相關生態有一個深入的瞭解。後面我也會持續分享 Vue3 的使用技巧及相關生態,希望你儘早掌握 Vue3!
有問題歡迎在評論區留言,如果覺得今天的分享對你有所幫助,記得點贊支持一下!😊
參考文檔:[Vue3 官網](http://cn.vuejs.org/guide/introduction.html)