乐趣区

关于typescript:vue3-ts-初体验

背景

自从去年尤大大公布 vue3.0 之后,曾经过来了大半年。前不久高产的尤大大又公布了 vite2.0,让我等搬码仔终日惶恐不安,因而就想对 vue3 进行摸索学习,想理解相较于 vue2.0 哪些地方做了扭转,以及新增了哪些个性。我先大体浏览了一下 vue2.0 迁徙指南,而后就从创立一个最简略的 todo mvc 开始了摸索学习之旅。

创立我的项目

我这里间接用的迁徙文档里提供的 vite 脚手架创立了我的项目,当初越来越多的开发者进行了 typescript 迁徙,因而我就抉择了基于 typescript 来进行开发。

定义路由

编写路由

home 页用于显示 todo 列表,点击列表项进入 detail 页显示 todo 详情。新建 router 目录如下:

router
├─index.ts

在 vue-router4.0 中,提供了两种创立 history 的形式,别离是 createWebHistorycreateWebHashHistory。因为采纳了 typescript 编码,在定义 routes 常量时,须要申明类型为 Array<RouteRecordRaw>

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,保障异步加载始终依照预期工作。

利用路由

最初须要在 main.ts 里利用定义好的路由,main.ts 文件内容如下:

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等不同的选项来组织代码逻辑。然而当组件变得很大时,逻辑关注点 的列表就会增长,这将导致逻辑碎片化,比方当咱们关注某个单个逻辑点时,会频繁地跳转不同的代码选项块,以至于难以浏览和了解代码逻辑。

因而在 vue3.0 当中,呈现了组合式 API。它的核心理念就是,将与同一个逻辑关注点的代码组合在一起,为了可能这样应用,就得提供一个应用的中央,因而呈现了 setup 函数。

setup 函数在创立组件之前执行,并且能够承受一个 props 参数和一个 context 参数。一旦 props 被解析,就作为组合式 API 的入口点。

因为在执行 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 的初体验。

退出移动版