ts基础及ts+vue入门

1,222 阅读18分钟

TypeScript基础

1、环境安装于运行

基本概念

  • ts要先编译转换成js,才能交给浏览器运行;就类似 Less 要转换成 CSS 一样
  • 全局安装 ts
npm i -g typescript

// 检验是否安装成功
tsc -v
  • tsc作用:负责将 ts 代码转为浏览器和 nodejs 能识别的 js 代码
// 手动敲击命令行编译
tsc 需要编译的ts文件

配置自动编译

  1. 运行tsc --init,生成 tsconfig.json 文件
  2. 修改 tsconfig.json 文件,设置存放转换成的js文件夹:"outDir": "./js/"
  3. 设置 vscode 监视任务:我们编写保存ts文件后自动编译成js文件并存放在我们制定的目录下

2、变量与数据类型

变量

  • 原有类型:undefined、number、null、boolean、string、Symbol、Object、Array
  • 新增类型:tuple元祖、enum枚举、any任意类型、never、void

在 ts 为变量指定了类型,就只能给这个变量设置相同类型的值,否则就会报错

数组

js中,一个数组可以存放各种各样的数据类型,但是在ts中声明数组时需要制定数组里面的元素类型,即只能存放单一类型

// 方式1
let 数组名:类型[] = [值1, 值2]
// 下面数组声明了只能存放字符串,即所有元素都必须是字符串
eg: let name: string[] = ['ceq', 'zjh']

// 方式2
let 数组名:Array<类型> = [值1, 值2]
eg: let name: Array<string> = ['ceq', 'zjh']

元祖(属于数组的一种)

概念:就是一个规定了元素数量和每个元素类型的“数组”,而每个元素的类型,可以不相同

语法:

let 元祖名:[类型1,类型2,类型3] = [值1,值2,值3]
let tup1: [string, number, boolean] = ["字符串", 18, true]

枚举

语法:枚举项一般用英文和数字,而枚举值用整型数字。默认情况下,从0开始为元素编号。也可以手动的指定成员的数值

enum 枚举名 {
    枚举项1 = 枚举值1
    枚举项2 = 枚举值2
    枚举项3 = 枚举值3
}

enum Gender {
    Boy = 1,
    Girl = 2,
    Unknown = 3
}
let userSex: Gender = Gender.Boy

枚举类型提供的一个便利是你可以由枚举的值得到它的名字

enum Color {Red = 1, Green, Blue}
let colorName: string = Color[2];

console.log(colorName);  // 显示'Green'因为上面代码里它的值是2

any

概念:any代表任意类型,一般在获取 dom 时使用;比如我们在接收用户输入 或 第三方代码库时,不确定会返回什么类型的值,此时也可以使用 any 类型

实例:

let txtName: any = document.getElementById('txtN')

void

概念: void 代表没有类型,一般用在无返回值的函数

语法:

function sayHi(): string {
    return 'hi'
}

function sayHi2(): void {
    console.log('无返回值')
}

never

概念:never代表不存在的值的类型,常用作为抛出异常或无限循环的函数返回类型

never类型是ts中的底部类型,所有类型都是 never 类型的父类,所以 never 类型值可以赋给任意类型的变量

类型推断

如果变量的声明和初始化是在同一行,则可以省略掉变量类型的声明

类型断言

类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。它没有运行时的影响,只是在编译阶段起作用。 TypeScript会假设你,程序员,已经进行了必须的检查。

  • 类型断言有两种形式。 其一是“尖括号”语法:
let someValue: any = "this is a string";

let strLength: number = (<string>someValue).length;
  • 另一个为as语法:
let someValue: any = "this is a string";

let strLength: number = (someValue as string).length;

联合类型

概念:表示取值可以为多种类型中的一种

let 变量名:变量类型1 | 变量类型2 = 值
// eg:接收 prompt 函数的返回值
let dName: string | null = prompt('请输入狗狗的名字:')
console.log('hello ' + dName)

非空断言符!

在上下文中当类型检查器无法断定类型时,一个新的后缀表达式操作符 ! 可以用于断言操作对象是非 null 和非undefined 类型。具体而言,x! 将从 x 值域中排除 nullundefined

例子:

function sayHello(name: string | undefined) {
  let sname: string = name; // Error,因为无法保证name的值不为 undefined
}

普通解决方案:

function sayHello(name: string | undefined) {
  let sname: string;
  if (name) {
    sname = name;
  }
}

ts断言符:

function sayHello(name: string | undefined) {
  let sname: string = name!;
}

3、函数

返回值类型

// 如果函数没有返回值,则定义为 void
function 函数名(): 返回值类型 {
    
}
let 变量名:变量类型 = 函数名()

参数类型

实参和形参类型要保持一致,数量也要一致

function 函数名(形参1:类型,形参2:类型): 返回值类型 {
    
}
let 变量名:变量类型 = 函数名(实参1,实参2)

可选参数(问号)

function 函数名(形参?:类型): 返回值类型 {
    
}

// 调用
函数名()  // 可以不传递参数
函数名(实参值) // 可以传递实参

默认值

function 函数名(形参1:类型 = 默认值1,形参2:类型 = 默认值2): 返回值类型 {
    
}

剩余参数

问题:比如下面的函数参数数量不确定

function add(a:number,b:number):void {
    console.log(a + b)
}

剩余参数

function add(a:number,b:number,...形参3:类型[]):void {
    console.log(a + b)
}

特点

  • 只能定义一个剩余参数
  • 只能被定义为数组
  • 只能定义在形参列表最后

调用

函数名(1,2)// 传递2个实参
函数名(1,2,3)// 传递3个实参
函数名(1,2,3,4,5)// 传递2+个实参 

重载

JavaScript本身是个动态语言。 JavaScript里函数根据传入不同的参数而返回不同类型的数据是很常见的。es5中出现同名方法,下面的会替换上面的方法;在ts中,它会查找重载列表,尝试使用第一个重载定义。 如果匹配的话就使用这个,否则就找下一个。

function getInfo(name: string):string // 重载列表1
function getInfo(age: number):string // 重载列表2

// 下面这个不是重载列表,总共就上面定义的两个重载列表
function getInfo(str):any {
     if (typeof str == "string") {
        return `我叫${str}`
    }
    else {
        retrun `我的年龄是${str}`
    }
}

alert(getInfo('张三')  // 正确
alert(getInfo(23)  // 正确
alert(getInfo(true)  // 错误写法

4、类

创建class

  • 当创建类的实例时,默认会调用类对应的constructor构造函数。
  • 当你不需要在constructor构造函数中做任何操作时,写和不写constructor构造函数都一样。当不写constructor构造函数时,编译器默认给该类生成一个空的constructor构造函数
  • 一般在new 一个类的实例的时候,我们可能需要“初始化该实例内部变量的值”,这种操作需要放到constructor构造函数中执行。
class City {
    // 成员变量属性,定义在类中,前面省略了public关键词
    cName: string
    cLevel: number
    
    // 构造函数,实例化类的时候触发的方法
    constructor(name: string, level: number) {
        this.cName = name
        this.cLevel = level
    }
    
    // 成员方法,定义在类中。挂载在原型上 City.prototype.getName
    getName():string {
        return this.cName
    }
}

类的继承 extends + super、重写

// 父类
class Person {
    name:string;
    
    constructor(name: string) {
        this.name = name;
    }
    
    run(): string {
        retrun `${this.name}在跑`
    }
}

// 子类
class Son extends Person {
    constructor(name: string) {
        // 初始化父类的构造函数
        // 它会执行基类的构造函数。 而且,在构造函数里访问 this的属性之前,我们 一定要调用 super()
        super(name)
    }
    
    // 重写父类的方法
    run(): string {
        return `${this.name}在跑---子类的`
    }
    
    // 拓展子类自己的方法
    work() {
        
    }
}

let t = new Son('ceq')
alert(t.run()) // 我们在执行一个类里面的方法时,首先从子类开始寻找,子类里面找不到该方法则往父类里面去找

类里面的修饰符

  • public:默认是公有,在类里面、子类、类外面都可访问
  • protected:保护类型,在类里面、子类里面可以访问;类外部不可访问
  • private:私有,在类里面可访问;子类、类外面都不可访问

static静态属性,静态方法

  • 实例方法:只有通过new之后才能调用;
  • 静态方法:直接通过类名就能调用,不用new一个实例
class Person {
    name:string;
    // 静态属性
    static sex: string = '男'
    
    constructor(name: string) {
        this.name = name;
    }
    
    // 实例方法
    run(): string {
        retrun `${this.name}在跑`
    }
    
    // 静态方法
    // 不能直接调用类里面的属性,比如这样 this.name
    static print() {
        alert('print是静态方法')
    }
}

// 直接通过类名调用静态属性静态方法
console.log(Person.sex)
Person.print()

多态

父类定义一个方法不去实现,让继承它的子类去实现,每一个子类有不同的表现;多态也是继承的一种表现

class Animal {
    name: string
    constructor(name:string) {
        this.name = name
    }
    // 让不同子类去重写这个方法
    eat() {
        console.log('不同动物吃的东西不一样')
    }
}

// 子类1
class Dog extends Animal{
    constructor(name: string) {
        super(name)
    }
    
    // 重写父类的eat方法
    eat() {
        console.log(`${this.name}吃肉`)
    }
}

// 子类2
class Cat extends Animal{
    constructor(name: string) {
        super(name)
    }
    
    // 重写父类的eat方法
    eat() {
        console.log(`${this.name}吃鱼`)
    }
}

抽象方法

  • ts中的抽象类:他是提供其他继承类的基类(父类),不能直接被实例化
  • abstract 关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类 中实现
  • 抽象 方法只能放在抽象类里面
  • 抽象类和抽象方法用来定义标准

比如下面的标准:Animal类要求他的子类必须包含eat 方法

abstract class Animal {
    name: string
    constructor(name:string) {
        this.name = name
    }
    
    // 抽象方法,在基类中不用实现,但在子类必须被实现
    abstract eat(): any;
}

// let a = new Animal() 错误写法,抽象类不能直接被实例化

class Dog extends Animal {
    constructor(name: string) {
        super(name)
    }
    
    // 抽象类的子类必须实现抽象类里面的抽象方法
    eat() {
        console.log(`${this.name}吃粮食`)   
    }
}

5、接口interface (定义一个标准)

接口的作用:在面向对象的编程中,接口是一种规范的定义,它定义了行为的规范和动作的规范,在程序设计里面接口起到一种限制和规范的作用。接口定义了某一批类所需要遵守的规范,接口不关心这些类的内部状态数据,也不关心这些类里方法的实现细节,他只规定这批类里必须提供某些方法,提供这些方法的类就可以满足实际需要。ts中的接口类似于java,同时还增加了更灵活的接口类型,包括属性、函数、可索引和类等。

也是定义标准,甚至可以说自定义一种新类型

属性接口

// 就是对传入对象的约束
interface FullName {
  firstName: string;  // 注意是以分号结束
  secondName: string;
}

function printName(name: FullName) {
  // 传入对象必须包含 firstName,secondName
  console.log(`${name.firstName}--${name.secondName}`);
}

let obj = { 
  age: 10,  // 可以多出其他属性,但是一般不这么写,用不到
  firstName: '张',
  secondName: '三'
};
printName(obj);

LabelledValue接口就好比一个名字,用来描述上面例子里的要求。这次使用接口来描述:必须包含一个label属性且类型为string:

函数类型接口

即对方法传入的参数以及返回值进行约束

例子:加密的函数类型接口

interface encrypt {
    // 规定了函数的参数以及返回值的格式
    (key: string, value: string): string;
}

let ms5: encrypt = function(key: string, value: string):string {
    // 模拟操作
    return key + value
}

// 调用
md5('name', 'zhangsan')

类类型接口(implements)

对类的约束,和抽象类有点相似

interface Animal {
    // 规定了必须要有 name 属性和 eat 方法
    name: string; 
    eat(str: string): void;
}

class Dog implements Animal {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    
    eat() {
        console.log(this.name + '吃粮食')
    }
}

let d = new Dog('小黑')
d.eat()

泛型(任意类型)

  • 软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。
  • 通俗来讲,泛型就是解决类、接口、方法的复用性、以及对不特定数据类型的支持(包括类型校验,any的话是没有类型校验的)

泛型函数

可以支持不特定数据类型,要求:传入的参数和返回的参数一致

// T 表示泛型,具体说明类型是调用这个方法的时候决定的
function getData<T>(value:T): T {
    return value;
}

getData<number>(123)

泛型类

class Minclass<T> {
    list: T[] = [];
    add(value: T): void {
        this.list.push(value)
    }
    
    min(): T {
        let minNum = this.list[0]
        for(let i = 0; i < this.list.length; i++) {
            if (minNum > this.list[i]) {
                minNum = this.list[i]
            }
        }
        return minNum;
    }
}

// 实例化类,并且制定了类的 T 代表的类型是number
let m1 = new Minclsaa<number>(); 
m1.add(1)
m1.add(2)
m1.add(3)
console.log(m1.min())

泛型接口

对比函数类型接口

interface ConfigFn {
    <T>(value: T): T
}

let getData:ConfigFn = function<T>(value: T):T {
    return value;
}

getData<string>("zhangsan");

写法2:

interface ConfigFn<T> {
    (value: T): T
}

function getData<T>(value: T):T {
    return value;
}
let myGetData:ConfigFn<string> = getData;

myGetData("zhangsan");

装饰器

  • 装饰器是一种特殊类型的声明,他能够被附加到类声明,方法,属性或参数上,可以修改类的行为
  • 通俗的讲装饰器就是一个方法,可以注入到类、方法、属性参数上来拓展类、属性、方法、参数的功能。
  • 常见的装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器
  • 装饰器的写法:普通装饰器(无法传参)、装饰器工厂(可传参)
  • 装饰器是过去中js最大成就之一,已是es7 的标准特性之一

类装饰器

类装饰器在类声明之前被声明(紧靠着类声明)。类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。传入一个参数

  • 写法1、普通装饰器(无法传参)
// 类装饰器,其实就是一个方法;
function logClass(params:any) {
    // params 就是当前类
    console.log(params)
    // 拓展当前类
    params.prototype.apiUrl = '动态拓展的属性'
    params.prototype.run = function() {
        console.log('我是拓展的方法run')
    }
}

@logClass // 调用类装饰器
class HttpClient {   // HttpClient类赋值给params
    constructor() {
        
    }
    
    getData() {
        
    }
}

var http:any = new HttpClient()
console.log(http.apiUrl)
http.run
  • 写法2、装饰器工厂(可传参)
function logClass(params:string) {
   return function(target:any) {
       console.log(target)
       console.log(params)
       target.prototype.apiUrl = params
    }
   }
}

@logClass('http://127.0.0.1/login')  // 接口 'http://127.0.0.1/login'赋值给params
class HttpClient {  // 把当前类HttpClient 赋值给target
    constructor() {
        
    }
    
    getData() {
        
    }
}

var http:any = new HttpClient()
console.log(http.apiUrl)

类装饰器重载构造函数

  • 类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。
  • 如果类装饰器返回一个值,他会使用提供的构造函数来替换类的声明。
function logClass(target:any) {
    console.log(target)
    
    return class extends target {
        apiUrl:any = '我是修改后的数据'
        
        getData() {
            this.apiUrl = this.apiUrl +  '---'
            console.log(this.apiUrl)
        }
    }
}

@logClass  // 调用类装饰器
class httpClient {
    public apiUrl:string | undefine
    constructor() {
        this.apiUrl = '我是构造函数里面的apiUrl'
    }
    getData() {
        console.log(this.apiUrl)
    }
}

属性装饰器

属性装饰器表达式会在运行的时候当作函数被调用,传入下列2个参数:

  • 对于静态成员来说是类的构造函数,对于实例成员来说是类的原型对象
  • 成员的名字
// 类装饰器
function logClass(params:string) {
   return function(target:any) {
       console.log(target)
       console.log(params)
    }
   }
}

// 属性装饰器
function logProperty(params:any){
    return function(target:any, attr:any) {
        console.log(target) // 类的原型对象,相当于开始的target.prototype
        console.log(attr)  // url
        
        target[attr] = params
    }
}

@logClass('xxx')  // 这个装饰器就是装饰下面这个类的
class HttpClient {
    @logProperty('http://127.0.0.1/login') // 调用属性装饰器,该装饰器就是装饰下面这个 url 属性的
    url: any | undefine
    constructor() {
        
    }
    
    getData() {
        console.log(this.url)
    }
}

var http:any = new HttpClient()
http.getData()

方法装饰器

他会被应用到方法的属性描述符上,可以用来监视,修改或者替换方法定义。方法装饰器会在运行时传入下列3个参数:

  • 对于静态成员来说是类的构造器,对于实例成员来说是类的原型对象
  • 成员的名字
  • 成员的属性描述符
// 方法装饰器一
function get(params:any) {
    return function(target:any, methodName:any, desc:any) {
        console.log(target) // 类的原型对象
        console.log(methodName) // getData
        console.log(desc)
        
        target.apiUrl = 'xxx'
        target.run = function() {
            console.log('run')
        }
    }
}
class HttpClient {
    url: any | undefine
    constructor() {
        
    }
    @get('http://127.0.0.1/login') // 调用方法装饰器
    getData() {
        console.log(this.url)
    }
}

var http:any = new HttpClient()
console.log(http.apiUrl)
http.run()

使用方法装饰器修改类里面的方法

// 方法装饰器二
function get(params:any) {
    return function(target:any, methodName:any, desc:any) {
        console.log(target) // 类的原型对象
        console.log(methodName) // getData
        console.log(desc.value) // 打印当前的方法
        
        // 修改装饰器的方法,把装饰器方法里面传入的所有参数改为 string 类型
        // 1、保存当前的方法
        let oMethod = desc.value
        desc.value = function(...args:any[]) {
            args = args.map(value => String(value)
            console.log(args)
            
            //执行原来的getData方法
            oMethod.apply(this,args)
        }
    }
}
class HttpClient {
    url: any | undefine
    constructor() {
        
    }
    @get('http://127.0.0.1/login') // 调用方法装饰器
    getData(...args:any[]) {、、
        console.log('我是getData里面的方法')
    }
}

var http:any = new HttpClient()
console.log(http.apiUrl)
http.run()

装饰器执行顺序

  • 属性 -> 方法 -> 方法参数 -> 类
  • 如果有多个同样的装饰器,他会先执行后面的

vue_ts综合应用

1、搭建vue + ts 脚手架

  1. 安装vue/cli(要3.0版本以上的才可以)
npm i -g @vue/cli
  1. 创建 vue 项目
vue create 项目名
  1. 安装 vue/typescript
vue add @vue/typescript

vue-cli 3.0 版本与3.0以下版本在搭建项目时的区别:

  • 3.0 版本的文件目录少了很多 eg:buildconfig
  • 那么如何像vue-cli 2.*之前关于端口号的配置、打包之后的路径的配置、图片的配置等,到哪里配置呢
  • vue-cli 3.0 可以在项目的根目录下新建一个 vue.config.js 文件,之前繁琐的配置都可以在这里直接配置
  • 官方是这样说的:vue.config.js 是一个可选的配置文件,如果项目的(和package.json 平级)根目录中存在这样一个文件,那么他会被 @vue/cli-service 自动加载,(并覆盖其内部的配置)

vue.config.js 里的相关配置可参考 官网

vue-cli 3 项目目录(不是ts,不过目录都差不多):

vue-cli 2.x 构建项目的目录:

├─build                         // webpack开发打包相关配置
│ ├─build.js                        // 构建环境下的配置:loading动画;删除创建目标文件夹;webpack编译;输出信息
│ ├─check-versions.js               // node和npm的版本检查
│ ├─logo.png                        
│ ├─utils.js                        // 配置静态资源路径;cssLoaders用于加载.vue文件中的样式;styleLoaders用于加载不在.vue文件中的单独存在的样式文件
│ ├─vue-loader.conf.js              
│ ├─webpack-base.conf.js            // 基本的webpack配置.配置webpack编译入口/webpack输出路径和命名规则/模块resolve规则/不同类型模块的处理规则
│ ├─webpack-dev.conf.js             // 开发环境配置,在base.conf基础进一步完善;合并基础webpack配置;将hot-reload相关的代码添加到entry chunks;使用styleLoaders;配置Source Maps / webpack插件;
│ ├─webpack-prod.conf.js            // 生产环境配置,在base.conf基础进一步完善;合并基础webpack配置;使用styleLoaders;配置webpack输出/webpack插件;gzip模式下的webpack插件配置;webpack-bundle分析
├─config                        // 项目配置(端口号等)
│ ├─dev.env.js                      
│ ├─index.js                        // 用于定义开发环境和生产环境所需要的参数
│ ├─prod.env.js                     
├─dist                          // cnpm run build  项目打包后生成的文件夹
├─node_modules                  // cnpm install    项目依赖模块
├─src
│ ├─assets                      // 资源目录(放置一些图片等),这里的资源会被webpack构建
│ ├─components
│ ├─router                          // 路由
│ ├─App.vue                         // 根组件
│ ├─main.js                         // 入口js
├─static                        // 纯静态资源(不会变动的资源,如图片、字体),不会被webpack构建,直接被复制到打包目录dist/static
├─.babelrc                      // 使用babel的配置文件,用来设置转码规则和插件
├─.editorconfig                 // 代码的规范文件(规定使用空格或tab缩进,缩进的长度等,使用的话需要在编辑器下载对应的插件)
├─.gitignore                    // 指定 git 忽略的文件,所有 git 操作均不会对其生效;
├─.postcssrc.js                 // 指定使用的 css 预编译器,里面默认配置了 autoprefixer ,自动补全浏览器前缀
├─index.html
├─package-lock.json            // 确定当前安装的包的依赖,以便后续重新安装的时候生成相同的依赖,而忽略项目开发过程中有些依赖已经发生的更新;
├─package.json                 // 项目配置文件
└─README.md                    // 备注文件,对项目开发过程中需要注意的地方进行一些说明

2、vue组件的TS写法

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

  • Vue.extend
  import Vue from 'vue'

  const Component = Vue.extend({
  	// type inference enabled
  })

  • vue-class-component
import { Component, Vue, Prop } from 'vue-property-decorator'

@Component
export default class Test extends Vue {
  @Prop({ type: Object })
  private test: { value: string }
}

Vue.extend模式,需要与mixins 结合使用。在 mixin 中定义的方法,不会被 typescript 识别到,这就意味着会出现丢失代码提示、类型检查、编译报错等问题。所以一般都是使用第二种(以下都是针对vue-class-component写法进行分析)

vue-property-decorator

这个官方支持的库里,提供了函数装饰器(修饰符语法

函数修饰符 @:

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

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

test(f){
    console.log("before ...");
    func()
	console.log("after ...");
 }

@test
func(){
	console.log("func was called");
}

运行结果
before ...
func was called
after ...

上面代码可以看出来:

  • 只定义了两个函数:testfunc,没有调用它们。
  • 如果没有@test,运行应该是没有任何输出的。

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

  • 去调用test函数,test函数的入口参数就是那个叫func的函数;

vue-property-decoratorvuex-class提供的装饰器

vue-property-decorator装饰器

  • @Prop
  • @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

原始模板对比ts写法模板

  • 原始模板:
<script>
import {componentA,componentB} from '@/components';

export default {
  components: { componentA, componentB},
  props: {
    propA: { type: Number },
    propB: { default: 'default value' },
    propC: { type: [String, Boolean] },
  }
  // 组件数据
  data () {
    return {
      message: 'Hello'
    }
  },
    
  // 计算属性
  computed: {
    reversedMessage () {
      return this.message.split('').reverse().join('')
    }
    // Vuex数据
    step() {
    	return this.$store.state.count
    }
  },
  methods: {
    changeMessage () {
      this.message = "Good bye"
    },
    getName() {
    	let name = this.$store.getters['person/name']
    	return name
    }
  },
  // 生命周期
  created () { },
  mounted () { },
  updated () { },
  destroyed () { }
}
</script>

TS中的修饰符写法:

<script lang="ts">  // lang="ts" 注明是ts语法
import { Component, Vue, Prop } from 'vue-property-decorator';
import { State, Getter } from 'vuex-class';
import { count, name } from '@/person'
import { componentA, componentB } from '@/components';

// 注册引入的组件 
@Component({
    components:{ 
      componentA, 
      componentB
    },
})
// 所有组件都是继承Vue的
export default class HelloWorld extends Vue{
    // props属性;readonly来指定只读属性
	@Prop(Number) readonly propA!: number | undefined
    @Prop({ default: 'default value' }) readonly propB!: string
    @Prop([String, Boolean]) readonly propC!: string | boolean | undefined
  
  // 原data
  message = 'Hello'
  
  // 计算属性
  private get reversedMessage (): string[] {
  	return this.message.split('').reverse().join('')
  }
  // Vuex 数据
  @State((state: IRootState) => state . booking. currentStep) step!: number
	@Getter( 'person/name') name!: name
  
  // method
  public changeMessage (): void {
    this.message = 'Good bye'
  },
  public getName(): string {
    let storeName = name
    return storeName
  }
	// 生命周期
  private created ():void { },
  private mounted ():void { },
  private updated ():void { },
  private destroyed ():void { }
}
</script>

我们在生命周期 列表那都添加private XXXX方法,因为这不应该公开给其他组件。而不对method做私有约束的原因是,可能会用到@Emit来向父组件传递信息。

添加全局工具

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

import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';

Vue.config.productionTip = false;

new Vue({
  router,  // 路由
  store,   // 全局vuex
  render: (h) => h(App),
}).$mount('#app');

参考文章

《Vue3.0 前的 TypeScript 最佳入门实践》