如何用TypeScript编写Vue项目
发布于 3 年前 作者 this-long 1081 次浏览 来自 客户端测试

# TypeScript

TypeScript 是 JavaScript 的一个超集,大家可以理解为是 JavaScript 的另一种写法。它可以通过 TypeScript 编译器或 Babel 转译为 JavaScript 代码。

参考文档 龙的读书笔记

## TypeScript 的优缺点

优点:

- 类型系统实际上是最好的文档 - 在代码编写时就会提示大部分错误 - 大大增强了编辑器和开发工具的功能,有强大的代码补全和提示 - 他是 JavaScript 的超集,可以直接讲 js 文件重命名为 ts 文件 - 既是编译器提示了错误,但是仍然可以生成 JavaScript 文件


缺点:

- 有一定的学习成本,他的接口、泛型等概念对于前端工程师理解起来可能有困难 - 短期开发会增加开发成本,由于有各种类型声明

## TypeScript 与 Javascript 的区别

TypeScript 中的数据要求带有明确的类型,JavaScript 不要求。

let a: string = "hello";
console.log(a.length);


// 编译成 JS 后的代码为


var a = "hello";
console.log(a.length);

TypeScript 中变量被限制了类型之后,就无法访问该类型中不存在的属性或方法,但 js 中可以

// 一段可以执行的js
let a = 100;
if (a.length !== undefined) {
  console.log(a.length);
} else {
  console.log("no length");
}


// 我们将上面的js用ts重写
let a: number = 100;
if (a.length !== undefined) {
  // error TS2339: Property 'length' does not exist on type 'number'.
  console.log(a.length);
} else {
  console.log("no length");
}
// 在ts中变量被声明类型后就不能使用不属于它的方法,否则会报错

## 基础数据类型

除了经典的:布尔值 数字 字符串 数组 null underfined object TypeScript 还有:元组 枚举 any void Never

TypeScript 声明时定义了类型,后续使用中不可以改变类型

//布尔值
let bool: boolean = true;


//数字
const num: number = 1;


//字符串
const username: string = "longbao1";


//数组
let arr: number[] = [1, 2, 3]; //这是一个数字数组
let arr1: string[] = ["1,2,3"]; //这是一个字符串数组


//数组泛型写法
let arr2: Array<string> = ["1"];


//元组
// 元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同
let x: [string, number] = ["hello", 10];


//枚举类型
enum Color {Red, Green, Blue}
let c: Color = Color.Green;//一个序号和值相互对应的对象。
console.log(c);


上面仅仅是 ts 中一些简单的类型,还有一些比较复杂的类型,大家可以自己了解。 参考文档

听到这里小伙伴们可能已经感觉用 TypeScript 来编写程序好麻烦,各种声明类型。实际上在 TypeScript 里存在类型推论。也就是说在有些没有明确指出类型的地方,类型推论会帮助提供类型。在有些时候我们可以省略掉对类型的声明。

npm install -g typescript全局安装 ts 编译工具 tsc 文件名.ts在文件目录内运行命令行,将 ts 文件编译为 js 文件

## 函数

上面我们理解了如何定义一个变量,下面简单的介绍一下如何声明一个函数

//定义函数参数类型
function add(x: number, y: number): number {
  //返回值类型
  return x + y;
}


//添加?来声明可选参数
function buildName(firstName: string, lastName?: string) {
  if (lastName) return firstName + " " + lastName;
  else return firstName;
}

## 其他

在 TypeScript 中还有许多的知识,例如:接口、类、继承、泛型、装饰器。由于时长的限制可能就不进行逐一介绍了,简单介绍下面可能用到的一些。

## Vue 与 typescript

详见git 文档

由于 Vue2 底层的缘故,可能对 ts 的使用并不非常友好,在代码提示上还不够方便强大。

<!-- ts 的 Vue 项目与 js 的 Vue 项目的区别还是较大的,写法变化较大。 -->

### 开始一个项目

改造现有的 vue 项目

- vue create vue_and_typescript - npm i vue-class-component vue-property-decoratornpm i ts-loader typescript tslint tslint-loader tslint-config-standard - 然后进行一系列的配置

包说明: vue-class-component vue 官方的 ts 支持包 vue-property-decorator 对 ts 支持包更好封装的装饰器 ts-loader webpack ts 解释器 typescript ts 核心 tslint 语法检查器 tslint-loader webpack 语法检查器 tslint-config-standard 语法规则包


创建新的 vue 项目

- 在创建项目时手动创建,选中 TypeScript 这一项,就可以创建出支持 ts 的 vue 项目。


文件构造:

- shims-tsx.d.ts :ts 的声明文件,在 ts 可以写 jsx - shims-vue.d.ts 声明在 ts 导入 vue 组件 - tsconfig.json 配置文件

// 文件结构
<template>
  <div id="app">
    <HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
  </div>
</template>


<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import HelloWorld from './components/HelloWorld.vue';


//Component 装饰器,这个装饰器即便没有内容也不能删除
[@Component](/user/Component)({
  // 组件注册
  components: {
    HelloWorld,
  },
export default class App extends Vue {
}
})
</script>

### 装饰器

类装饰器

//装饰器内返回一个函数一般就可以传参
function demoDecorator(countValue: number) {
  return function (target: any) {
    // console.log();
    target.prototype.count = countValue;
  };
}


@demoDecorator(1000) //装饰器直接写在类的前面,就可以装饰这个类
class Greeter {
  greeting: string;
  constructor(message: string) {
    this.greeting = message;
  }
}


const greeter: any = new Greeter("lucy");
console.log(greeter);


// 返回值
// Greeter
// greeting: "lucy"
// __proto__:
// count: 1000
// constructor: ƒ Greeter(message)
// __proto__: Object

方法装饰器

//targetPrototype 类的原型对象、methodName方法名,方法的描述、desc.value就是方法本身
function showPonitDec(param?: string) {
  return function (targetPrototype: any, methodName: any, desc: any) {
    // 首先将获取到的函数存储下来
    const oDescValue = desc.value;
    //替换方法本身。参数传递
    desc.value = function (...arg: any[]) {
      //判断是否传递了事件名、没传递就是事件名相同省略了,
      const eventName = param ? param : methodName;
      console.log(eventName);
      //接收父级传递的自定义函数,这是[@Emit](/user/Emit)的核心
      this.$emit(eventName, ...arg);
      // 运行函数本身代码,实现了函数合并的效果
      oDescValue.apply(this, arg);
    };
  };
}
class Point {
  x: number;
  y: number;
  constructor(x: number, y: number) {
    this.x = x;
    this.y = y;
  }
  //没有传递事件名
  @showPonitDec()
  //传递了参数
  showPoint(id: string) {
    console.log("old1" + id);
  }
}
const point = new Point(1, 2);
point.showPoint("dsvfsdfh");

属性装饰器

// 属性装饰器
function Prop(param?:string){
    // targetPrototype 类的原型对象
    // propName 属性名
    return function(targetPrototype:any,propName:any){
        //可传可不传
        targetPrototype[propName]=param?param:propName
            // 接收prop的值并赋值,实现[@Prop](/user/Prop)的功能
            // targetPrototype[propName]=this[param?param:propName]
    }
}


class ColorPoint{
    x:number
    y:number
    [@Prop](/user/Prop)('col1or11') color!:string
        constructor(x:number,y:number){
            this.x=x
            this.y=y
        }


}
const colorPoint =new ColorPoint(1,2)
console.log(colorPoint.color);

而我们刚刚见到的@Component 其实就是一个类装饰器,将组件装饰到的它下面的对象上。

### ts 中 vue 各部分写法

#### ts 中的 prop 传递数值

//传递
    <TodoWrap title="i am title" :count.sync="count" />


//需要在接收prop的页面导入prop
import { Component, Vue, Prop, PropSync } from "vue-property-decorator";
[@Component](/user/Component)
export default class TodoWrap extends Vue {
  [@Prop](/user/Prop)(String) title!: string;
  [@PropSync](/user/PropSync)("count", { type: Number }) syncedCount!: number;
//接收的变量名以及类型


  //在ts中定义函数直接定义,不需要有 methods
  changecount() {
    this.$emit("update:count", 20000);
  }
}

实际上@Prop 和@PropSync 就是属性装饰器

#### 定义函数,传递函数

//App定义函数传给下级组件
export default class App extends Vue {
  count = 100;
  //定义函数
  handleClick(value: number) {
    console.log(value);
    this.count = value;
  }
}
//  @handle-click="handleClick" 传递需要用驼峰


//组件接收函数
 [@Emit](/user/Emit)("handle-click")
  changeCountNew(value: number) {}
  //一个函数对应一个Emit,Emit也需要导入
  // 可以理解为将两个函数合并了

#### 生命周期

[@Component](/user/Component)({
created(){}
})


#### @Watch

侦听器

  //可以对一个数据监听多次,相互之间不会覆盖
  [@Watch](/user/Watch)("question", { immediate: true /*(监听配置) */ })
  //监听函数
  onQuestionChange2(newValue: string, oldValue: string) {
    console.log(newValue);
  }

### 小结

装饰器语法的使用 @Component :

- 内部可以写组件注册 - 生命周期 - 计算属性

@Prop @Emit @PropSync @Model @VModel(与 ModelSync 功能相似) @ModelSync @Watch

回到顶部