Vue 3.0前的 TypeScript 最佳入门实践

前言

我个人对更严格类型限制没有积极的看法,毕竟各类转类型的骚写法写习惯了。

然鹅最近的一个项目中,是 TypeScriptVue ,毛计喇,学之……真香!

1. 使用官方脚手架构建

新的 VueCLI 工具允许开发者 使用  TypeScript 集成环境 创建新项目。

只需运行 vue createmy-app

然后,命令行会要求选择预设。使用箭头键选择 Manuallyselectfeatures

接下来,只需确保选择了 TypeScript 和  Babel 选项,如下图:

完成此操作后,它会询问你是否要使用 class-style component syntax

然后配置其余设置,使其看起来如下图所示。

Vue CLI工具现在将安装所有依赖项并设置项目。

接下来就跑项目喇。

总之,先跑起来再说。

2. 项目目录解析

通过 tree 指令查看目录结构后可发现其结构和正常构建的大有不同。

这里主要关注 shims-tsx.d.ts 和  shims-vue.d.ts 两个文件

两句话概括:

  • shims-tsx.d.ts ,允许你以  .tsx 结尾的文件,在  Vue 项目中编写  jsx 代码

  • shims-vue.d.ts 主要用于  TypeScript 识别  .vue 文件,  Ts 默认并不支持导入  vue 文件,这个文件告诉  ts 导入  .vue 文件都按  VueConstructor<Vue> 处理。

此时我们打开亲切的 src/components/HelloWorld.vue ,将会发现写法已大有不同

至此,准备开启新的篇章 TypeScript 极速入门 和  vue-property-decorator

## 3. TypeScript 极速入门

3.1 基本类型和扩展类型

Typescript 与  Javascript 共享相同的基本类型,但有一些额外的类型。

  • 元组  Tuple

  • 枚举  enum

  • Any 与  Void

1. 基本类型合集

2. 特殊类型

1. 元组  Tuple

想象 元组 作为有组织的数组,你需要以正确的顺序预定义数据类型。

如果不遵循 为元组 预设排序的索引规则,那么 Typescript 会警告。

tuple 第一项应为  number 类型)

2. 枚举  enum *

enum 类型是对JavaScript标准数据类型的一个补充。 像C#等其它语言一样,使用枚举类型可以为一组数值赋予友好的名字。

另一个很好的例子是使用枚举来存储应用程序状态。

3. Void

Typescript 中, 你必须在函数中定义返回类型 。像这样:

若没有返回值,则会报错:

我们可以将其返回值定义为 void :

此时将无法 return

4. Any

Emmm…就是什么类型都行,当你无法确认在处理什么类型时可以用这个。

但要慎重使用,用多了就失去使用Ts的意义。

主要应用场景有:

  1. 接入第三方库

  2. Ts菜逼前期都用

5. Never

用很粗浅的话来描述就是:” Never 是你永远得不到的爸爸。”

具体的行为是:

  • thrownewError(message)

  • returnerror("Something failed")

  • while(true){}// 存在无法达到的终点

3. 类型断言

简略的定义是:可以用来手动指定一个值的类型。

有两种写法,尖括号和 as :

使用例子有:

当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法:

如果你访问长度将会报错,而有时候,我们确实需要在还不确定类型的时候就访问其中一个类型的属性或方法,此时需要断言才不会报错:

安全导航操作符 ( ?. )和非空断言操作符(!.)

安全导航操作符 ( ?. ) 和空属性路径:为了解决导航时变量值为null时,页面运行时出错的问题。

非空断言操作符:

能确定变量值一定不为空时使用。

与安全导航操作符不同的是,非空断言操作符不会防止出现 null 或 undefined。

3.2 泛型: Generics

软件工程的一个主要部分就是构建组件,构建的组件不仅需要具有明确的定义和统一的接口,同时也需要组件可复用。支持现有的数据类型和将来添加的数据类型的组件为大型软件系统的开发过程提供很好的灵活性。

C# 和  Java 中,可以使用”泛型”来创建可复用的组件,并且组件可支持多种数据类型。这样便可以让用户根据自己的数据类型来使用组件。

1. 泛型方法

在TypeScript里, 声明泛型方法 有以下两种方式:

调用方式也有两种:

2. 泛型与 Any

Ts 的特殊类型  Any 在具体使用时,可以代替任意类型,咋一看两者好像没啥区别,其实不然:

  • 方法一,打印了  arg 参数的  length 属性。因为  any 可以代替任意类型,所以该方法在传入参数不是数组或者带有  length 属性对象时,会抛出异常。

  • 方法二,定义了参数类型是  Array 的泛型类型,肯定会有  length 属性,所以不会抛出异常。

3. 泛型类型

泛型接口:

3.3 自定义类型: Interface vs  Typealias

Interface ,国内翻译成接口。

Typealias ,类型别名。

以下内容来自:

Typescript 中的 interface 和 type 到底有什么区别

1. 相同点

都可以用来描述一个对象或函数:

都允许拓展(extends):

interface 和  type 都可以拓展,并且两者并不是相互独立的,也就是说  interface 可以  extendstypetype 也可以  extendsinterface 。  虽然效果差不多,但是两者语法不同

interface extends interface

type extends type

interface extends type

type extends interface

2. 不同点

type 可以而  interface 不行

  • type 可以声明基本类型别名,联合类型,元组等类型

  • type 语句中还可以使用  typeof 获取实例的 类型进行赋值

  • 其他骚操作

interface 可以而  type 不行

interface 能够声明合并

interface 有可选属性和只读属性

  • 可选属性

    接口里的属性不全都是必需的。 有些是只在某些条件下存在,或者根本不存在。 例如给函数传入的参数对象中只有部分属性赋值了。带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个 ? 符号。如下所示

  • 只读属性

    顾名思义就是这个属性是不可写的,对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用 readonly 来指定只读属性,如下所示:

上面的例子说明,当完成User对象的初始化后loginName就不可以修改了。

3.4 实现与继承: implements vs  extends

extends 很明显就是ES6里面的类继承,那么  implement 又是做什么的呢?它和  extends 有什么不同?

implement ,实现。与C#或Java里接口的基本作用一样,  TypeScript 也能够用它来明确的强制一个类去符合某种契约

implement基本用法:

extends 是继承父类,两者其实可以混着用:

搭配 interface 和  type 的用法有:

3.5 声明文件与命名空间: declare 和  namespace

前面我们讲到Vue项目中的 shims-tsx.d.ts 和  shims-vue.d.ts ,其初始内容是这样的:

declare :当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能。

这里列举出几个常用的:

namespace :“内部模块”现在称做“命名空间”

moduleX{ 相当于现在推荐的写法  namespaceX{ )

跟其他 JS 库协同

类似模块,同样也可以通过为其他 JS 库使用了命名空间的库创建 .d.ts 文件的声明文件,如为  D3 JS 库,可以创建这样的声明文件:

所以上述两个文件:

  • shims-tsx.d.ts , 在全局变量  global 中批量命名了数个内部模块。

  • shims-vue.d.ts ,意思是告诉  TypeScript *.vue 后缀的文件可以交给  vue 模块来处理。

3.6 访问修饰符: private 、  public 、  protected

其实很好理解:

  1. 默认为 public

  2. 当成员被标记为 private 时,它就不能在声明它的类的外部访问,比如:

protected 和  private 类似,但是,  protected 成员在派生类中可以访问

4. Vue 组件的  Ts 写法

从 vue2.5 之后,vue 对 ts 有更好的支持。根据官方文档,vue 结合 typescript ,有两种书写方式

Vue.extend

vue-class-component

理想情况下, Vue.extend 的书写方式,是学习成本最低的。在现有写法的基础上,几乎 0 成本的迁移。

但是 Vue.extend 模式,需要与  mixins 结合使用。在 mixin 中定义的方法,不会被 typescript 识别到

,这就意味着会出现 丢失代码提示、类型检查、编译报错等问题。

菜鸟才做选择,大佬都挑最好的。直接讲第二种吧:

4.1 vue-class-component

我们回到 src/components/HelloWorld.vue

有写过 python 的同学应该会发现似曾相识:

  • vue-property-decorator 这个官方支持的库里,提供了函数  装饰器(修饰符) 语法

1. 函数修饰符 @

“@”,与其说是修饰函数倒不如说是引用、调用它修饰的函数。

或者用句大白话描述: @ : “下面的被我包围了。”

举个栗子,下面的一段代码,里面两个函数,没有被调用,也会有输出结果:

直接运行,输出结果:

上面代码可以看出来:

  • 只定义了两个函数:  test 和  func ,没有调用它们。

  • 如果没有“@test”,运行应该是没有任何输出的。

但是,解释器读到函数修饰符“@”的时候,后面步骤会是这样:

  1. 去调用 test 函数,  test 函数的入口参数就是那个叫“  func ”的函数;

  2. test 函数被执行,入口参数的(也就是  func 函数)会被调用(执行);

换言之,修饰符带的那个函数的入口参数,就是下面的那个整个的函数。有点儿类似 JavaScrip t里面的  functiona(function(){...});

2. vue-property-decorator 和  vuex-class 提供的装饰器

vue-property-decorator 的装饰器:

  • @Prop

  • @PropSync

  • @Provide

  • @Model

  • @Watch

  • @Inject

  • @Provide

  • @Emit

  • @Component ( provided by vue-class-component)

  • Mixins (the helper function named  mixins provided by vue-class-component)

vuex-class 的装饰器:

  • @State

  • @Getter

  • @Action

  • @Mutation

我们拿原始Vue组件模版来看:

以上模版替换成修饰符写法则是:

正如你所看到的,我们在生命周期 列表那都添加 privateXXXX 方法,因为这不应该公开给其他组件。

而不对 method 做私有约束的原因是,可能会用到  @Emit 来向父组件传递信息。

4.2 添加全局工具

引入全局模块,需要改 main.ts :

npm iVueI18n

但仅仅这样,还不够。你需要动 src/vue-shim.d.ts

之后使用 this.$i18n() 的话就不会报错了。

4.3 Axios 使用与封装

1. 新建文件 request.ts

文件目录:

2. request.ts 文件解析

为了方便,我们还需要定义一套固定的 axios 返回的格式,新建 ajax.ts

3. main.ts 接口调用:

5. 编写一个组件

为了减少时间,我们来替换掉 src/components/HelloWorld.vue ,做一个博客帖子组件:

然后在 Home.vue 中使用:

这时候运行项目

这就是简单的父子组件

6. 参考文章

TypeScript — JavaScript with superpowers — Part II

VUE WITH TYPESCRIPT

TypeScript + 大型项目实战

Python修饰符 (一)—— 函数修饰符 “@”

Typescript 中的 interface 和 type到底有什么区别

作者掘金文章总集

需要转载到公众号的喊我加下白名单就行了。

  • 「真®全栈之路」Web前端开发的后端指南

  • 「Vue实践」5分钟撸一个Vue CLI 插件

  • 「Vue实践」武装你的前端项目

  • 「中高级前端面试」JavaScript手写代码无敌秘籍

  • 「从源码中学习」面试官都不知道的Vue题目答案

  • 「从源码中学习」Vue源码中的JS骚操作

  • 「从源码中学习」彻底理解Vue选项Props

  • 「Vue实践」项目升级vue-cli3的正确姿势

  • 为何你始终理解不了JavaScript作用域链?

:heart: 看完三件事

如果你觉得这篇内容对你挺有启发,我想邀请你帮我三个小忙:

  1. 点个 「在看」 ,让更多的人也能看到这篇内容( 喜欢不点在看,都是耍流氓 -_-

  2. 关注我的 GitHub:github.com/yygmind ,让我们成为长期关系

  3. 关注公众号「高级前端进阶」, 每周重点攻克一个前端面试重难点 ,公众号后台回复「面试题」 送你高级前端面试题。