Vue3 新特性

語言: CN / TW / HK

Vue3 新特性

image - 首先是向下相容,Vue3 支援大多數 Vue2 的特性。甚至就拿 Vue2 的語法開發 Vue3,也是沒有任何問題的。

  • 效能的提升,每個人都希望使用的框架更快,更輕。Vue3 做到了,給開發者一個極致的體驗。官方網站給出的資料是:打包大小減少 41%,初次渲染快 55%,更新快 133%,記憶體使用減少 54%。

  • 新推出的Composition API ,在 Vue2 中遇到的問題就是複雜元件的程式碼變的非常麻煩,甚至不可維護。說白了就是封裝不好,重用不暢。這個Composition API一推出,立馬解決了這個問題。它是一系列 API 的合集。

  • 其他新特性:Teleport(瞬移元件)、Suspense(解決非同步載入元件問題)和全域性 API 的修改和優化。

  • 更好TypeScript支援,Vue3 的原始碼就是使用TypeScript進行開發的。所以在新的版本上使用TS也更加順暢無阻。

一、composition-api

Vue在2.x中編寫程式碼要按照一定的模板,比如資料就只能放在data()中,方法只能放在methods中,按照模板編寫程式碼對於新手來講可能是好事,但是一旦專案變大,維護起來就顯得很困難。

下圖的左邊圖示,即Vue2使用的Options-api,圖中相同的顏色對應是元件的一種功能,可以看到為了實現一種功能,Options-api所寫的程式碼是非常分散的。

image

如果元件邏輯複雜,程式碼量多,我們新增新程式碼不光要不停的上下滑動,而且在後期程式碼維護中,閱讀起來也變得十分的困難,因為實現一種功能的程式碼並沒有集中在一起。另外就是作為一個新接手的開發人員,在茫茫的 method、data、computed 等選項中一目瞭然的發現這個變數是屬於哪個功能是比較困難的 。

而在Composition-api中,我們可以把實現一種功能的程式碼寫在一起,甚至還可以把它們單獨抽取在一個js檔案或者一個函式中。在js檔案中也可以引用Composition-api的生命週期函式。這將極大的提高程式碼的可維護性。這樣就可以更好的提取和重用多個元件之間的邏輯。

優劣比較:

  • 在邏輯組織和邏輯複用方面,Composition API是優於Options API,因為Composition API幾乎是函式,會有更好的型別推斷。

  • Composition API對 tree-shaking 友好,程式碼也更容易壓縮。

  • Composition API中見不到this的使用,減少了this指向不明的情況。

  • 如果是小型元件,可以繼續使用Options API,也是十分友好的。

1、setup

setup 函式是一個新的元件選項。作為在元件內使用 Composition API 的入口點。 新的 setup 選項在元件被建立之前執行,一旦 props 被解析完成,它就將被作為組合式 API 的入口。

setup函式的引數

我們先來研究一個setup函式的引數,它主要有兩個引數:

  • 第一個引數:props
  • 第二個引數:context

props非常好理解,它其實就是父元件傳遞過來的屬性會被放到props物件中,我們在setup中如果需要使用,那麼就可以直接通過props引數獲取:

  • 對於定義props的型別,我們還是和之前的規則是一樣的,在props選項中定義;
  • 並且在template中依然是可以正常去使用props中的屬性,比如message;
  • 如果我們在setup函式中想要使用props,那麼不可以通過 this 去獲取(後面我會講到為什麼);
  • 因為props有直接作為引數傳遞到setup函式中,所以我們可以直接通過引數來使用即可;

另外一個引數是context,我們也稱之為是一個SetupContext,它裡面包含三個屬性:

  • attrs:所有的非prop的attribute;
  • slots:父元件傳遞過來的插槽(這個在以渲染函式返回時會有作用);
  • emit:當我們元件內部需要發出事件時會用到emit(因為我們不能訪問this,所以不可以通過 this.$emit發出事件);

setup函式的返回值

setup既然是一個函式,那麼它也可以有返回值,它的返回值用來做什麼呢?

  • setup的返回值可以在模板template中被使用;
  • 也就是說我們可以通過setup的返回值來替代data選項;

甚至是我們可以返回一個執行函式來代替在methods中定義的方法:

```js

```

setup不可以使用this

官方關於this有這樣一段描述:

  • 表達的含義是this並沒有指向當前元件例項;

  • 並且在setup被呼叫之前,data、computed、methods等都沒有被解析;

  • 所以無法在setup中獲取this;

image

2、Ref 和 Reactive

在我看來 ref 和 reactice 都是用來建立響應式物件的。 - reactive 接受的引數是一個物件或陣列型別。如果是陣列型別會轉換成proxy物件。 - ref 一般建立一個基本型別變數,有一個 .value屬性,可以通過其對值進行讀取或修改。 - reactive 在組合函式返回時記得新增上 ’...toRefs(state)‘以保持響應性,對物件解構或展開後會失去響應性,所以需要使用roRefs()把reactive型別轉為ref型別。 - ref 可以用於子元件的ref屬性使用。(後面會提到) ```js

``` 以下是兩種關於Ref ,Reactive的風格建議

  • 就像你在普通 JavaScript 中區別宣告基礎型別變數與物件變數時一樣區別使用 ref 和 reactive。我們推薦你在此風格下結合 IDE 使用型別系統。
  • 所有的地方都用 reactive,然後記得在組合函式返回響應式物件時使用 toRefs。這降低了一些關於 ref 的心智負擔,但並不意味著你不需要熟悉這個概念。

3、computed 、watch、watchEffect

```js

```

watchEffect

  • watch是:既要指明監視的屬性,也要指明監視的回撥。
  • watchEffect是:不用指明監視哪個屬性,監視的回撥中用到哪個屬性,那就監視哪個屬性。

這個函式的功能和計算屬性差不多,但是 - computed注重的計算出來的值(回撥函式的返回值),所以必須要寫返回值。 - watchEffect更注重的是過程(回撥函式的函式體),所以不用寫返回值。

```js

``` - itemRefs 不必是陣列:它也可以是一個物件,其 ref 會通過迭代的 key 被設定。 - 如果需要,itemRef 也可以是響應式的且可以被監聽。

6、props emit ref

直接上程式碼吧 ~

```html

```

7、provide 和 inject

provide 和inject 是vue提供的一對API 這對API 可以實現元件之間的通訊 無論層級有多深 都可以通過這對API 來實現

provide

在 setup() 中使用 provide 時,我們首先從 vue 顯式匯入 provide 方法。這使我們能夠呼叫 provide 來定義每個 property。 provide 函式允許你通過兩個引數定義 property: - name ( 型別) - value

```js

```

inject

在 setup() 中使用 inject 時,也需要從 vue 顯式匯入。匯入以後,我們就可以呼叫它來定義暴露給我們的元件方式。 inject 函式有兩個引數: - 要 inject 的 property 的 name - 預設值 (可選)

```js

```

響應式

  • 新增響應性: 為了增加 provide 值和 inject 值之間的響應性,我們可以在 provide 值時使用 ref 或 reactive。

```js

``` 現在,如果這兩個 property 中有任何更改,子元件中的值也將自動更新!

  • 修改響應式 property: 當使用響應式 provide / inject 值時,官方中建議儘可能將對響應式 property 的所有修改限制在定義 provide 的元件內部。

```js // 父元件 export default { setup() { const location = ref('North Pole') const geolocation = reactive({ longitude: 90, latitude: 135 })

// 修改provide中值的方法
const updateLocation = () => {
  location.value = 'South Pole'
}

provide('location', location)
provide('geolocation', geolocation)
provide('updateLocation', updateLocation)

} }

// 子元件 export default { setup() { const userLocation = inject('location', 'The Universe') const userGeolocation = inject('geolocation') // 子元件呼叫父元件中修改值的方法 const updateUserLocation = inject('updateLocation')

return {
  userLocation,
  userGeolocation,
  updateUserLocation
}

} } ```

如果要確保通過 provide 傳遞的資料不會被 inject 的元件更改,官方建議對提供者的 property 使用 readonly。

```js import { provide, reactive, readonly, ref } from 'vue' export default { setup() { const location = ref('North Pole') const geolocation = reactive({ longitude: 90, latitude: 135 })

const updateLocation = () => {
  location.value = 'South Pole'
}

provide('location', readonly(location))
provide('geolocation', readonly(geolocation))
provide('updateLocation', updateLocation)

} }

```

8、

```

9、style 特性

scoped

```

TIP

通過 v-html 建立的 DOM 內容不會被作用域樣式影響,但你仍然可以使用深度選擇器來設定其樣式。

module

<style module> 標籤會被編譯為 CSS Modules 並且將生成的 CSS 類作為 $style 物件的鍵暴露給元件:

```js

``` 自定義注入名稱:

```js

``` 與組合式 API 一同使用:        注入的類可以通過 useCssModule API 在 setup() 和

```

這個語法同樣也適用於

```

單檔案元件狀態驅動的 CSS 變數 (

```

10、getCurrentInstance

getCurrentInstance()是Vue3.x中的核心方法,用於訪問例項上下文的router及vuex等。 1. 概述:一個很重要的方法,獲取當前元件的例項、上下文來操作router和vuex等。 2. 引入:由vue提供,按需引入:import { getCurrentInstance} from 'vue'; 3. 使用:獲取當前元件的上下文,推薦使用:const { proxy } = getCurrentInstance()。

```js import { getCurrentInstance } from 'vue'; // 獲取當前元件例項 const instance = getCurrentInstance();

// 獲取當前元件的上下文,下面兩種方式都能獲取到元件的上下文。 const { ctx } = getCurrentInstance(); // 方式一,這種方式只能在開發環境下使用,生產環境下的ctx將訪問不到 const { proxy } = getCurrentInstance(); // 方式二,此方法在開發環境以及生產環境下都能放到元件上下文物件(推薦) // ctx 中包含了元件中由ref和reactive建立的響應式資料物件,以及以下物件及方法; proxy.$attrs proxy.$data proxy.$el proxy.$emit proxy.$forceUpdate proxy.$nextTick proxy.$options proxy.$parent proxy.$props proxy.$refs proxy.$root proxy.$slots proxy.$watch ```

二、vue3周邊生態

1、vue-router

  • vue2.x中,可以通過this.$router或者this.$route來獲取或者操作路由。

  • 在vue3.0中,引入了Composition-api。在setup函式中無法使用this獲取元件例項。新版本的vue-router也提供了支援Composition-api的hooks,例如useRouter,useRoute函式。

```js import {onMounted} from 'vue' import {useRoute, useRouter} from "vue-router";

export default { setup() { const router = useRouter() const route = useRoute()

onMounted(() => {
  const {id = ''} = route.params
})

function pushWithQuery(query) {
  router.push('/index')
}

}, } ```

另外,vue-router還提供了支援的Composition-api的兩個路由守衛:update and leave - beforeRouteLeave:離開當前頁面路由時觸發,return false則阻止跳轉,next中不能寫引數 - beforeRouteUpdate:動態路由 只有引數發生變化是才執行(通俗理解及跳轉頁面時)

```js import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'

export default { setup() { onBeforeRouteLeave((to, from) => { const answer = window.confirm( 'Do you really want to leave? you have unsaved changes!' ) if (!answer) return false })

const userData = ref()

onBeforeRouteUpdate(async (to, from) => {
  if (to.params.id !== from.params.id) {
    userData.value = await fetchUser(to.params.id)
  }
})

}, } ```

2、vuex

```js import { useStore } from 'vuex'

export default { setup() { const store = useStore()

onMounted(() => {
  const data = store.state.someData
})

const handleClick = () => {
    store.commit('CHANGE_DATA', { a: 1})
}

}, }

```

三、其他

1、Teleport

Teleport 在國內大部分都翻譯成了瞬間移動元件或任意傳送門,也有把這個函式叫獨立元件。 是一種能夠將我們的模板移動到 DOMVue app 之外的其他位置的技術。

場景:像 modals,toast 等這樣的元素,很多情況下,我們將它完全的和我們的 Vue 應用的 DOM 完全剝離,管理起來反而會方便容易很多。

原因在於如果我們巢狀在 Vue 的某個元件內部,那麼處理巢狀元件的定位、z-index 和樣式就會變得很困難。

另外,像 modals,toast 等這樣的元素需要使用到 Vue 元件的狀態(data 或者 props)的值。

這就是 Teleport 派上用場的地方。我們可以在元件的邏輯位置寫模板程式碼,這意味著我們可以使用元件的 data 或 props。然後在 Vue 應用的範圍之外渲染它。

使用

Teleport方法,可以把Dialog元件渲染到你任意想渲染的外部Dom上,不必巢狀再#app裡了,這樣就不會互相干擾了。你可以把Teleport看成一個傳送門,把你的元件傳送到你需要的地方。 teleport元件和其它元件沒有任何其它的差異,用起來都是一樣的。

  • 首先我們在 index.html 中新增我們需要傳送到的位置。

```html

``` - 將編寫的元件包裝在 teleport 元件中,還需要指定一個 to 屬性,為該屬性分配一個查詢選擇器,以標識目標元素。

```html

```

2、Suspense

等待非同步元件時渲染一些額外內容,讓應用有更好的使用者體驗。

試驗性

Suspense 是一個試驗性的新特性,其 API 可能隨時會發生變動。特此宣告,以便社群能夠為當前的實現提供反饋。

生產環境請勿使用。

以上是官方的警告!


使用: - 首先我們先寫一個非同步元件 注意點:如果你要使用Suspense的話,要返回一個promise物件,而不是原來的那種JSON物件。

```js

```

  • 使用suspense元件

```js

export default { components: { AsyncShow: defineAsyncComponent(() => import('./AsyncShow.vue')) } }

```

  • 處理非同步請求錯誤

在非同步請求中必須要作的一件事情,就是要捕獲錯誤,因為我們沒辦法後端給我們返回的結果,也有可能服務不通,所以一定要進行捕獲異常和進行處理。

在vue3.x的版本中,可以使用onErrorCaptured這個鉤子函式來捕獲異常。在使用這個鉤子函式前,需要先進行引入.

```js

import { ref, defineAsyncComponent, onErrorCaptured} from "vue";

export default { components: { AsyncShow: defineAsyncComponent(() => import('./AsyncShow.vue')) }, setup() { const error = ref(null); onErrorCaptured(e => { error.value = e; return false; }); return { error }; } } ``` 有了onErrorCaptured就可以直接在setup()函式中直接使用了。鉤子函式要求我們返回一個布林值,代表錯誤是否向上傳遞。這裡的false一方面表示:錯誤不會冒泡給父元件;另一方面表示vue將停止該錯誤的傳播。

3、片段(Fragment)

在 Vue2.x 中, template中只允許有一個根節點: <template> <div> <span></span> <span></span> </div> </template> 但是在 Vue3.x 中,你可以直接寫多個根節點: <template> <span></span> <span></span> </template>

最後的最後

最後的最後給大家推薦幾個vue3相關網址 - 第一個vue3的官網:https://staging-cn.vuejs.org/ - 一個集合了vue3很多相關技術棧的網址:https://vue3js.cn/ - 一個前端技術棧博主J技術胖:https://www.jspang.com/article/64#toc0