Vue 3尚未正式發佈,但是維護者已經發佈了Beta版本,供我們的參與者嘗試並提供反饋。 如果你想知道Vue 3的主要功能和主要變化是什麼,那麼我將在這篇文章中重點介紹一下,告訴你使用Vue 3 beta 9創建一個簡單的應用程式。 我將介紹儘可能多的新內容,包括fragments,teleport ...
Vue 3尚未正式發佈,但是維護者已經發佈了Beta版本,供我們的參與者嘗試並提供反饋。
如果你想知道Vue 3的主要功能和主要變化是什麼,那麼我將在這篇文章中重點介紹一下,告訴你使用Vue 3 beta 9創建一個簡單的應用程式。
我將介紹儘可能多的新內容,包括fragments,teleport,Composition API以及其他一些晦澀的更改。我將儘力解釋該功能或更改的原理。
Vue3相關文章:
- Vue3 Composition API如何替換Vue Mixins
- Vue3 Composition API中的提取和重用邏輯
- 如何在Vue2與Vue3中構建相同的組件
- Vue3中的Vue Router初探
我們將建立什麼
我們將構建一個帶有模式視窗功能的簡單應用。我之所以選擇它,是因為它可以方便地展示Vue 3的許多變化。
這是該應用在打開和關閉狀態下的外觀,因此你可以在腦海中描繪出我們正在做什麼:
Vue 3安裝和setup
與其直接安裝Vue 3,不如克隆一個項目 vue-next-webpack-preview
,這將為我們提供一個包括Vue 3在內的最小的Webpack設置。
$ git clone https://github.com/vuejs/vue-next-webpack-preview.git vue3-experiment
$ cd vue3-experiment
$ npm i
複製代碼
一旦克隆好了,安裝好了NPM模塊,我們需要做的就是刪除樣板文件,然後創建一個新的 main.js
文件,這樣我們就可以從頭開始創建我們的Vue 3 app了。
$ rm -rf src/*
$ touch src/main.js
複製代碼
現在,我們將運行開發伺服器:
$ npm run dev
複製代碼
創建一個新的Vue 3 app
我們啟動一個新的Vue應用程式的方式改變了,我們現在需要導入新的 createApp
方法,而不是使用新的 Vue()
。
我們調用這個方法,傳遞我們的Vue實例定義對象,並將返回對象分配給一個變數 app
。
接下來,我們將在 app
上調用 mount
方法,並傳遞一個CSS選擇器來指示我們的mount元素,就像在Vue 2中使用 $mount
實例方法一樣。
// src/main.js
import { createApp } from "vue";
const app = createApp({
// 根實例定義
});
app.mount("#app");
變化的原因
與舊的API一樣,我們添加的任何全局配置(plugins,mixins,原型屬性等)都將永久更改全局狀態。例如:
// src/main.js
// 影響兩個實例
Vue.mixin({ ... })
const app1 = new Vue({ el: '#app-1' })
const app2 = new Vue({ el: '#app-2' })
複製代碼
在單元測試中,這確實是一個問題,因為要確保將每個測試都與上一個測試隔離是很棘手的。
在新的API下,調用 createApp
將返回一個新的app實例,該實例不會被應用於其他實例的任何全局配置污染。
瞭解更多:Global API change RFC。
添加state屬性
我們的模態視窗可以處於兩種狀態之一——打開或關閉。讓我們用一個布爾狀態屬性 modalOpen
來管理它,我們將給它一個初始值 false
。
在Vue 2下,我們可以通過在我們的應用實例上創建一個 data
屬性並將一個對象分配給該對象來聲明 modalOpen
屬性,例如:
// src/main.js
const app = createApp({
data: {
modalOpen: false
}
});
複製代碼
不再允許這樣做。相反,必須為數據分配一個返回狀態對象的工廠函數。
// src/main.js
const app = createApp({
data: () => ({
modalOpen: false
})
});
複製代碼
變化的原因
使用對象而不是工廠函數來存儲數據的優點是,首先,它在語法上更簡單;其次,你可以在多個根實例之間共用頂級狀態,例如:
// src/main.js
const state = {
sharedVal: 0
};
const app1 = new Vue({ state });
const app2 = new Vue({ state });
// 影響兩個實例
app1._data.sharedVal = 1;
複製代碼
這種用例很少,可以使用。因為有兩種類型的聲明是不適合初學者的,所以決定刪除這個特性。
在繼續之前,我們還添加一個方法來切換 modalOpen
值。這與Vue 2沒什麼不同。
// src/main.js
const app = createApp({
data: () => ({
modalOpen: true
}),
methods: {
toggleModalState() {
this.modalOpen = !this.modalOpen;
}
}
});
複製代碼
使用一個根組件
如果你現在進入瀏覽器並檢查控制台,則會看到警告“Component is missing render function”,因為我們尚未為根實例定義模板。
Vue 2的最佳實踐是為根實例創建一個最小的模板,並創建一個app組件,其中將聲明主app標記。
讓我們在這裡也這樣做。
$ touch src/App.vue
複製代碼
現在我們可以獲取根實例來渲染該組件。區別在於,對於Vue 2,我們通常會使用render函數來執行此操作:
// src/main.js
import App from "./App.vue";
const app = createApp({
...
render: h => h(App)
});
app.mount("#app");
複製代碼
我們仍然可以做到這一點,但是Vue 3有一個更簡單的方法——使 App
成為根組件。為此,我們可以刪除根實例定義,而是傳遞 App
組件。
// src/main.js
import App from "./App.vue";
const app = createApp(App);
app.mount("#app");
複製代碼
這意味著 App
組件不僅由根實例渲染,而且是根實例。
在此過程中,我們通過刪除 app
變數來簡化語法:
// src/main.js
createApp(App).mount("#app");
複製代碼
現在移至根組件,讓我們向該組件重新添加狀態和方法:
// src/App.vue
<script>
export default {
data: () => ({
modalOpen: true
}),
methods: {
toggleModalState() {
this.modalOpen = !this.modalOpen;
}
}
};
</script>
複製代碼
我們還為模態功能創建一個新組件:
$ touch src/Modal.vue
複製代碼
現在,我們將提供一個最小的模板,其中包括內容插槽。這確保了我們的模態是可重用的。稍後我們將向此組件添加更多內容。
// src/Modal.vue
<template>
<div class="modal">
<slot></slot>
</div>
</template>
複製代碼
多根模板
現在讓我們為我們的根組件創建模板。我們將創建一個按鈕來打開模態,它將觸發 toggleModalState
方法。
我們還將使用我們剛剛創建的modal組件,它將根據 modalState
的值來渲染。讓我們也在槽中插入一段文字作為內容。
// src/App.vue
<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中,由於有了一個叫做片段(fragments)的功能,它不再強制要求有一個單一的根元素!
使用Composition API進行重構
Vue 3的旗艦功能是Composition API。這個新的API允許你使用 setup
功能而不是使用添加到組件定義對象的屬性來定義組件功能。
現在,讓我們重構App組件以使用Composition API。
在解釋代碼之前,請清楚我們所做的只是重構——組件的功能將相同。還要註意,模板沒有更改,因為Composition API僅影響我們定義組件功能的方式,而不影響我們渲染它的方式。
src/App.vue
<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 方法
首先,請註意,我們導入了 ref
函數,該函數允許我們定義響應式變數 modalState
。此變數等效於this.modalState
。
toggleModalState
方法只是一個普通的JavaScript函數。但是,請註意,要更改方法主體中的 modalState
值,我們需要更改其子屬性 value
。 這是因為使用 ref
創建的響應式變數被封裝在一個對象中。這對於保留它們的響應式是非常必要的,因為它們在被傳遞的過程中會被保留下來。
最後,我們從 setup
方法返回 modalState
和 toggleModalState
,因為這些是在呈現模板時傳遞給模板的值。
變化的原因
請記住,Composition API並不是更改,因為它純粹是可選的。主要動機是允許更好的代碼組織和組件之間的代碼重用(因為mixin本質上是一種反模式)。
如果你認為在這個例子中重構App組件以使用Composition API是沒有必要的,那你的想法是正確的。但是,如果這是一個更大的組件,或者我們需要與其他組件共用其功能,那麼你就會發現它的用處。
Teleporting content
如果你曾經創建過模態功能,你會知道它通常被放置在關閉的 </body>
標簽之前。
<body>
<div>
<!--main page content here-->
</div>
<!--modal here-->
</body>
複製代碼
這樣做是因為模式通常具有覆蓋頁面的背景,要使用CSS來實現,您不需要處理父元素定位和z-index堆棧上下文,因此最簡單的解決方案是將模式放在DOM的最底部。
但這在Vue.js中產生了一個問題,它假定UI將作為一個單一的組件樹來構建。為了允許將樹的片段移動到DOM中的其他位置,在Vue 3中添加了一個新的 teleport
組件。
要使用teleport,首先要在頁面上添加一個元素,我們要將模態內容移動到該頁面。我們將轉到 index.html
,並將ID為 modal-wrapper
的 div
放在Vue的安裝元素旁邊。
index.html
<body>
...
<div id="app"></div><!--Vue mounting element-->
<div id="modal-wrapper">
<!--modal should get moved here-->
</div>
</body>
複製代碼
現在,回到 App.vue
,我們將模態內容包裝在 teleport
組件中。我們還需要指定一個 to
屬性,為該屬性分配一個查詢選擇器,以標識目標元素,在本例中為 #modal-wrapper
。
src/App.vue
<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>
複製代碼
就是這樣,teleport
中的任何內容都將渲染在目標元素中。
Emitting 和 event
現在,讓我們在modal中添加一個按鈕,讓它可以被關閉。要做到這一點,我們要在modal 模板中添加一個按鈕元素,並添加一個點擊處理程式,該處理程式會發出一個 close
事件。
src/Modal.vue
<template>
<div class="modal">
<slot></slot>
<button @click="$emit('close')">Dismiss</button>
</div>
</template>
複製代碼
然後,該事件將由父組件捕獲,並將切換 modalState
的值,從邏輯上將其設置為 false
並導致視窗關閉。
src/App.vue
<template>
...
<modal
v-if="modalOpen"
@click="toggleModalState"
>
<p>Hello, I'm a modal window.</p>
</modal>
</teleport>
</template>
複製代碼
到目前為止,此功能與Vue 2中的功能相同。但是,現在在Vue 3中,建議您使用新的 emits
組件選項顯式聲明組件的事件。就像props一樣,你可以簡單地創建一個字元串數組來命名組件將發出的每個事件。
src/Modal.vue
<template>...</template>
<script>
export default {
emits: [ "close" ]
}
</script>
複製代碼
變化的原因
想象一下,打開別人寫的組件的文件,看到它的prop和event明文聲明。馬上,你就會明白這個組件的界面,也就是它要發送和接收什麼。
除了提供自說明代碼外,你還可以使用事件聲明來驗證你的事件有效載荷,雖然我在這個例子中找不到理由來驗證。
瞭解更多:Emits Option RFC
樣式插槽內容
為了使模態可重用,我們提供了一個內容插槽。讓我們開始通過為組件添加 style
標簽來為內容設置樣式。
在我們的組件中使用 scoped
CSS是一種很好的做法,以確保我們提供的規則不會對頁面中的其他內容產生意外影響。
讓我們把任何被放入插槽中的段落文字變成斜體。要做到這一點,我們將使用 p
選擇器創建一個新的CSS規則。
src/Modal.vue
<template>...</template>
<script>...</script>
<style scoped>
p {
font-style: italic;
}
</style>
複製代碼
如果你嘗試一下,你會發現這一點並不奏效。問題是,在編譯時,當插槽內容仍屬於父對象時,Scoped styling是在編譯時確定的。
Vue 3提供的解決方案是提供一個偽選擇器 ::v-slotted()
,允許你在提供插槽的組件中使用範圍化規則來針對插槽內容。
這是我們的用法:
<style scoped>
::v-slotted(p) {
font-style: italic;
}
</style>
複製代碼
Vue 3還包含了其他一些新的Scoped Styling選擇器:::v-deep
和 ::v-global
,你可以在這裡瞭解更多:Scoped Styles RFC。
其他改變
好吧,這就是我可以在一個簡單示例中涵蓋的所有新功能。主要的我基本都有了,但這裡有一些我認為很重要的,在總結文章之前,我覺得足夠重要,可以自己研究一下。
添加的:
移出的:
- Filters
- Inline templates
- Event interface for components(不再有event bus)
更改的:
關於Vue Router也有各種變化,但我將專門用一篇文章來介紹這些變化!
本文的文字及圖片來源於網路加上自己的想法,僅供學習、交流使用,不具有任何商業用途,版權歸原作者所有,如有問題請及時聯繫我們以作處理