vue3 + ts 初體驗

背景

自從去年尤大大發布 vue3.0 以後,已通過去了大半年。前不久高產的尤大大又發佈了 vite2.0,讓我等搬碼仔終日驚慌失措,所以就想對 vue3 進行探索學習,想了解相較於 vue2.0 哪些地方作了改變,以及新增了哪些特性。我先大致閱讀了一下 vue2.0 遷移指南,而後就從建立一個最簡單的 todo mvc 開始了探索學習之旅。html

建立項目

我這裏直接用的遷移文檔裏提供的 vite 腳手架建立了項目,如今愈來愈多的開發者進行了 typescript 遷移,所以我就選擇了基於 typescript 來進行開發。vue

定義路由

編寫路由

home 頁用於顯示 todo 列表,點擊列表項進入 detail 頁顯示 todo 詳情。新建 router 目錄以下:java

router
├─index.ts

在 vue-router4.0 中,提供了兩種建立 history 的方式,分別是 createWebHistorycreateWebHashHistory。因爲採用了 typescript 編碼,在定義 routes 常量時,須要聲明類型爲 Array<RouteRecordRaw>react

import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
import { defineAsyncComponent } from 'vue'

const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    redirect: '/home'
  },
  {
    path: '/home',
    name: 'home',
    component: defineAsyncComponent(() => import('../pages/home.vue'))
  },
  {
    path: '/detail',
    name: 'detail',
    component: defineAsyncComponent({
      loader: () => import('../pages/detail.vue'),
      delay: 200,
      timeout: 3000
    })
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

export default router

在 vue3.0 裏,經過 defineAsyncComponent 來定義異步組件。若是須要對異步組件進行配置,能夠傳遞一個對象進去,在 vue2.0 中的 component 被重命名爲了 loader,並且須要注意的是,loader 函數自己再也不接受 resolve 和 reject,且必須返回一個 Promise,保證異步加載始終按照預期工做。vue-router

應用路由

最後須要在 main.ts 裏應用定義好的路由,main.ts 文件內容以下:typescript

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

const app = createApp(App)

app.use(router)

app.mount('#app')

同時也能夠看到,在 vue3.0 中,是經過 createApp 函數來建立根實例,而在 vue2.0 中是經過 new Vue() 來建立根實例的。數組

關於 setup

在 vue2.0 中,在編寫組件代碼時是基於選項 API 來進行的,經過 datawatchcomputedmethods等不一樣的選項來組織代碼邏輯。然而當組件變得很大時,邏輯關注點的列表就會增加,這將致使邏輯碎片化,好比當咱們關注某個單個邏輯點時,會頻繁地跳轉不一樣的代碼選項塊,以致於難以閱讀和理解代碼邏輯。mvc

所以在 vue3.0 當中,出現了組合式 API。它的核心理念就是,將與同一個邏輯關注點的代碼組合在一塊兒,爲了可以這樣使用,就得提供一個使用的地方,所以出現了 setup 函數。app

setup 函數在建立組件以前執行,而且能夠接受一個 props 參數和一個 context 參數。一旦 props 被解析,就做爲組合式 API 的入口點。dom

因爲在執行 setup 時,組件實例還沒有被建立,所以沒法使用 this,所以只能經過 props 訪問組件中聲明的屬性。

由於 setup 函數是圍繞 beforeCreate 和 created 鉤子函數運行的,因此在 vue3.0 裏這兩個函數就不須要了,在這兩個函數裏編寫的任何代碼都應該直接放在 setup 函數裏。

建立組件

在 src 目錄下新建 pages 文件夾,並在其下新建 home.vuedetail.vue

pages
├─home.vue
├─detail.vue

編寫 home 組件

對於 todomvc 來講,一個 todo 項至少包含兩個屬性 —— 待辦事項和狀態,所以定義接口 Todo:

interface Todo {
  text: string,
  done: boolean
}

接着就能夠定義 todolist 類型了,這是一個複雜數據類型。在 vue3.0 裏,若是要定義基本數據類型做爲響應式,那麼須要使用 ref;若是要定義複雜數據類型做爲響應式,就須要使用 reactive,所以這裏使用 reactive 來定義對象數組類型:

import { defineComponent, ref, reactive } from 'vue'

export default defineComponent({
  setup (props, context) {
    let todoList = reactive<Array<Todo>>([
      {
        text: 'learn js',
        done: true
      },
      {
        text: 'learn java',
        done: false
      }
    ])
  }
})

因爲須要動態添加待辦事項,所以須要再定義一個字符串類型的數據,用來接收用戶輸入:

let inputText = ref('')

這裏能夠不用顯示地添加類型約束,由於 typescript 會根據 ref 接收的參數自動推斷出類型爲 string。

一個 todomvc 離不開新增和刪除,所以咱們接着定義 addTodo、removeTodo 兩個方法。

const addTodo = () => {
  // 記住這裏是 `inputText.value`,這是由於基本數據類型被包裝成了一個響應式對象,此時只能經過 value 屬性來使用它的值
  if (inputText.value.trim().length) {
    const item = {
      text: inputText.value,
      done: false
    }
    todoList.push(item)
    inputText.value = ''
  }
}
const removeTodo = (index: number) => {
  todoList.splice(index, 1)
}

最後必不可少的一步就是在 setup 函數裏 return 咱們前面定義好的數據和方法,這是由於模版渲染時會使用到它們:

setup (props, context) {
  // 此處省略...
  return {
    todoList,
    inputText,
    addTodo,
    removeTodo
  }
}

編寫 detail 組件

detail 組件很簡單,只須要顯示路由跳轉時傳遞過來的待辦事項對象便可,包括了待辦事項和狀態。可是在跳轉前,須要在 home.vue 定義路由跳轉的方法。

使用 vue-router4.0

vue-router4.0 提供了新的使用方法,即便用 router 或者 route 時須要咱們本身去 import 提供的 useRouteruseRoute 函數。爲了使用 router 進行路由跳轉,須要手動 import。

// home.vue
import { useRouter, RouteLocationOptions } from 'vue-router'

export default defineComponent({
  setup (props, context) {
    let router = useRouter()
    // ...
    const jumpToDetail = (item: Todo) => {
      router.push({
        name: 'detail',
        params: {
          text: item.text,
          done: item.done
        }
      } as RouteLocationOptions)
    }

    return {
      todoList,
      inputText,
      addTodo,
      removeTodo,
      jumpToDetail,
      router
    }
  }
})
須要注意的是,咱們這裏路由跳轉是經過 push 對象的形式來完成的,所以須要指定這個對象是 RouteLocationOptions 類型,這個類型是 vue-router 爲咱們提供的。若是 push 的是一個字符串,那麼就須要指定這個字符串類型爲 RouteLocationRaw。

獲取路由參數

如上所述,在 vue-router4.0 中,若是要獲取某個路由對象,須要使用 useRoute,所以須要手動 import。

<template>
  <div id="detail">
    <h2>未來要作什麼: {{ todo.text }}</h2>
    <h2>狀態:<input type="checkbox" v-model="checked"/>{{ todo.done ? '已完成' : '未完成' }}</h2>
  </div>
</template>
import { useRoute } from 'vue-router'
import { ref, reactive, watch, defineComponent } from 'vue'

export default defineComponent({
  name: 'detail',
  setup (props, context) {
    const route = useRoute()
    // 使用 params,刷新頁面參數會丟失
    const done = route.params.done === 'true' ? true : false
    let todo = reactive({
      text: route.params.text,
      done
    })
    let checked = ref(todo.done)

    watch(checked, (newV, oldV) => {
      if (newV) {
        todo.done = true
      } else {
        todo.done = false
      }
    }, {
      deep: true
    })

    return {
      todo,
      route,
      checked
    }
  }
})

這裏須要使用到 watch 函數,方便咱們在勾選複選框時,狀態能及時改變。不一樣於 vue2.0 裏的 watch 選項,vue3.0 裏的 watch 是一個純函數,而且能夠屢次使用,它接收的參數包括:監聽目標、回調函數和可選項,第三個參數接收一個對象,裏面包含深度監聽和當即執行選項。

以上即是關於 vue3.0 + ts 的初體驗。

相關文章
相關標籤/搜索