不再头秃!typescript平滑重构vue项目

4,461 阅读3分钟

原始项目,令人头秃

我刚接手手头这个项目的时候,console里到处飘红,整个项目哪哪都是bug,满目疮痍。经过一段时间的调整,虽然可以正常运行,但是很多问题迟迟没有解决,比如乱七八糟的调用、没有闭合的if else、没有处理的try catch……可以说,为了维护这一个问题多多的项目,牺牲了很多我的头发...以上是牢骚段落。

作为一个深知“懒是第一生产力”的工具人,在烦躁之余,一直在寻找一劳永逸的方法。终于,typescript进入我的视线。有了typescipt,一些基础的问题可以直接在编写、编译时报错,它的好处我也不必多说了。

然而,vue+typescript的做法并不普遍,也有争议,网上也多是老版本的做法,对于vue-cli3+typescript,怎么样平滑地、一天做一点地引入typescript呢?

生产力工具篇

vue add typescript

第一步看官网资料,官网详尽地写了如何配置,不过,对于大多数用vue-cli搭建的项目,其实重点是第一句

vue cli
就是vue-cli,通过引入vue-cli-plugin-typescript插件,很简单,一行代码搞定:

vue add @vue/typescript

该插件会帮助配置webpack,引入ts.config,并把js文件转换成ts文件,作为参考,还会同时生成一个ts写法的vue文件。

因为我们需要平滑重构,而不是一下完成所有的事情,所以Allow .js files to be compiled选择yes,允许js编译。

vue-js转换为vue-ts

重构最累的一点就是,为了给类型标注,几乎要把之前的所有文件重写一遍,比如:

import Vue from 'vue'
import Component from 'vue-class-component'

// 1.添加修饰符
@Component()
// 2.基于类的vue文件
export default class MyComponent extends Vue {
  // 3.属性标注
  message: string = 'Hello!'
  // 4.方法标注
  onClick (): void {
    window.alert(this.message)
  }
  // 5.其他变化
}

这样修改很难吗?不难,但谁会想做个重复劳动百八十回的工具人呢?

解放双手,自动转换:TransVue2Ts

这里特别感谢 @清液 大神的解放双手-vue语法自动转typescript ,文中提到了转换工具,可以将vue-js的data/computed/prop/watch/mixin等统统转换成vue-ts,我都一一测试过,没有任何问题,强力推荐!👍👍👍

具体的转换方式和写法都在上文帖子中,我就不赘述了。 有了转换工具,完全可以在需要时再将vue-js转为vue-ts,局部重构。

tslint

我用的是腾讯团队的tslint-config-alloy,规则因团队而异,大体按照这套规则。

踩坑篇

引入第三方插件

当在vue中引入第三方的插件时,typescript会无法识别

import '*' as _ from 'lodash'

这时可以先尝试添加官方的typescript类型声明npm install @types/xxx

npm install @types/lodash 

像lodash这种,官方是有ts声明文件的,添加后就可以识别了;对于没有声明文件的,则需要在shime-global.d.ts文件中声明

declare module 'xxx'

data属性无法访问全局属性

解决方案:写在created()钩子中。最常见的例子:

@Component()
export default class Hello extends Vue {
    id:string = this.$route.query.id  // 这样是会报错的
    
    // 改成这样即可
    id:string = ''
    created(){
        this.id = this.$route.query.id
    }
}

对象的undefined、null处理

我们经常会遇到这种情况:

let user= {
    name:'小明',
    friends:[
        {name:'小明的朋友'}
    ]
}
alert(user.friends[0].name) // '小明的朋友'

当小明不存在(user为null),或者小明没有朋友(friends为null)时,就会报错,解决:

// 方法一
if(user&&user.friends&&user.friends.length>0){
    alert(user.friends[0].name) 
}
// 方法二
alert((((user||{}).friends||[])[0]||{}).name) 

要是再多几层,就会更难写难读,所以我推荐方法三:使用 ts-optchain

import { oc } from 'ts-optchain';
alert(oc(user).friends[0].name('这里可以给个默认值'))

小结

文章仅是我个人的经验总结,也只是提到了重构中关键性的工具,具体配置以官方为准,如有错漏,欢迎批评指正!