用TypeScript开发Vue——如何通过vue实例化对象访问实际ViewModel对象
目录
用 TypeScript 开发 Vue——如何通过 vue 实例化对象访问实际 ViewModel 对象
背景
我个人很喜欢 TypeScript 也很喜欢 Vue,但在两者共同使用的时候遇到一个问题。
Vue 的实例化对象代理了所有实际 ViewModel 对象,具体可参见官方文档(
http://vuejs.org.cn/guide/instance.html# 属性与方法)
Vue 的属性与方法:
每个 Vue 实例都会代理其 data 对象里所有的属性
实际上 vue 实例不仅仅是代理了 data 属性,还代理了 methods 属性、computed 属性等,可以通过 这篇文档 看到。那么怎么在 TypeScript 里面通过 vue 实例访问 data 属性和 methods 属性里面的变量是最大问题,否则就没办法使用 TS 的最大的作用——强类型检查。
如下图可以看到:
虽然实际上 vm.xxcanghaiFn 是可用的,但是过不了 TypeScript 的编辑检查,提示不在 'xxcanghaiFn' 不在 'Vue' 类型中。
因为类型 'Vue' 中肯定只有内部方法,自然会报错,虽然我们可以通过<any>
语法强制使语法检查失效,如下代码:
var vm: any = new Vue({//vm 变量增加 any 声明
el: "#app",
data: {
xxcanghaiData: "xxcanghai"
},
methods: {
xxcanghaiFn: function () { }
}
});
vm.xxcanghaiFn// 无编译器报错
虽然没有编译器报错,但同时也无法再使用 TS 提供的智能补全,强类型检查等功能。这就跟直接写 js 没有任何区别了。
解决方案
- 将 data 属性,以及 methods 等需要合并进 vue 类型的对象分开写
- 利用 TypeScript 的
typeof
和declare
关键字将类型合并声明。 - 最后 new Vue 时强制用
<any>
声明并赋值。
如下代码:
// 核心声明,利用 typeof 将 data 和 methods 属性合并进 Vue 类型
declare var VM: typeof vmData & typeof vmMethods & vuejs.Vue;
var vmData = {
xxcanghaiData: "xxcanghai"
};
var vmMethods = {
xxcanghaiFn: () => { }
}
var vm: typeof VM = <any>new Vue({
el: "#app",
data: vmData,
methods: <any>vmMethods
});
效果如下,既可以实现识别 Vue 内置函数及属性:
也能实现识别我们自定义的 data 属性和 methods 属性中的值:
关于 Vue 中的计算属性类型
Vue 有有一种特殊的 ViewModel 的属性——计算属性。
计算属性在使用 ts 的强类型的时候就会出错,代码如下:
declare var VM: vuejs.Vue & typeof vmComputed;
var vmComputed = {
/**
* 字符串计算属性
*/
xxcanghaiCom: function () {
return "xxcanghaiCom";
}
}
var vm: typeof VM = <any>new Vue({
el: "#app",
computed: <any>vmComputed
});
计算属会被 ts 的类型系统识别为一个函数,而出现函数相关的方法,此时调用字符串方法自然会报错。如图:

虽然计算属性实际上确实是一个函数,但是我们希望能够把计算属性拿来当一个字符串变量来使用。
TypeScript 的强制类型声明语法
这里可以使用 ts 的强制类型声明语法 <TYPE>
,来把指定类型强制声明为其他类型,如下:
var a;
(<string>a).charAt(0);// 合法
(<number>a).toFixed();// 合法
强制类型声明的局限性
但是此语法也有局限性,即只能强制声明那些未知类型的变量,不能强制声明已知类型的变量,如下:
var a = 0;
(<string>a);// 报错 Neither type 'number' nor type 'string' is assignable to the other.
因为变量 a 已经可以被类型推断出为number
类型了,遂不能再强制声明为string
类型。
计算属性类型的解决方案
解决方案为 利用any
类型中转来实现强制类型声明转换。
在 TypeScript 中的any
类型的规则为:
1、任何类型都可以被转换为
any
类型。
2、any
类型可以转换为任何类型。
所以先将计算属性的函数,或是 getter,setter 的 Object 声明为any
类型,再声明为你想实际使用的变量类型。如下:
declare var VM: vuejs.Vue & typeof vmComputed;
var vmComputed = {
/**
* 字符串计算属性
*/
xxcanghaiCom: <string>(<any>function () {
return "xxcanghaiCom";
}),
/**
* getter 和 setter 形式的字符串计算属性
*/
xxcanghaiGetSet: <string>(<any>{
get: function () {
return vm.xxcanghaiCom;
},
set: function (newVal: string) {
vm.xxcanghaiCom = newVal;
}
})
}
var vm: typeof VM = <any>new Vue({
el: "#app",
computed: <any>vmComputed
});
效果如下图,虽然xxcanghaiGetSet
是 object,但此处可以按照我们想要的string
类型来使用。
后记
本文比较初级,因为刚刚开始接触 Vue,因为之前用过 Angular 和 Avalon,所以上手起来还算舒服,之前用 Angular 的时候就因为用 TypeScript 写非常难受,遂打算好好研究下 TypeScript 与 Vue 的协同工作的问题。
写的比较匆忙,vue 也还没有完全了解,遂文中有不对的地方欢迎指正。😃