做者:Filip Rakowskihtml
翻譯:瘋狂的技術宅前端
原文:vueschool.io/articles/vu…vue
未經容許嚴禁轉載node
在上一篇文章中,咱們瞭解了 Vue 3 將帶來的性能改進。咱們已經知道,用新的 Vue 3 編寫的程序效果會很好,但性能並非最重要的部分。對開發人員而言,最重要的是新版本將會怎樣影響咱們編寫代碼的方式。react
如你所料,Vue 3 帶來了許多使人興奮的新功能。值得慶幸的是,Vue 團隊主要介紹了對當前 API 的添加和改進,而不是重大更改,因此已經瞭解 Vue 2 的人們應該很快就會對新語法感到滿意。webpack
Let's start with the API that most of you probably heard about... 讓咱們從大多數人可能據說過的API開始...git
組件 API 是 Vue 的下一個主要版本中最經常使用的討論和特點語法。這是一種全新的邏輯重用和代碼組織方法。github
當前,咱們使用所謂的 Options API 構建組件。爲了向 Vue 組件添加邏輯,咱們填充(可選)屬性,例如 data
、methods
、computed
等。這種方法的最大缺點是其自己並非有效的 JavaScript 代碼。你須要確切地知道模板中能夠訪問哪些屬性以及 this
關鍵字的行爲。在後臺,Vue 編譯器須要將此屬性轉換爲工做代碼。所以咱們沒法從自動建議或類型檢查中受益。web
組件 API 旨在經過將組件屬性中當前可用的機制公開爲 JavaScript 函數來解決這個問題。 Vue 核心團隊將組件 API 描述爲 「一組基於函數的附加 API,能夠靈活地組合組件邏輯。」 用組件 API 編寫的代碼更具備可讀性,而且其背後沒有任何魔力,所以更易於閱讀和學習。json
讓咱們經過一個用了新的組件 API 的組件的簡單示例,來了解其工做原理。
<template>
<button @click="increment">
Count is: {{ count }}, double is {{ double }}, click to increment.
</button>
</template>
<script>
import { ref, computed, onMounted } from 'vue'
export default {
setup() {
const count = ref(0)
const double = computed(() => count.value * 2)
function increment() {
count.value++
}
onMounted(() => console.log('component mounted!'))
return {
count,
double,
increment
}
}
}
</script>
複製代碼
如今,讓咱們把代碼分解爲幾部分,來了解發生了些什麼:
import { ref, computed, onMounted } from 'vue'
複製代碼
正如我以前提到的,組件 API 將組件屬性公開爲函數,所以第一步是導入所需的函數。在例子中,須要使用 ref
建立響應性引用,用 computed
創建計算屬性,並用 onMounted
訪問安裝的生命週期 hook。
如今你可能很想知道這神祕的 setup
方法究竟是什麼?
export default {
setup() {
複製代碼
簡而言之,它只是一個將屬性和函數返回到模板的函數而已。咱們在這裏聲明全部響應性屬性、計算屬性、觀察者和生命週期 hook,而後將它們返回,以即可以在模板中使用它們。
咱們不從 setup
函數返回的內容在模板中將會不可用。
const count = ref(0)
複製代碼
根據上面的內容,咱們聲明瞭帶有 ref
函數的名爲 count
的響應屬性。它能夠包裝任何原語或對象並返回其響應性引用。傳遞的元素的值將會保留在所建立引用的 value
屬性中。例如,若是你想訪問 count
引用的值,則須要明確要求 count.value
。
const double = computed(() => count.value * 2)
function increment() {
count.value++
}
複製代碼
這正是咱們在聲明計算屬性 double
和 increment
函數時所要作的。
onMounted(() => console.log('component mounted!'))
複製代碼
使用 onMounted
hook,咱們會在安裝組件時記錄一些消息,只是向你展現能夠作到!😉
return {
count,
double,
increment
}
複製代碼
最後,咱們將使用 increment
方法返回 count
和 double
屬性,以使它們在模板中可用。
<template>
<button @click="increment">
Count is: {{ count }}, double is {{ double }}. Click to increment.
</button>
</template>
複製代碼
瞧!如今咱們能夠訪問模板中 setup
方法返回的屬性和函數,就像經過舊的 Options API 聲明它們同樣。
這是一個簡單的例子,也能夠經過 Options API 輕鬆實現。新的組件 API 的真正好處不只在於能以不一樣的方式進行編碼,在對咱們的代碼和邏輯進行重用時,這些好處也能顯示出來。
新的組件 API 具備更多優勢。考慮一下代碼重用。目前若是咱們要在其餘組件之間共享一些代碼,則有兩個可用的選擇:mixins 和做用域插槽( scoped slots)。可是二者都有缺點。
假設咱們要提取 counter
中的功能並在其餘組件中重用。在下面,你能夠看到如何將其與可用的 API 和新的組件 API 結合使用:
讓咱們從 mixins 開始:
import CounterMixin from './mixins/counter'
export default {
mixins: [CounterMixin]
}
複製代碼
mixins 的最大缺點在於咱們對它實際上添加到組件中的行爲一無所知。這不只使代碼變得難以理解,並且還可能致使名稱與現有屬性和函數發生衝突。
下面是做用域插槽:
<template>
<Counter v-slot="{ count, increment }">
{{ count }}
<button @click="increment">Increment</button>
</Counter>
</template>
複製代碼
經過使用做用域插槽,咱們確切地知道能夠經過 v-slot
屬性訪問了哪些屬性,所以代碼更容易理解。這種方法的缺點是咱們只能在模板中訪問它,而且只能在 Counter
組件做用域內使用。
如今該用組件 API 了:
function useCounter() {
const count = ref(0)
function increment () { count.value++ }
return {
count,
incrememt
}
}
export default {
setup () {
const { count, increment } = useCounter()
return {
count,
increment
}
}
}
複製代碼
是否是更優雅?咱們不受模板和組件做用域的限制,而且可以確切地知道能夠從 counter 訪問哪些屬性。另外咱們能夠受益於編輯器中可用的代碼補全功能,由於 useCounter
只是一個返回某些屬性的函數,所以編輯器能夠幫助咱們進行類型檢查和建議。
這也是使用第三方庫的更優雅的方式。例如,若是咱們想使用 Vuex,則能夠顯式地使用 useStore
函數,而不是污染 Vue 原型(this.$store
)。這種方法也消除了 Vue 插件的幕後魔力。
const { commit, dispatch } = useStore()
複製代碼
若是你想了解有關組件 API 及其使用案例的更多信息,我強烈建議你閱讀 Vue 團隊的這篇文章,其中解釋了新 API 背後的緣由,並提出了最好的用例建議。還有 great repository ,其中包含來自 Vue 核心團隊的 Thorsten Lünborg 使用的組件 API 的例子。
咱們能夠在實例化和配置程序的方式中找到另外一個重大變化。讓咱們看看它如今是如何工做的:
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')
複製代碼
當前,咱們正在用全局 Vue
對象提供全部配置並建立新的 Vue 實例。對 Vue
對象所作的任何更改都會影響每一個 Vue 實例和組件。
如今,讓咱們看看它如何在 Vue 3 中運行:
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')
複製代碼
你可能已經注意到,每一個配置都限於使用 createApp
定義的某個 Vue 程序。
它可使你的代碼更易於理解,而且不易出現由第三方插件引起的意外問題。目前,若是某些第三方解決方案正在修改 Vue 對象,那麼它可能會以意想不到的方式(尤爲是全局混合)影響你的程序,而 Vue 3 則沒有這個問題。
目前,此 API 的更改正在 這個 RFC 中進行討論,這意味着未來可能會有所更改。
咱們能夠在 Vue 3 中期待的另外一個激動人心的附加功能是片斷。
你可能會問什麼片斷?好吧,若是你建立了一個 Vue 組件,那麼它只能有一個根節點。
這意味着沒法建立這樣的組件:
<template>
<div>Hello</div>
<div>World</div>
</template>
複製代碼
緣由是表明任何 Vue 組件的 Vue 實例都須要綁定到單個 DOM 元素中。建立具備多個 DOM 節點的組件的惟一方法是建立一個沒有基礎 Vue 實例的功能組件。
事實證實,React 社區也有一樣的問題。他們提出的解決方案是一個名爲 Fragment 的虛擬元素。看上去是這樣的;
class Columns extends React.Component {
render() {
return (
<React.Fragment> <td>Hello</td> <td>World</td> </React.Fragment> ); } } 複製代碼
儘管 Fragment 看起來像是普通的 DOM 元素,但它是虛擬的,根本不會在 DOM 樹中渲染。這樣咱們就能夠將組件功能綁定到單個元素中,而無需建立冗餘的 DOM 節點。
如今你能夠在帶有 vue-fragments 庫的 Vue 2 中使用片斷,而在 Vue 3 中你能夠直接使用它!
將被用在 Vue 3 中的另外一個從 React 學來的功能是 Suspense 組件。
Suspense 可以暫停你的組件渲染,並渲染後備組件,直到條件知足爲止。在 Vue London 期間,尤雨溪簡短地談到了這個主題,並向咱們展現了能夠指望的 API。事實證實,Suspense 只是帶有插槽的組件:
<Suspense>
<template >
<Suspended-component />
</template>
<template #fallback>
Loading...
</template>
</Suspense>
複製代碼
直到 Suspended-component
徹底渲染前將會顯示後備內容。掛起能夠等待,直到該組件被下載(若是該組件是異步組件的話),或者在 setup
函數中執行一些異步操做。
V-model 是一種指令,可用於在給定組件上實現雙向綁定。咱們能夠傳遞響應性屬性,並從組件內部對其進行修改。
咱們能夠從表單元素上很好的瞭解 v-model
:
<input v-bind="property /> 複製代碼
可是你知道能夠對每一個組件都使用 v-model
嗎?在內部, v-model
只是傳遞 value
屬性和偵聽 input
事件的捷徑。把上面的例子重寫爲如下語法,將具備徹底相同的效果:
<input v-bind:value="property" v-on:input="property = $event.target.value" />
複製代碼
咱們甚至能夠用組件 model
屬性來更改默認屬性和事件的名稱:
model: {
prop: 'checked',
event: 'change'
}
複製代碼
如你所見,若是咱們想要在組件中進行雙向綁定,v-model
指令多是一個很是有用的語法。不幸的是,每一個組件只能有一個 v-model
。
幸運的是,這在 Vue 3 中不會有問題!你將可以給 v-model
屬性名,並根據須要擁有儘量多的屬性名。在下面的例子中,你能夠在表單組件中找到兩個 v-model
:
<InviteeForm v-model:name="inviteeName" v-model:email="inviteeEmail" />
複製代碼
目前,此 API 的更改已在這個 RFC 中進行討論,這意味着未來可能會有更改。
Portals 是特殊的組件,用來在當前組件以外渲染某些內容。它也是在 React 中實現的功能之一。這就是 React 文檔關於 Portals 的內容:
「Portals 提供了一種獨特的方法來將子級渲染到父組件的 DOM 層次結構以外的 DOM 節點中。」
這種處理模式,是彈出式窗口以及一般顯示在頁面頂部的組件所使用的一種很是好的方法。經過使用 Portals,你能夠確保沒有任何主機組件 CSS 規則會影響你要顯示的組件,而且能夠避免用 z-index
進行的黑客攻擊。
對於每一個 Portal,咱們須要爲其指定目標位置,在該目標位置將渲染 Portals 內容。在下面,你能夠從 portal-vue 庫中看到實現,該庫將此功能添加到了 Vue 2:
<portal to="destination">
<p>This slot content will be rendered wherever thportal-target with name 'destination'
is located.</p>
</portal>
<portal-target name="destination">
<!-- This component can be located anywhere in your App. The slot content of the above portal component wilbe rendered here. -->
</portal-target>
複製代碼
Vue 3 對 Portals 提供開箱即用的支持!
自定義指令 API 在 Vue 3 中將略有變化,以便更好地與組件生命週期保持一致。這項改進應使 API 更加直觀,從而使新手更容易理解和學習 API。
這是當前的自定義指令 API:
const MyDirective = {
bind(el, binding, vnode, prevVnode) {},
inserted() {},
update() {},
componentUpdated() {},
unbind() {}
}
複製代碼
這是在 Vue 3 中的樣子。
const MyDirective = {
beforeMount(el, binding, vnode, prevVnode) {},
mounted() {},
beforeUpdate() {},
updated() {},
beforeUnmount() {}, // new
unmounted() {}
}
複製代碼
即便這是一項重大改進,也應很容易被 Vue 兼容版本涵蓋到。
在此 RFC 中討論了這個 API 的更改,這意味着未來可能會改進。
除了 Composition API(它是 Vue 3 中最大的主要新 API)以外,咱們還能夠找到不少較小的改進。能夠看到 Vue 正在朝着更友好的開發體驗和更簡單、更直觀的 API 邁進。十分高興看到 Vue 團隊決定在框架的核心採用了許多目前只能經過第三方庫得到的想法。
上面的列表僅表示主要的 API 更改和改進。若是你對其餘的內容感到好奇,請務必檢查 Vue RFCs 信息庫。