Vue 3教程(適用於Vue 2用戶)

Vue 3還沒有正式發佈,可是維護者已經發布了Beta版本,供咱們的參與者嘗試並提供反饋。javascript

若是你想知道Vue 3的主要功能和主要變化是什麼,那麼我將在這篇文章中重點介紹一下,告訴你使用Vue 3 beta 9建立一個簡單的應用程序。html

我將介紹儘量多的新內容,包括fragments,teleport,Composition API以及其餘一些晦澀的更改。我將盡力解釋該功能或更改的原理。前端


咱們將創建什麼

咱們將構建一個帶有模式窗口功能的簡單應用。我之因此選擇它,是由於它能夠方便地展現Vue 3的許多變化。vue

這是該應用在打開和關閉狀態下的外觀,所以你能夠在腦海中描繪出咱們正在作什麼:java

Vue 3安裝和setup

與其直接安裝Vue 3,不如克隆一個項目 vue-next-webpack-preview,這將爲咱們提供一個包括Vue 3在內的最小的Webpack設置。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了。git

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

如今,咱們將運行開發服務器:github

$ npm run dev

建立一個新的Vue 3 app

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

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

接下來,咱們將在 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;

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

瞭解更多: Data object declaration removed RFC

在繼續以前,咱們還添加一個方法來切換 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 方法返回 modalStatetoggleModalState ,由於這些是在呈現模板時傳遞給模板的值。

變化的緣由

請記住,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-wrapperdiv 放在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

其餘改變

好吧,這就是我能夠在一個簡單示例中涵蓋的全部新功能。主要的我基本都有了,但這裏有一些我認爲很重要的,在總結文章以前,我以爲足夠重要,能夠本身研究一下。

添加的:

移出的:

更改的:

關於Vue Router也有各類變化,但我將專門用一篇文章來介紹這些變化!


本文首發於公衆號《前端外文精選》,關注後私信回覆:大禮包,送某網精品視頻課程網盤資料,準能爲你節省很多錢!
subscribe2.png

相關文章
相關標籤/搜索