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 的初体验。

相关文章
相关标签/搜索