Vue 官方团队于 2020 年 9 月 18 日晚 11 点半左右发布了Vue3.0版本 🎉。代号为One Piece。
Vue 3.0 终于发布了,具体更新内容详见 v3.0.0。官网地址 Vue,但内容还都是英文的,毕竟刚发布嘛,中文内容还没那么快。
索性不如自己阅读仓库文档,看看 Vue3 都给我们带来了哪些具体变化。
由于最近 TS 也更新到 4.0,就从它开始看起吧。
TypeScript 支持
Vue CLI 提供内置的 TypeScript 工具支持。
NPM 包中的官方声明
随着应用的增长,静态类型系统可以帮助防止许多潜在的运行时错误,这就是为什么 Vue 3 是用 TypeScript 编写的。这意味着在 Vue 中使用 TypeScript 不需要任何额外的工具——它作为头等公民被支持。
推荐配置
// tsconfig.json
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
// 这样就可以对 `this` 上的 data 属性进行更严格的推断
"strict": true,
"jsx": "preserve",
"moduleResolution": "node"
}
}
请注意,必须包含 strict: true (或至少包含 noImplicitThis: true,它是 strict 标志的一部分) 才能在组件方法中利用 this 进行类型检查,否则它总是被视为 any 类型。
参见 TypeScript 编译选项文档查看更多细节。
开发工具
项目创建
Vue CLI 可以生成使用 TypeScript 的新项目,起步:
# 1. 安装 Vue CLI, 如果尚未安装
npm install --global @vue/cli@next
# 2. 创建一个新项目, 选择 "Manually select features" 选项
vue create my-project-name
# 3. 如果你已经有一个不是 TypeScript 编写的 Vue CLI项目,请添加合适的 Vue CLI插件:
vue add typescript
请确保组件的 script 部分已将语言设置为 TypeScript:
<script lang="ts">
...
</script>
编辑器支持
对于使用 TypeScript 开发 Vue 应用程序,我们强烈建议使用 Visual Studio Code,它为 TypeScript 提供了很好的开箱即用支持。如果你使用的是单文件组件 (SFCs),使用效果很好的 Vetur extension(Vetur 扩展),它在 SFCs 中提供了 TypeScript 检查和许多其他优秀的特性。
WebStorm 也为 TypeScript 和 Vue 提供开箱即用的支持。
定义 Vue 组件
为了让 TypeScript 在 Vue 组件里正确地推断出选项中的类型,你需要使用全局方法 defineComponent 定义组件:
import { defineComponent } from 'vue'
const Component = defineComponent({
// 已启用类型推断
})
配合使用 Options API
TypeScript 应该能够在不显式定义类型的情况下推断大多数类型。例如,如果你的组件里有一个数字类型的属性 count,如果你试图对其调用专属于字符串的方法,则会出现错误:
const Component = defineComponent({
data() {
return {
count: 0
}
},
mounted() {
const result = this.count.split('') // => Property 'split' does not exist on type 'number'
}
})
如果你有一个复杂的类型或接口,你可以使用 类型断言 对其进行指派:
interface Book {
title: string
author: string
year: number
}
const Component = defineComponent({
data() {
return {
book: {
title: 'Vue 3 Guide',
author: 'Vue Team',
year: 2020
} as Book
}
}
})
注释返回类型
由于 Vue 声明文件的循环特性,TypeScript 可能难以推断 computed(计算属性) 的类型。因此,你可能需要对 computed(计算属性)返回的类型做注解。
import { defineComponent } from 'vue'
const Component = defineComponent({
data() {
return {
message: 'Hello!'
}
},
computed: {
// 需要注释
greeting(): string {
return this.message + '!'
}
// 在一个计算属性中,它的 setter 和 getter 都需要被注解
greetingUppercased: {
get(): string {
return this.greeting.toUpperCase();
},
set(newValue: string) {
this.message = newValue.toUpperCase();
},
},
}
})
注释 Props
Vue 对定义了 type 的 props 执行运行时验证。要将这些类型提供给 TypeScript,我们需要使用 PropType 对构造方法进行指派:
import { defineComponent, PropType } from 'vue'
interface ComplexMessage {
title: string
okMessage: string
cancelMessage: string
}
const Component = defineComponent({
props: {
name: String,
success: { type: String },
callback: {
type: Function as PropType<() => void>
},
message: {
type: Object as PropType<ComplexMessage>,
required: true,
validator(message: ComplexMessage) {
return !!message.title
}
}
}
})
如果你发现验证器没有得到类型推断或者成员完成不起作用(译者注:这一句原文可能就有错 typescript-support),那么使用期望的类型对参数进行注解,也许有助于帮助定位这些问题。
配合使用 Composition API
在 setup() 函数中,不需要将类型传递给 props 参数,因为它将从 props 组件选项推断类型。
import { defineComponent } from 'vue'
const Component = defineComponent({
props: {
message: {
type: String,
required: true
}
},
setup(props) {
const result = props.message.split('') // 正确, 'message' 被标为字符串类型
const filtered = props.message.filter(p => p.value) // 将引发错误: Property 'filter' does not exist on type 'string'
}
})
类型声明 ref
Refs 根据初始值推断类型:
import { defineComponent, ref } from 'vue'
const Component = defineComponent({
setup() {
const year = ref(2020)
const result = year.value.split('') // => Property 'split' does not exist on type 'number'
}
})
有时我们可能需要为 ref 的内部值指定复杂类型。我们可以在调用 ref 时简单地传递一个泛型参数覆盖掉默认推断:
const year = ref<string | number>('2020') // year's type: Ref<string | number>
year.value = 2020 // ok!
::: 注意,如果泛型的类型未知,建议将 ref 指派为 Ref<T>。
:::
类型声明 reactive
当声明类型 reactive 属性时,我们可以使用接口:
import { defineComponent, reactive } from 'vue'
interface Book {
title: string
year?: number
}
export default defineComponent({
name: 'HelloWorld',
setup() {
const book = reactive<Book>({ title: 'Vue 3 Guide' })
// 或
const book: Book = reactive({ title: 'Vue 3 Guide' })
// 或
const book = reactive({ title: 'Vue 3 Guide' }) as Book
}
})
类型声明 computed
计算值将根据返回值自动推断类型
import { defineComponent, ref, computed } from 'vue'
export default defineComponent({
name: 'CounterButton',
setup() {
let count = ref(0)
// 只读
const doubleCount = computed(() => count.value * 2)
const result = doubleCount.value.split('') // => Property 'split' does not exist on type 'number'
}
})