簡單梳理下 Vue3 的新特性
阿新 • • 發佈:2021-02-24
> 在 Vue3 測試版剛剛釋出的時候,我就學習了下 Composition API,但沒想到正式版時隔一年多才出來,看了一下發現還是增加了不少新特性的,在這裡我就將它們一一梳理一遍。
本文章只詳細闡述 Vue3 中重要或常用的新特性,如果想了解全部的特性請轉:[Vue3 響應性基礎 API](https://vue3js.cn/docs/zh/api/basic-reactivity.html)
## Composition API
這是一個非常重要的改變,我認為 Composition API 最大的用處就是**將響應式資料和相關的業務邏輯結合到一起,便於維護**(這樣做的優點在處理龐大元件的時候顯得尤為重要)。
之所以叫做 Composition API(或組合式 API) 是因為所有的響應式資料和業務邏輯程式碼都可以放在 setup 方法中進行處理,我們通過程式碼看一下 Vue2 的 Options API 和 Composition API 的區別:
```javascript
/* Options API */
export default {
props: {},
data(){},
computed: {},
watch: {},
methods: {},
created(),
components:{}
// ...other options
}
/* Composition API */
export default {
props: {},
setup(),
components:{}
}
```
這就是兩種 API 在大致結構上的不同,雖然 Composition API 提倡使用 `setup` 來暴露元件的 `data`、`computed`、`watch`、生命週期鉤子... 但並不意味著強制使用,在 Vue3 中同樣可以選擇 Options API 或者兩種寫法混用。
接下來我們看看在 setup 的使用。
### setup
#### 執行時機
`setup` 在 `beforeCreate` 之前執行,因此訪問不到元件例項,換句話說 **setup 內無法使用 this 訪問元件例項**。
#### 引數
`setup` 方法接受兩個引數 `setup(props, context)` ,`props` 是父元件傳給元件的資料,`context`(上下文) 中包含了一些常用屬性:
##### attrs
`attrs` 表示由上級傳向該元件,但並不包含在 `props` 內的屬性:
```html
```
```javascript
/* child.vue */
export default {
props: { name: String },
setup(props, context) {
console.log(props) // {name: 'child'}
console.log(context.attrs) // {msg: 'hello world'}
},
}
```
##### emit
用於在子元件內觸發父元件的方法
```html
```
```javascript
/* child.vue */
export default {
setup(_, context) {
context.emit('sayWhat')
},
}
```
##### slots
用來訪問被插槽分發的內容,相當於 `vm.$slots`
```html
header
content
footer
```
```javascript
/* child.vue */
import { h } from 'vue'
export default {
setup(_, context) {
const { header, content, footer } = context.slots
return () => h('div', [h('header', header()), h('div', content()), h('footer', footer())])
},
}
```
## 生命週期
Vue3 的生命週期除了可以使用傳統的 Options API 形式外,也可以在 `setup` 中進行定義,只不過要在前面加上 `on`:
```javascript
export default {
setup() {
onBeforeMount(() => {
console.log('例項建立完成,即將掛載')
})
onMounted(() => {
console.log('例項掛載完成')
})
onBeforeUpdate(() => {
console.log('元件dom即將更新')
})
onUpdated(() => {
console.log('元件dom已經更新完畢')
})
// 對應vue2 beforeDestroy
onBeforeUnmount(() => {
console.log('例項即將解除掛載')
})
// 對應vue2 destroyed
onUnmounted(() => {
console.log('例項已經解除掛載')
})
onErrorCaptured(() => {
console.log('捕獲到一個子孫元件的錯誤')
})
onActivated(() => {
console.log('被keep-alive快取的元件啟用')
})
onDeactivated(() => {
console.log('被keep-alive快取的元件停用')
})
// 兩個新鉤子,可以精確地追蹤到一個元件發生重渲染的觸發時機和完成時機及其原因
onRenderTracked(() => {
console.log('跟蹤虛擬dom重新渲染時')
})
onRenderTriggered(() => {
console.log('當虛擬dom被觸發重新渲染時')
})
},
}
```
Vue3 沒有提供單獨的 `onBeforeCreate` 和 `onCreated` 方法,因為 `setup` 本身是在這兩個生命週期之前執行的,Vue3 建議我們**直接在** `setup` **中編寫這兩個生命週期中的程式碼**。
## Reactive API
### ref
`ref` 方法用來為一個指定的值(可以是任意型別)建立一個響應式的資料物件,該物件包含一個 `value` 屬性,值為響應式資料本身。
對於 `ref` 定義的響應式資料,無論獲取其值還是做運算,都要用 `value` 屬性。
```javascript
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
const obj = ref({ a: 2 })
console.log(obj.value.a) // 2
return {
count,
obj,
}
},
}
```
但是在 `template` 中訪問 `ref` 響應式資料,是不需要追加 `.value` 的:
```html
...
...
```
在 Vue3 中,允許多個根元素的存在:
```html
...
...
```
這不僅簡化了巢狀,而且暴露出去的多個元素可以受父元件樣式的影響,一定程度上也減少了 css 程式碼。
早在以前,React 就允許 Fragments 元件,該元件用來返回多個元素,而不用在其上面新增一個額外的父節點:
```javascript
render() {
return (
);
}
```
如果想在 Vue2 實現 Fragments,需要安裝 Vue-fragment 包,如今 Vue3 整合了 Vue-fragment,我們可以直接使用這個功能了。
## Teleport
`Teleport` 用來解決邏輯屬於該元件,但從技術角度(如 css 樣式)上看卻應該屬於 app 外部的其他位置。
一個簡單的栗子讓你理解:
```html
```
```html
```
`teleport` 接受一個 `to` 屬性,值為一個 css 選擇器,可以是 id,可以是標籤名(如 body)等等。
`teleport` 內的元素將會插入到 to 所指向的目標父元素中進行顯示,而內部的邏輯是和當前元件相關聯的,除去邏輯外,上面的程式碼相當於這樣:
```html
```
![GIF.gif](https://i.loli.net/2021/02/23/bWBj13Aney7pdI5.gif)
這樣在一開始會顯示 1 秒的 Loading...,然後才會顯示 `AsyncComponent`,因此在做載入動畫的時候可以用 `Suspense` 來處理。
## 其他新特性
更多比較細小的新特性官網說的很詳細,請看:[其他新特性](https://vue3js.cn/docs/zh/guide/migration/array-refs.h
- count: {{count}}
- obj.a: {{obj.a}}
- obj1: {{obj1}}
- attrA: {{attrA}}
- obj2: {{obj2}}
- attrB: {{attrB}}
Here are some messages
Here are some messages
``` 因此 `teleport` 中的元素樣式,是會受到目標父元素樣式的影響的,這在建立全屏元件的時候非常好用,全屏元件需要寫 css 做定位,很容易受到父元素定位的影響,因此將其插入到 app 外部顯示是非常好的解決方法。 ## Suspense `Suspense` 提供兩個 `template`,當要載入的元件不滿足狀態時,顯示 `default template`,滿足條件時才會開始渲染 `fallback template`。 ```html {{msg}} ``` ```html