手拉手帶你開啓Vue3世界的鬼斧神工

前言

2020註定是不平凡的一年,不管是疫情的橫空出世,仍是世界局勢的激烈動盪,抑或是股市的跌宕起伏,我相信不少國人都會有一種慶幸我是中國人的深入感悟。做爲一名技術宅,除了疫情,更多的仍是雨女無瓜,隨着Vue3.0 Beta的推出,你們都爭先恐後的嚐了鮮,筆者也不例外,通過一番研究,以爲Vue3也的確存在它獨特的魅力,也應該寫一篇博客方便尚未體驗的朋友可以快速對其有一個大致的認知。javascript

開始

此文默認你已熟悉Vue2.x相關知識vue

環境搭建

相關庫版本java

  • Vue-Cli 4.x
  • Vue 3.0.0-beta.1
  • Vue-Router 4.0.0-alpha.7

具體步驟以下:react

  1. 使用VueCli建立一個Vue基礎項目:vue create project
  2. 在項目中,執行升級命令:vue add vue-next

項目目錄結構以下:vue-router

進行完以上操做無誤以後基本環境就已經搭建完畢。npm

配置路由

  • 項目中執行npm install vue-router@4.0.0-alpha.7 -S

按照通常規範,在src目錄下新建router文件夾,並在router文件夾中新建index.js文件。api

index.js內容:app

import { createRouter, createWebHashHistory } from 'vue-router';
import Home from '../components/home'

const routes = [
    {path: '/', redirect: '/home'},
    {path: '/home', component: Home}
]

export default createRouter({
    history: createWebHashHistory(),
    routes
})

複製代碼

基本的路由配置沒有太大改變,大多數狀況下你只須要關注routes中路由規則編寫。接下來,咱們須要在main.js中接入router框架

main.js:dom

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

const app = createApp(App);

app.use(router);
app.mount('#app');

複製代碼

不一樣於咱們以前採用new Vue()建立實例的方式,Vue3在這裏進行了改變;不只如此,咱們不難發現,安裝路由的方式也由以前的Vue.use(Router)變成如上方式,同理對於Vuex的接入也是大同小異,筆者這裏就不過多贅述了。

App.js

<template>
  <div id="app"> <router-view></router-view> </div> </template>
複製代碼

基礎語法初探

setup

setup功能是新的組件選項,它充當在組件內部使用Composition API(新特性)的入口點;建立組件實例時,在初始道具解析後當即調用。在生命週期方面,它在beforeCreate掛接以前被調用。

通常來講,按照咱們以前常規的寫法,咱們在對須要使用變量、計算屬性的時候,咱們會習慣性的寫上:

home/index.vue

<template>
    <div class='home'>
        <div>{{count}}</div>
        <div>{{foo}}</div>
    </div>
</template>

<script>

import { ref } from 'vue'

export default {
    name: 'home',
    data() {
        return {
            count: 0
        }
    },
    computed: {
        foo() {
            return this.count + 1;
        }
    }
};
複製代碼

二者是須要被分類到各自的對象中,在同等功能實現上來講,Vue3的實現以下:

<template>
    <div class='home'>
        <div>{{count}}</div>
        <div>{{foo}}</div>
    </div>
</template>

<script>

import { ref, computed } from 'vue'

export default {
    name: 'home',
    setup(props, context) {
        const count = ref(0)
        const foo = computed(() => count.value + 1)
        return {
            count,
            foo
        }
    }
};
</script>

複製代碼

看到這種語法你是否是懵了,不用慌,這部分着重介紹setup這個入口函數,具體內部語法能夠先不用在乎,後面都會進行對應性一一講解。

setup接收兩個重要參數:

  • props:這個天然不用多提了,等同於vue2props,在這個地方咱們須要注意的地方是,咱們不能對這個參數進行解構,若是使用解構會使他失去響應性。例以下面代碼就會讓props傳過來的值失去響應性:
export default {
  props: {
    name: String
  },
  setup({ name }) {
    watchEffect(() => {
      console.log(`name is: ` + name) // 失去響應性!
    })
  }
}

複製代碼
  • context:其實這個參數咱們也是比較熟悉的,它提供了一個上下文對象,該對象公開了先前this在2.x API中公開的屬性的選擇性列表,它僅包含三個屬性(attrsslotsemit),舉個栗子:
setup(props, context) {
    context.attrs // 2.x:this.attrs
    context.slots // 2.x:this.slots
    context.emit // 2.x:this.emit
}

複製代碼

看完代碼,咱們基本能夠理解爲setup函數就是咱們整個組件各項邏輯關係以及操做的入口了,在Vue3中,咱們使用不一樣api的方式都是採用導入的形式,這就至關於咱們有了更大的操做空間,有了更大的自由性。

雖說Vue3向下兼容Vue2,可是這裏其實咱們須要注意的是,咱們應該儘可能避免2.x和setup函數的混用,這將會致使一些問題。

reactive

取得一個對象並返回原始對象的反應式代理。這等效於2.x的Vue.observable()

對這個api用法,筆者用代碼講解會比較好理解:

<template>
    <div class='home'>
        <div>{{name}}</div>
    </div>
</template>

<script>

import { reactive } from 'vue'

export default {
    name: 'home',
    setup() {
        const obj = reactive({name: '流星啊'})
        obj.name = 'bilibili'; // 修改屬性值
        return obj;
    }
};
</script>
複製代碼

相信你們已經看出來端倪,沒錯,這個api就是單純的把一個對象變得可響應。

ref

接受一個內部值並返回一個反應性且可變的ref對象。ref對象具備.value指向內部值的單個屬性。

在這裏我估計有小夥伴就要問了,這不就是2.x裏的ref嗎,不不不,在3.x它跟那個種在標籤上的ref沒有半點關係,也和$refs沒有任何關係,對於新的相似於2.x的獲取dom的方式請看模板引用部分。

一樣,舉個栗子:

<template>
    <div class='home'>
        <div>{{count}}</div>
    </div>
</template>

<script>

import { ref } from 'vue'

export default {
    name: 'home',
    setup() {
        const count = ref(0);
        count.value++; // 有疑問的往下看筆者介紹
        console.log(count.value);
        return {
            count
        };
    }
};
</script>
複製代碼

這裏也有一個注意點,你若是想要修改使用ref構造出來的變量,只能對xxx.value進行修改,同理你想要在js中訪問它的值必須使用xxx.value,直接對count進行賦值如count++,這種寫法會報錯。

在這裏估計又有小夥伴要問了,那爲啥在template模板中使用{{count}}訪問的時候不用加.value,這裏其實你在使用插值表達式的時候,它內部會自動展開,因此咱們直接用就好了。

若是說咱們把reactiveref結合起來用會有怎樣的奇淫巧計呢😱,繼續舉個栗子:

const count = ref(0)
const state = reactive({
  count
})

console.log(state.count) // 0

state.count = 1
console.log(count.value) // 1

複製代碼

當ref被訪問或做爲反應對象的屬性進行更改時,它會自動展開爲內部值,所以其行爲相似於普通屬性。

computed

這個api也相似2.x,可使用gettersetter,話很少說,上代碼:

const count = ref(1)
const plusOne = computed(() => count.value + 1)

console.log(plusOne.value) // 2

plusOne.value++ // 無效
複製代碼

在這裏咱們不難發現,它的訪問方式也等同於ref,也是須要採用xxx.value,與此同時呢,若是你想要修改一個計算屬性的值,你必須爲他設置setter,並對相應的依賴進行修改。廢話少說,看碼:

const count = ref(1)
const plusOne = computed({
  get: () => count.value + 1,
  set: val => {
    count.value = val - 1
  }
})

plusOne.value = 1
console.log(count.value) // 0

複製代碼

readonly

意如其名,顧名思義,就是構造一個只能訪問的屬性,這個玩意它針對的很強,也就是不管你這個對象嵌套有多深,被他包裝後的對象必定是隻能讀,其實就是至關於一個代理:

const re = reactive({count: 0})
const readonlyRe = readonly(re);
readonlyRe.count++; // 無效,而且會給出警告
複製代碼

watchEffect

對於這個屬性呢,你能夠拿它跟2.x的watch對象相比較了,沒錯他就是來監聽的。

const count = ref(0)

watchEffect(() => console.log(count.value))
// -> 打印 0

setTimeout(() => {
  count.value++
  // -> 打印 1
}, 100)

複製代碼

總而言之,他會收集傳入函數的依賴,一旦依賴發生發生改變,他就會從新調用你傳進來的函數,用過react hooks的童靴可能會說,這玩意怎麼這麼像useEffect,其實Vue3也的確借鑑了react一些不錯的設計,因此,你們也不要以爲抄襲不抄襲的,框架終究是爲用戶服務,好的設計天然應該值得借鑑,就像react也有借鑑Vue的一些優勢對自身進行優化。

接下來咱們繼續刨析這個api。調用這個api的同時,它會返回一個用於暫停句柄的函數,咱們能夠顯式調用它用於的中止當前監聽,而且對於傳入給watchEffect的回調函數,這個api在觸發調用的時候會傳入一個用於註冊無效回調的函數onInvalidate。具體例子以下:

const stop = watchEffect(onInvalidate => {
  const token = performAsyncOperation(id.value); // 執行一個異步操做
  onInvalidate(() => {
    // 依賴的id發生變化,可是異步操做還未完成,咱們就能夠在這裏中止你的異步操做。
    token.cancel(); // 這裏咱們假設你的異步操做返回了一個包含取消操做的方法。
  })
})

stop(); // 咱們可使用這個方法去中止它

複製代碼

若是咱們註冊了無效回調方法,那麼在這個依賴已經變化可是異步請求還未完成的時候,它內部就會幫咱們調用咱們註冊的無效回調。

生命週期函數

這裏給一個與2.x的對比:

  • beforeCreate (vue3棄用) -> 使用 setup()
  • created (vue3棄用) -> 使用 setup()
  • beforeMount -> onBeforeMount
  • mounted -> onMounted
  • beforeUpdate -> onBeforeUpdate
  • updated -> onUpdated
  • beforeDestroy -> onBeforeUnmount
  • destroyed -> onUnmounted
  • errorCaptured -> onErrorCaptured

使用示例:

import { onMounted, onUpdated, onUnmounted } from 'vue'

const MyComponent = {
  setup() {
    onMounted(() => {
      console.log('mounted!')
    })
    onUpdated(() => {
      console.log('updated!')
    })
    onUnmounted(() => {
      console.log('unmounted!')
    })
  }
}
複製代碼

模板引用

在看完前面ref這個api的介紹以後,很對小夥伴確定也會疑惑,那我要獲取dom怎麼辦,這個Vue3也有的,放寬心,聽筆者繼續娓娓道來。

<template>
    <div class='home'>
        <div ref="dom"></div>
    </div>
</template>

<script>

import { ref, onMounted } from 'vue'

export default {
    name: 'home',
    setup() {
        const dom = ref(null)
        onMounted(() => {
            console.log(dom.value);
        })
        return {
            dom
        }
    }
};
</script>

複製代碼

從代碼中咱們能夠發現,如今這種訪問dom的方式和以前區別在於,咱們須要顯示設定一個響應性變量,而後再在模板中使用以前咱們耳熟能詳的方式ref='xxx'來進行設定。

結尾

聽完筆者的敘述,你有沒有對Vue3有一種躍躍欲試的想法呢😜。

若有發現筆者表述不當的地方,請在評論區戳筆者哦,感激涕零😝。

相關文章
相關標籤/搜索