自从去年尤大大发布 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 的初体验。