寫給 vue2.0 開發者的 vue3.0 教程

Vue 3尚未正式發佈,可是維護者已經發布了beta版本,以供咱們的用戶嘗試並提供反饋
html

若是您想知道Vue 3的主要特性和主要變化,我將在本文中經過使用Vue 3 beta 9建立一個簡單的應用程序來強調它們vue

我將介紹儘量多的新內容,包括片斷、傳送、複合API和其餘一些模糊的更改。我也會盡我所能來解釋這個特性或變動的基本原理webpack

如何構建

咱們將構建一個帶有模態窗口功能的簡單應用程序。我選擇這個是由於它方便地容許我展現一些Vue 3的更改。git

下面是這款應用在打開和關閉狀態下的樣子,這樣你就能夠在腦海中想象出咱們正在作的事情:github

圖片

Vue3.0的安裝與啓動

與其直接安裝Vue 3,不如克隆Vue -next- Webpack -preview項目,它將爲咱們提供包括Vue 3在內的最小的Webpack設置。web

$ git clone https://github.com/vuejs/vue-next-webpack-preview.git vue3-experiment
$ cd vue3-experiment
$ npm i

一旦克隆並安裝了NPM模塊,咱們所須要作的就是刪除樣板文件並建立一個新的main.js文件,這樣咱們就能夠從頭建立Vue 3應用程序了。npm

$ rm -rf src/*
$ touch src/main.js

如今咱們將運行開發服務器:數組

建立一個新的 vue3.0 app

立刻,咱們啓動一個新的Vue應用程序的方式改變了。咱們如今須要導入新的createApp方法,而不是使用新的Vue()瀏覽器

而後咱們調用這個方法,傳遞咱們的Vue實例定義對象,並將返回對象分配給一個變量app服務器

接下來,咱們將在app上調用mount方法,並傳遞一個CSS選擇器來指示咱們的mount元素,就像咱們在Vue 2中使用$mount實例方法同樣

import { createApp } from "vue";

const app = createApp({
 // root instance definition
});

app.mount("#app");

改變的緣由

在舊的API中,咱們添加的任何全局配置(插件、混合、原型屬性等)都會永久地改變全局狀態。例如:

// Affects both instances
Vue.mixin({ ... })

const app1 = new Vue({ el: '#app-1' })
const app2 = new Vue({ el: '#app-2' })

這在單元測試中確實是一個問題,由於它使確保每一個測試與上一個測試隔離變得很棘手。

在新的API下,調用createApp將返回一個新的app實例,該實例不會被應用於其餘實例的任何全局配置所污染。

Learn more:Global API change RFC.

添加狀態屬性

咱們的模式窗口能夠處於兩種狀態之一——打開或關閉。讓咱們用一個布爾狀態屬性modalOpen來管理它,咱們會給它一個初始值false

const app = createApp({
 data: {
   modalOpen: false
 }
});

這是不容許的。相反,必須爲數據分配一個返回狀態對象的工廠函數。

這是您必須爲Vue組件作的事情,可是如今它也對Vue應用程序實例強制執行

const app = createApp({
 data: () => ({
   modalOpen: false
 })
});

Reason for change

使用對象而不是工廠函數的優勢是,首先,它在語法上更簡單,其次,你能夠在多個根實例之間共享頂層狀態,例如:

const state = {
 sharedVal: 0
};

const app1 = new Vue({ state });
const app2 = new Vue({ state });

// Affects both instances
app1._data.sharedVal = 1;

這種用例不多,可使用。由於有兩種類型的聲明是不適合初學者的,因此決定刪除這個特性。

Learn more:Data object declaration removed RFC

在繼續以前,讓咱們添加一個方法來切換modalOpen值。這與Vue 2沒有什麼不一樣。

const app = createApp({
 data: () => ({
   modalOpen: true  
 }),
 methods: {
   toggleModalState() {
     this.modalOpen = !this.modalOpen;
   }
 }
});

使用根組件

若是您如今轉到瀏覽器並檢查控制檯,您將看到警告「組件缺乏呈現函數」,由於咱們尚未爲根實例定義模板。

Vue 2的最佳實踐是爲根實例建立一個最小的模板,並建立一個應用程序組件,其中將聲明主應用程序標記。

咱們在這裏也作一下。

touch src/App.vue

如今咱們能夠得到根實例來呈現該組件。不一樣之處在於,在Vue 2中,咱們一般會使用渲染函數來完成如下操做:

import App from "./App.vue";

const app = createApp({
 ...
 render: h => h(App)
});

app.mount("#app");

咱們仍然能夠這樣作,可是Vue 3有一個更簡單的方法——讓應用程序成爲一個根組件。爲此,咱們能夠刪除根實例定義並傳遞App組件。

import App from "./App.vue";

const app = createApp(App);

app.mount("#app");

這意味着App組件不只由根實例呈現,並且是根實例。

在此過程當中,讓咱們經過刪除app變量來簡化一下語法:

createApp(App).mount("#app");

如今移動到根組件,讓咱們從新添加狀態和方法到這個組件:

<script>
export default {
 data: () => ({
   modalOpen: true  
 }),
 methods: {
   toggleModalState() {
     this.modalOpen = !this.modalOpen;
   }
 }
};
</script>

讓咱們也爲模態特性建立一個新組件:

touch src/Modal.vue

如今,咱們將提供一個包含內容插槽的最小模板。這確保了咱們的模式是可重用的。稍後咱們將向該組件添加更多內容。

<template>
 <div class="modal">
   <slot></slot>
 </div>
</template>

Multi-root模板

如今讓咱們爲根組件建立模板。咱們將建立一個按鈕來打開模態,它將觸發toggleModalState方法

咱們還將使用剛剛建立的模態組件,它將根據modalState的值呈現。咱們還能夠在內容槽中插入一段文本。

<template>
 <button @click="toggleModalState">Open modal</button>
 <modal v-if="modalOpen">
   <p>Hello, I'm a modal window.</p>
 </modal>
</template>
<script>
import Modal from "./Modal.vue";
export default {
 components: {
   Modal
 },
 ...
}
</script>

注意到這個模板有什麼奇怪的地方嗎?看一遍。我將等待。

沒錯,有兩個根元素。在Vue 3中,因爲一個稱爲fragment的特性,它再也不強制擁有單個根元素!

使用複合API重構

Vue 3的旗艦特性是複合API。這個新的API容許您使用setup函數定義組件功能,而不是使用添加到組件定義對象的屬性。

如今,讓咱們重構應用程序組件,以使用複合API。

在我解釋代碼以前,要清楚咱們所作的一切都是重構——組件的功能是相同的。還要注意,模板沒有改變,由於複合API隻影響咱們定義組件功能的方式,而不是咱們呈現它的方式。

<template>
 <button @click="toggleModalState">Open modal</button>
 <modal v-if="modalOpen">
   <p>Hello, I'm a modal window.</p>
 </modal>
</template>
<script>
import Modal from "./Modal.vue";
import { ref } from "vue";
export default {
 setup () {
   const modalState = ref(false);
   const toggleModalState = () => {
     modalState.value = !modalState.value;
   };
   return {
     modalState,
     toggleModalState
   }
 }
};
</script>

setup method

首先,請注意咱們導入了ref函數,該函數容許咱們定義一個反應變量modalState。這個變量等價於This . modalstate。

toggleModalState方法只是一個普通的JavaScript函數。可是,請注意,要更改方法體中的modalState的值,咱們須要更改它的子屬性值。這是由於使用ref建立的反應變量被包裝在一個對象中。這對於保持它們在傳遞過程當中的活性是必要的。

若是您想詳細瞭解refs的工做方式,最好查閱Vue Composition API文檔。

最後,咱們從setup方法返回modalState和toggleModalState,由於它們是在模板呈現時傳遞給模板的值。

Reason for change

請記住,組合API不是一個更改,由於它徹底是可選的。主要動機是考慮更好的代碼組織和組件之間的代碼重用(由於mixin本質上是一種反模式)

若是您認爲在本例中重構應用程序組件以使用複合API是沒必要要的,那麼您是正確的。可是,若是這是一個更大的組件,或者咱們須要與其餘組件共享它的特性,那麼您就會看到它的有用性。

提供更深刻的示例超出了本文的範圍,因此若是您有興趣瞭解更多關於新API的使用,請參閱個人另外一篇文章,瞭解什麼時候使用新Vue複合API(以及什麼時候不使用)。


傳送內容

若是您之前建立過模態特性,您就會知道它一般被放置在關閉的標記以前。

<body>
 <div>
   <!--main page content here-->
 </div>
 <!--modal here-->
</body>

這樣作是由於情態動詞一般有一個頁面覆蓋的背景(若是你不明白個人意思,請參閱開頭的圖片)。要使用CSS實現這一點,您不須要處理父元素定位和z-index疊加上下文,所以最簡單的解決方案是將模態放在DOM的最底部。

這就與Vue產生了問題。不過,它假設UI將被構建爲一個組件樹。爲了容許樹的片斷移動到DOM中的其餘位置,Vue 3中添加了一個新的傳送組件

要使用傳送,讓咱們首先向頁面添加一個元素,咱們但願將模態內容移動到該頁面。咱們將轉到index.html,並在Vue的掛載元素旁邊放置一個帶ID modal-wrapper的div。

<body>
 ...
 <div id="app"></div><!--Vue mounting element-->
 <div id="modal-wrapper">
   <!--modal should get moved here-->
 </div>
</body>

如今,回到App.vue,咱們將把模態內容包裝在傳送組件中。咱們還須要指定一個to屬性,它將被分配一個用於標識目標元素的查詢選擇器,在本例中是#modal-wrapper。

<template>
 <button @click="toggleModalState">Open modal</button>
 <teleport to="#modal-wrapper">
   <modal v-if="modalOpen">
     <p>Hello, I'm a modal window.</p>
   </modal>
 </teleport>
</template>

就是這樣。傳送中的任何內容都將在目標元素中呈現。然而,它仍然會像它在層級中的最初位置同樣工做(關於道具,事件等)。

所以,在您保存代碼以後,從新加載頁面,在開發工具中檢查DOM,您會感到驚訝!

Learn more:Teleport RFC

發出一個事件

如今讓咱們在模態中添加一個按鈕來關閉它。爲此,咱們將向modal tempate添加一個按鈕元素,並使用一個發出事件close的click處理程序。

<template>
 <div class="modal">
   <slot></slot>
   <button @click="$emit('close')">Dismiss</button>
 </div>
</template>

而後父組件將捕捉此事件,並切換modalState的值,使其在邏輯上爲假,並致使窗口關閉。

<template>
 ...
   <modal
     v-if="modalOpen"
     @click="toggleModalState"
   >
     <p>Hello, I'm a modal window.</p>
   </modal>
 </teleport>
</template>

到目前爲止,這個特性與Vue 2中的特性徹底相同。可是,在Vue 3中,如今建議您使用新的component選項顯式地聲明組件的事件。就像使用道具同樣,您能夠簡單地建立一個字符串數組來命名組件將發出的每一個事件

<template>...</template>
<script>
export default {
 emits: [ "close" ]
}
</script>

Reason for change

想象一下,打開別人編寫的組件文件,並查看顯式聲明的組件的道具和事件。立刻,您就會理解這個組件的接口,即它要發送和接收什麼。

除了提供自我記錄的代碼以外,您還可使用事件聲明來驗證事件負載,儘管在本例中我找不到這樣作的理由。

Learn more:Emits Option RFC

樣式槽內容

爲了使咱們的模式可重用,咱們爲內容提供了一個插槽。讓咱們經過向組件添加樣式標籤來開始對該內容進行樣式化。

在咱們的組件中使用限定範圍的CSS是一個很好的實踐,以確保咱們提供的規則不會對頁面中的其餘內容產生意外的影響

讓咱們把任何段落文本放到槽裏都改爲斜體。爲此,咱們將使用p選擇器建立一個新的CSS規則。

<template>...</template>
<script>...</script>
<style scoped>
 p {
   font-style: italic;
 }
</style>

若是你試一下,你會發現它不起做用。問題是,當槽內容仍然屬於父內容時,在編譯時肯定了做用域樣式。

Vue 3提供的解決方案是提供一個僞選擇器::v- sloated(),容許您使用提供插槽的組件中的做用域規則來鎖定插槽內容。

Here's how we use it:

<style scoped>
 ::v-slotted(p) {
   font-style: italic;
 }
</style>

Vue 3 also includes some other new scoped styling selectors::v-deepand::v-globalwhich you can learn more about here:Scoped Styles RFC

Other changes

好了,這就是我能夠在一個簡單的例子中介紹的全部新特性。我獲得了大部分主要的,但這裏有一些我認爲重要到足以在結束文章以前提到,你能夠本身研究:

Added:

Removed:

  • Filters

  • Inline templates

  • Event interface for components (no more event bus!)

Changed:

  • Async component API

  • Custom directive API

  • Render function syntax

There are also various changes regarding Vue Router but I'll be dedicating a separate article to those!

相關文章
相關標籤/搜索