1. 程式人生 > 實用技巧 >題解 P7108 【移花接木】

題解 P7108 【移花接木】

vue3.0 上手體驗

vue3.0beta 版本已經發布有一陣子了,是時候上手體驗一波了~

注意,本文所有演示都是基於 vue3.0 beta 版本,不保證後續正式版 api 不改動。等官方文件出來後,以官網為準。

環境搭建

直接使用腳手架,如果本地沒有安裝的可以執行腳手架安裝命令:

npm install -g @vue/cli

如果本地安裝過的,可以嘗試更新一下:

npm update -g @vue/cli

測試 vue-cli 版本:

vue -V
@vue/cli 4.4.1

接下來建立一個vue專案:

vue create vue3-demo

在出現的命令互動視窗選擇 Manually select features :

Vue CLI v4.4.1
? Please pick a preset:
  常用配置 (router, vuex, sass, babel, eslint)
  sass (router, vuex, sass, babel, eslint)
  test (less, babel, eslint)
  default (babel, eslint)
❯ Manually select features

隨後勾選以下選項,一般開發商業專案都需要這些:

Vue CLI v4.4.1
? Please pick a preset: Manually select features
? Check the features needed for your project:
 ◉ Babel
 ◯ TypeScript
 ◯ Progressive Web App (PWA) Support
 ◉ Router
 ◉ Vuex
❯◉ CSS Pre-processors
 ◉ Linter / Formatter
 ◯ Unit Testing
 ◯ E2E Testing

回車後根據自己的習慣選擇好,就開始建立專案。注意這時候還是 vue2 的專案環境,接下來就是升級為 vue3 的執行環境。

升級為 vue3.0 專案

vue-cli 還沒有直接支援 vue3.0,需要依賴外掛升級,輸入指令:

cd vue3-demo
vue add vue-next

執行完上述命令後,會自動安裝vue-cli-plugin-vue-next 外掛,它會將專案升級為 vue3.0 的依賴環境,包括 vue-router 和 vuex 都會升級為 4.x 的版本。

最新版 vue-cli 可以直接建立 vue3 專案了(2020.09更新)

今天將 vue-cli 更新到了 4.5.4 版本,發現可以直接建立 vue3 專案了。使用 vue create vue3-demo 命令建立一個測試專案,會出現以下選項:

可以直接選擇預設的 vue3 配置,也可以選擇最後一項來手動配置,這裡演示手動配置,選擇 Manually select features,回車:

根據需要選擇,注意第一項表明了可以選擇 vue 版本,選完後回車:

這裡選擇 vue 的版本,直接選擇 3.x 回車即可。這樣就能建立一個基於 vue3 搭建的專案了。

vue3.0 新特性體驗

按照上面步驟升級為 vue3.0 專案後,會自動幫我們將一些檔案改成 vue3.0 的寫法。

升級 vue3 後還是可以使用 vue2 中的寫法,就是說 vue3 保留了 vue2 的開發模式。當然我們學習 vue3 就是為了它的新特性,所以下面所有介紹只關注新特性的改變。

建立vue例項

vue3 建立vue例項不需要使用 new 的方式了,來看 src/main.js 檔案:

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

createApp(App).use(router).use(store).mount('#app')

現在是函式式風格來建立vue例項,還記得 vue2 是怎麼建立例項的嗎,對比下:

// Vue2 建立例項

import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'

new Vue({
  el: '#app',
  router,
  store,
  render: h => h(App)
})

你喜歡哪一種方式?我比較喜歡vue3.0的函式式風格。

路由

Vue2.0中我們路由一般會選擇使用vue-router,在Vue3.0依然可以使用vue-router,不過和Vue3.0一樣當前vue-router的版本也是beta版本,在本文撰寫的時候,版本是4.0.0-beta7

因為當前vue-router針對vue3.0的版本還是beta版本,所以不能直接通過yarn add vue-router進行安裝,而是需要帶上版本號

yarn add [email protected]

看看路由配置檔案:src/router/index.js

import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes
})

export default router

這是升級後路由配置方法,可以看到與 vue2 版本有很大的區別。現在建立路由例項需要手動引入 createRouter 方法,建立 history 模式路由也需要手動引入 createWebHistory 方法,這達到 Tree-Shaking 的目的,即不會把所有的 api 都打包進來,只會打包你用到的 api,vue3 將都會使用這種形式。

響應式資料、methods、watch 和 computed

這裡應該是改動最大的部分,也是爭議最大的部分,來看看怎麼回事。

在vue2版本中,我們宣告響應式資料是這樣的:

// Vue2
export default {
  // ....
  data() {
    return {
      state: {
        count: 0
      }
    };
  },
}

在vue3.0中,需要這樣:

// Vue3
import { ref } from 'vue'
export default {
  setup () {
    const count = ref(0) // 宣告 count,初始值為 0
    const str = ref('hello') // 宣告 str,初始值為 'hello'
    return {
      count,
      str
    }
  }
}

我們要先引入 ref 方法,然後使用它來宣告響應式變數。重要的是,這些操作需要在 setup 函式中進行,而且新增 methods,watch 和 computed 都需要在 setup 中進行。這就跟在vue2中有很大的不同,vue2中我們是使用選項的方式來建立 data、methods、watch 和 computed 的。

接下來演示一個計數器的功能,結合 methods、watch 和 computed:

<template>
  <div class="home">
   <p>count: {{count}}</p>
   <p>doubleCount: {{doubleCount}}</p>
   <button @click="add">增加</button>
  </div>
</template>

<script>
import { ref, watch, computed } from 'vue'
export default {
  setup () {
    // 宣告 count 變數,初始值為 0
    const count = ref(0)

    // 定義 add 方法
    const add = () => {
      count.value++
    }

    // 定義 watch,需要手動引入 watch 方法
    watch(
      () => count.value,
      (val, oldVal) => { console.log(`new count: ${val},old count: ${oldVal}`) }
    )

    // 定義computed,同樣需要手動引入 computed 方法
    const doubleCount = computed(() => count.value * 2)

    return {
      count,
      add,
      doubleCount
    }
  }
}
</script>

來看這個例子,首先定義方法是可以直接定義在 setup 函式中的,然後 return 出去即可,不知可否注意到在方法裡訪問 count 時,是使用 .value 方法訪問的,這裡要強調一下,在 setup 中訪問已宣告的響應式變數時,需要使用 .value 方式訪問,包括在 watch 和 computed 中。

接下來是定義 watch,需要手動引入 watch 方法,這也是達到了 Tree-Shaking 的目的,使用到什麼就引入什麼,最後打包也只打包用到的 api,後面的 computed 也同理。

watch方法有兩個引數,都是函式,第一個函式返回要監聽的值,第二個函式是回撥函式,它兩個引數分別表示新值和舊值。

computed 方法返回計算過的值,最後需要 return 出去。用法如上,還是比較好理解的。

你也可以這樣宣告響應式資料(使用 reactive)

前面說到宣告響應式資料,需要使用 ref,其實你也可以使用 reactive 來一次宣告多個變數,下面例子:

<template>
  <div class="home">
    <p>str: {{state.str}}</p>
   <p>count: {{state.count}}</p>
   <button @click="add">增加</button>
  </div>
</template>

<script>
import { reactive } from 'vue'
export default {
  setup () {
    // 引入 reactive,同時定義多個變數
    const state = reactive({
      count: 0,
      str: 'hello'
    })

    // 現在訪問變數,不能使用 .value 方式訪問了
    const add = () => {
      // state.count.value++ // 錯誤
      state.count++
    }

    return {
      state,
      add
    }
  }
}
</script>

reactive 和 ref

上面說到 ref 和 reactive,這裡再簡單說說。reactive 是接收一個普通物件,返回該物件的響應式代理,它等同於 2.x 中的 Vue.observable()。

const obj = reactive({ count: 0 })

// obj 此時是一個響應式的物件
// 訪問或修改,直接基於 obj.count

ref 也是接收一個引數並返回一個響應式且可改變的 ref 物件,一般引數是基礎型別,比如數值或字串等。如果傳入的引數是一個物件,將會呼叫 reactive 方法進行深層響應轉換。ref 物件擁有一個指向內部值的單一屬性 .value,即當你要訪問它的值時,需要 .value 拿到它的值。但是如果是在 setup 中返回且用到模板中時,在 {{}} 裡不需要加 .value 訪問,在返回時已經自動解套。

<template>
  <div>{{ count }}</div>
</template>

<script>
  export default {
    setup() {
      return {
        count: ref(0), // 這裡返回,在模板中無需 .value 訪問值
      }
    },
  }
</script>

獲取路由資訊

vue3.0 中使用 getCurrentInstance 方法獲取當前元件例項,然後通過 ctx 屬性獲取當前上下文,ctx.$router 是路由例項,而 ctx.$router.currentRoute 就包含當前路由資訊。

<script>
import { getCurrentInstance } from 'vue'
export default {
  setup () {
    const { ctx } = getCurrentInstance()
    console.log(ctx.$router.currentRoute.value)
  }
}
</script>

vuex

vue-router一樣,新的vuex當前也處於beta版本,當前版本是4.0.0-beta.4

安裝vuex

yarn add [email protected]

檢視檔案 src/store/index.js:

import Vuex from 'vuex'

export default Vuex.createStore({
  state: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
})

發現建立 store 例項的方式改變了,vue2 中是使用 new 的方式:

// Vue2 中建立 store 例項
export default new Vuex.Store({
   // ... 
})

一個小例子演示 vue3.0 中使用 store。

建立 store:

import Vuex from 'vuex'

export default Vuex.createStore({
  state: {
    count: 0
  },
  mutations: {
    ADD (state) {
      state.count++
    }
  },
  actions: {
    add ({ commit }) {
      commit('ADD')
    }
  },
  modules: {
  }
})

元件中使用 store:

<template>
  <div class="home">
    <p>{{count}}</p>
    <button @click="add">增加</button>
  </div>
</template>

<script>
import { computed } from 'vue'
import { useStore } from 'vuex'
export default {
  setup () {
    const store = useStore()
    const count = computed(() => store.state.count)

    const add = () => {
      store.dispatch('add')
    }

    return {
      count,
      add
    }
  }
}
</script>

可以看到 vuex 的 api 基本沒變化,只是在元件中使用時需要引入 useStore 方法返回 store 例項,其實你也可以在當前元件上下文中獲取 store,如下:

import {getCurrentInstance} from 'vue'

// ...
const store = getCurrentInstance().ctx.$store