VUE 3.0 學習探索入門系列 - Vue3.x 使人期待的新特性(7)

在前面的文章中咱們也聊了許多 Vue3.x 相比 Vue2.x 有哪些變化,也介紹了一些它的特色,今天就重點介紹下它新增的這些特性。javascript

本文主要參考:vueschool.io/articles/vu…html

總覽

  1. Compostion API 合成API
  2. 取消 Vue 全局變量
  3. 自定義指令 Directives API調整
  4. Component 組件支持 v-model 指令
  5. Fragments Template 支持有多個根節點
  6. Suspense Template Fallback 組件
  7. Teleport Template Dom佔位傳遞組件

1 Compostion API 合成API

上一篇文章已經重點介紹過了。vue

查看:juejin.im/post/5e8010…java

2 取消 Vue 全局變量

Vue2.x 中的代碼片斷:node

import Vue from 'vue'
import App from './App.vue'

Vue.config.ignoredElements = [/^app-/]
Vue.use(/* ... */)
Vue.mixin(/* ... */)
Vue.component(/* ... */)
Vue.directive(/* ... */)

new Vue({
  render: h => h(App)
}).$mount('#app')
複製代碼

Vue3.x 中取消了全局變量 Vue,改成實例函數 createApp() 建立實例對象。git

import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)

app.config.ignoredElements = [/^app-/]
app.use(/* ... */)
app.mixin(/* ... */)
app.component(/* ... */)
app.directive(/* ... */)

app.mount('#app')
複製代碼

RFC查看網友討論:github.com/vuejs/rfcs/…github

這個變化很大,將會給咱們從 Vue2.x 升級到 Vue3.x 帶來不小的工做量。編程

爲何這麼改變?其實也好理解,Vue3.x 基於函數式編程,因此:一切皆函數。 爲了保證每一個函數都有本身的小 圈子 能獨立運行,因此從源頭上就開始 開刀api

3 自定義指令 Directives API調整

Vue2.x 中自定義一個指令:app

const MyDirective = {
  bind(el, binding, vnode, prevVnode) {},
  inserted() {},
  update() {},
  componentUpdated() {},
  unbind() {}
}
複製代碼

Vue3.x 中變成了:

const MyDirective = {
  beforeMount(el, binding, vnode, prevVnode) {},
  mounted() {},
  beforeUpdate() {},
  updated() {},
  beforeUnmount() {}, // new
  unmounted() {}
}
複製代碼

這能夠算上是一個 breaking change 了,主要是在 Vue3.x 中生命週期函數的變化致使的。

查看上一篇文章,瞭解 Vue3.x 的生命週期有哪些變化:juejin.im/post/5e8010…

RFC看這裏:github.com/vuejs/rfcs/…

4 Component 組件支持 v-model 指令

Vue2.x 中咱們會把 v-model 用在一些表單元素上,用於數據的雙向綁定。

<input v-model="property /> 複製代碼

可是,若是咱們但願父子組件也能雙向綁定時,Vue2.x 是不建議的,由於這會給父組件的維護帶來災難!

因此在 Vue2.x 中建議使用 this.$emit() 事件回傳機制明確通知父組件,真正的更新仍是父組件本身實現。

後來爲了簡化上述操做,在 Vue2.3.0 新增了 .sync 修飾符。

參考:cn.vuejs.org/v2/guide/co…

好比:父組件調用子組件 text-document 時,子組件就能夠修改父組件的 doc.title

<text-document v-bind:title.sync="doc.title"></text-document>
複製代碼

好了,經過以上描述咱們能夠得出結論:

  • v-model 能夠實現表單元素的數據雙向綁定
  • v-bind:xxx.sync 或者簡寫爲 :xxx.sync 能夠實現父子組件的雙向綁定

那麼在 Vue3.x 中獲得了統一:

:xxx.sync 將被 v-model:xxx 取代

若是你但願跟子組件直接雙向綁定,則:

<text-document v-model="doc"></text-document>
複製代碼

或者多個屬性之間一一綁定:

<text-document v-model:title="doc.title" v-model:content="doc.content" ></text-document>
複製代碼

RFC查看網友討論:github.com/vuejs/rfcs/…

5 Fragments Template 支持有多個根節點

Vue2.x 中 Template 模板你一般是這麼寫的:

<template>
    <div>
        <p>Hello</p>
        <p>Vue2.x</p>
    </div>
</template>
複製代碼

template 中只能有惟一一個根節點。緣由就是每一個 Vue 的實例只容許綁定到惟一的Dom樹上。

若是你但願綁定兩個Dom,那麼你就只能在新建一個 Vue 示例,可是這樣就跟當前系統脫節了,沒啥意義了。

或者使用這個插件:vue-fragments,相似於 React 中的 <React.Fragment>

<template>
  <v-fragment>
    <div>Fragment 1</div>
    <div>Fragment 2</div>
  </v-fragment>
</template>
複製代碼

可是在 Vue3.x 中,就能夠不用惟一根節點,也不用插件了,變得簡單了許多:

<template>
    <p>Hello</p>
    <p>Vue3.x</p>
</template>
複製代碼

6 Suspense Template Fallback 組件

Vue2.x 中你應該會常常遇到這種場景:

<template>
    <div>
        <div v-if="!loading">
            ...
        </div>
        <div v-if="loading">Loading...</div>
    </div>
</template>
複製代碼

或者安裝這個插件:vue-async-manager

而後,就變成了:

<template>
    <div>
        <Suspense>
            <div>
                ...
            </div>
            <div slot="fallback">Loading...</div>
        </Suspense>
    </div>
</template>
複製代碼

Vue3.x 感受就是參考了上面這個組件的作法,如今能夠這麼寫:

<Suspense>
  <template #default>
    ...
  </template>
  <template #fallback>
    Loading...
  </template>
</Suspense>
複製代碼

#fallback 其實在 Vue3.x 中就是 slot 的簡寫。因此,#default 能夠省略。

固然 React 也有 Suspense 組件解決相似的問題。

其實,這個全局組件可能更多的會配合異步組件使用。順便說下,在 Vue3.x 中,定義一個異步組件使用:defineAsyncComponent

7 Teleport Template Dom佔位傳遞組件

注意: teleport3.0.0-alpha.11 剛改的名字,以前叫:portal, 查看 CHANGELOG

Vue2.x 中你應該會常常遇到這種場景:

<!-- UserCard.vue -->
<template>
  <div class="user-card">
    <b> {{ user.name }} </b>  
    <button @click="isPopUpOpen = true">刪除用戶</button>
    
    <!-- 注意這一塊代碼 -->
    <div v-show="isPopUpOpen">
      <p>肯定刪除?</p>
      <button @click="removeUser">肯定</button>
      <button @click="isPopUpOpen = false">取消</button>
    </div>
    
  </div>
</template>
複製代碼

以上代碼就是當咱們須要作一個彈窗的時候,按照業務邏輯,彈窗和其餘代碼在一塊寫着。 可是這麼寫每每會出現問題,就是一旦咱們點擊 「刪除用戶」,本但願彈窗顯示,可是每每這個彈出框被外邊的元素擋住!因爲 z-index 的緣由。

那咱們就千方百計讓這個彈出框直接掛在到 body 節點上,這樣就沒問題了。好比:能夠經過 Javascript 追加這個彈出框到 body 中等,處理起來比較麻煩。

關鍵的問題是:業務邏輯被打斷了,代碼也不連貫了

因此爲了解決這個煩惱,Vue3新增了這個組件,如今你就能夠這麼寫了。

在最外層的 App.vue 中:

<!-- 預留一塊空地,專門用來顯示這個容易被遮擋的層 -->
<div id="modal-container"></div>

<!-- app -->
<div id="app">
複製代碼

在你本身的組件中:

<!-- UserCard.vue -->
<template>
  <div class="user-card">
    <b> {{ user.name }} </b>  
    <button @click="isPopUpOpen = true">刪除用戶</button>
    
    <!-- 注意這一塊代碼 -->
    <Teleport to="#modal-container">
        <div v-show="isPopUpOpen">
          <p>肯定刪除?</p>
          <button @click="removeUser">肯定</button>
          <button @click="isPopUpOpen = false">取消</button>
        </div>
    </Teleport>
    
  </div>
</template>
複製代碼

這樣技能保證你代碼的完整性、業務的連貫性,又能解決彈窗口被遮擋的問題。

另外,今天在 3.0.0-alpha.11 CHANGELOG 中又看到一條更新記錄 16cd8ee

portal: portal should always remove its children when unmounted (16cd8ee)

就是說一旦組件被銷燬 unmounted,Teleport 裏面的元素應該被清空。若是不自動清空掉,隨着你頁面的切換,前一次頁面遺留的彈窗可能一直存在的bug。

最後

本文也是 VUE 3.0 學習探索入門系列 裏面的最後一篇,但願能對你們入門 Vue3 有所幫助。

接下來就等 Vue3.0 正式 Release 之後,再帶給你們 VUE 3.0 實戰上手篇 系列,歡迎你們關注我,及時瞭解動態。

本系列歷時20天完成,再次感謝你們。

(全劇終)

相關文章
相關標籤/搜索