自從去年尤大大發布 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 的方式,分別是 createWebHistory
和 createWebHashHistory
。因爲採用了 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() 來建立根實例的。數組
在 vue2.0 中,在編寫組件代碼時是基於選項 API 來進行的,經過 data
、watch
、computed
、methods
等不一樣的選項來組織代碼邏輯。然而當組件變得很大時,邏輯關注點的列表就會增加,這將致使邏輯碎片化,好比當咱們關注某個單個邏輯點時,會頻繁地跳轉不一樣的代碼選項塊,以致於難以閱讀和理解代碼邏輯。mvc
所以在 vue3.0 當中,出現了組合式 API。它的核心理念就是,將與同一個邏輯關注點的代碼組合在一塊兒,爲了可以這樣使用,就得提供一個使用的地方,所以出現了 setup 函數。app
setup 函數在建立組件以前執行,而且能夠接受一個 props 參數和一個 context 參數。一旦 props 被解析,就做爲組合式 API 的入口點。dom
因爲在執行 setup 時,組件實例還沒有被建立,所以沒法使用 this,所以只能經過 props 訪問組件中聲明的屬性。
由於 setup 函數是圍繞 beforeCreate 和 created 鉤子函數運行的,因此在 vue3.0 裏這兩個函數就不須要了,在這兩個函數裏編寫的任何代碼都應該直接放在 setup 函數裏。
在 src 目錄下新建 pages 文件夾,並在其下新建 home.vue
和 detail.vue
。
pages ├─home.vue ├─detail.vue
對於 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 組件很簡單,只須要顯示路由跳轉時傳遞過來的待辦事項對象便可,包括了待辦事項和狀態。可是在跳轉前,須要在 home.vue
定義路由跳轉的方法。
使用 vue-router4.0
vue-router4.0 提供了新的使用方法,即便用 router 或者 route 時須要咱們本身去 import 提供的 useRouter
和 useRoute
函數。爲了使用 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 的初體驗。