新建工程
- 新建工程时 选择 typescript
- 在已有项目上 使用
vue add @vue/typescript
package.json
多了一些依赖
"dependencies": {
"vue-class-component": "^7.2.3",
"vue-property-decorator": "^9.1.2"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^4.18.0",
"@typescript-eslint/parser": "^4.18.0",
"@vue/cli-plugin-typescript": "^4.5.13",
"@vue/eslint-config-typescript": "^7.0.0",
"typescript": "~4.1.5",
},
tsconfig.json
{
// 编译选项
"compilerOptions": {
"target": "esnext", // 编译输出目标 ES 版本
"module": "esnext", // 采用的模块系统
"strict": true,// 以严格模式解析
"jsx": "preserve",
"importHelpers": true, // 从 tslib 导入外部帮助库: 比如__extends,__rest等
"moduleResolution": "node", // 如何处理模块
"experimentalDecorators": true, // 启用装饰器
"esModuleInterop": true,
"allowSyntheticDefaultImports": true, // 允许从没有设置默认导出的模块中默认导入
"strictPropertyInitialization" : false, // 定义一个变量就必须给它一个初始值
"allowJs": true, // 允许编译javascript文件
"sourceMap": true, // 是否包含可以用于 debug 的 sourceMap
"noImplicitThis": false, // 忽略 this 的类型检查, Raise error on this expressions with an implied any type.
"baseUrl": ".", // 解析非相对模块名的基准目录
"pretty": true,// 给错误和消息设置样式,使用颜色和上下文。
"types": ["webpack-env"], // 设置引入的定义文件
// 指定特殊模块的路径
"paths": {
"@/*": ["src/*"]
},
// 编译过程中需要引入的库文件的列表
"lib": ["esnext", "dom", "dom.iterable", "scripthost"]
},
// ts 管理的文件
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
// ts 排除的文件
"exclude": ["node_modules"]
}
shims-vue.d.ts
:主要用于 TS 识别.vue 文件
declare module '*.vue' {
import Vue from 'vue'
export default Vue
}
shims-tsx.d.ts
:允许.tsx 结尾的文件
import Vue, { VNode } from 'vue'
declare global {
namespace JSX {
// tslint:disable no-empty-interface
interface Element extends VNode {}
// tslint:disable no-empty-interface
interface ElementClass extends Vue {}
interface IntrinsicElements {
[elem: string]: any
}
}
}
使用ts开发时如果要使用第三方js库的同时还想利用ts诸如类型检查等特性就需要声明文件,类似xx.d.ts
同时,vue项目中还可以在shims-vue.d.ts中对已存在模块进行补充npm i @types/xxx
范例:利用模块补充$axios属性到Vue实例,从而在组件里面直接用:
// main.ts
import axios from 'axios'
Vue.prototype.$axios = axios;
// shims-vue.d.ts
import Vue from "vue";
import { AxiosInstance } from "axios";
declare module "vue/types/vue" {
interface Vue {
$axios: AxiosInstance;
}
}
装饰器
https://class-component.vuejs.org/guide/property-type-declaration.html
vue-class-component
import Vue from 'vue'
import Component from 'vue-class-component'
@Component
export default class HelloWorld extends Vue {
// Declared as component data
message = 'Hello World!'
// Declared as component method
hello() {
console.log(message)
}
// Declared as computed property getter
get message() {
return this.message
}
// Declared as computed property setter
set message(value) {
this.message = value
}
beforeRouteEnter(to, from, next) {
console.log('beforeRouteEnter')
next()
}
beforeRouteUpdate(to, from, next) {
console.log('beforeRouteUpdate')
next()
}
beforeRouteLeave(to, from, next) {
console.log('beforeRouteLeave')
next()
}
}
mixins
// mixins.js
import Vue from 'vue'
import Component from 'vue-class-component'
// You can declare mixins as the same style as components.
@Component
export class Hello extends Vue {
hello = 'Hello'
}
@Component
export class World extends Vue {
world = 'World'
}
import Component, { mixins } from 'vue-class-component'
import { Hello, World } from './mixins'
// Use `mixins` helper function instead of `Vue`.
// `mixins` can receive any number of arguments.
@Component
export class HelloWorld extends mixins(Hello, World) {
created () {
console.log(this.hello + ' ' + this.world + '!') // -> Hello World!
}
}
vue-property-decorator
vue-property-decorator 是在 vue-class-component 上增强了更多的结合 Vue 特性的装饰器,新增了这 7 个装饰器:
- @Emit
- @Inject
- @Model
- @Prop
- @Provide
- @Watch
- @Component (从 vue-class-component 继承) ```typescript import { Component, Emit, Inject, Model, Prop, Provide, Vue, Watch } from ‘vue-property-decorator’ import HelloWorld from ‘./components/HelloWorld.vue’;
@Component({ components: { HelloWorld, }, }) export class MyComponent extends Vue { @Prop(String) propA:string;
@Watch(‘child’) onChildChanged(val: string, oldVal: string) { } }
```typescript
import HelloWorld from './components/HelloWorld.vue';
export default {
components:{
HelloWorld
},
props: {
propA: String,
}
methods: {
onChildChanged(val, oldVal) { }
},
watch: {
'child': {
handler: 'onChildChanged',
immediate: false,
deep: false
}
}
}
Emit
// 通知父类新增事件,若未指定事件名则函数名作为事件名(羊肉串形式)
@Emit()
private addFeature(event: any) {// 若没有返回值形参将作为事件参数
const feature = { name: event.target.value, id: this.features.length + 1 };
this.features.push(feature);
event.target.value = "";
return feature;// 若有返回值则返回值作为事件参数
}
原理
装饰器是加工厂函数,它能访问和修改装饰目标
//类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。
function log(target: Function) {
// target是构造函数
console.log(target === Foo); // true
target.prototype.log = function() {
console.log(this.bar);
}
}
@log
class Foo {
bar = 'bar'
}
const foo = new Foo();
// @ts-ignore
foo.log();