前端知识库(lizh)
  • README
  • Bugs
    • 前端调试随笔
    • 浏览器常见问题概览
    • 浏览器兼容问题概览
    • HTML常见问题概览
    • CSS常见问题概览
    • JS常见问题概览
    • 移动端兼容性问题概览
    • 微信小程序开发
    • NodeJs常见问题概览
    • Mac常见问题概览
    • 微信开发遇坑指南
    • Npm包常见问题概览
    • 其他问题汇总
  • Css探索系列
    • CSS基础知识
    • CSS常见问答
    • CSS常见问答02
    • CSS应用示例
    • CSS应用示例02
    • 由Z Index引发的层叠上下文思考
    • 由浮动塌陷引发的块级格式上下文思考
    • CSS探索系列 Flex布局
    • CSS探索系列 Margin
    • CSS探索系列 Auto关键字
    • CSS探索系列 Gradient
    • CSS探索系列 Line Height
    • CSS探索系列 元素居中
    • CSS探索系列 动画
    • 为什么使用PostCSS处理CSS?
    • 重新认识伪类与伪元素
    • 自定义表单伪元素样式
    • 如何理解Css中的Display属性
    • 视口和软键盘对视口的影响
    • 关于Css
  • Frontend
    • 00 关于Web前端
    • 01 前端知识概览
    • 02 常用前端库概览
    • 基础 00 前端常见问题01
    • 基础 01 浏览器缓存
    • 基础 02 浏览器工作原理
    • 基础 03 谈谈前端跨源问题及解决方法
    • 进阶 01 Web性能优化
    • 进阶 02 搜索引擎优化(SEO)
    • 进阶 03 前端模块化编程
    • 进阶 04 规范代码:Linter、Prettier、EditorConfig
    • 进阶 11 前端自动化测试
    • 高级 01 前端安全
    • Vue2.X原理篇
    • Vue3初步了解及迁移指南
    • 重读Vue教程
    • React17.X原理篇
    • 你必须知道的React问题
    • 重读React教程
    • 聊一聊Cookie的一些问题
    • 如何理解HTTP响应的状态码
    • HTTP的历史演变及概述
    • Webpack4.X原理篇
    • Webpack基础入门篇
    • Webpack构建优化篇
    • TypeScript使用指南
    • 代码规范
      • 前端规范
      • HTML
      • CSS
      • JS
  • Html探索系列
    • HTML基础知识
    • HTML基础知识02
    • HTML常见问答
    • HTML经典实践用例
    • HTML元素的宽高及位置详解
    • Video元素的使用和常见问题总结
    • Html探索系列 Meta标签
    • DOCTYPE:文档类型与浏览器模式
    • DHTML(动态网页)简介
    • HTML标签详解
    • HTML布局的几种方式
    • HTML全局属性
    • 关于Html
  • Js探索系列
    • 基础知识
    • 常见问答
    • 应用示例
    • 趣味示例
    • 基础篇 05 AJAX
    • 基础篇 06 Window对象
    • 基础篇 07 Error、JSON、Math、Console对象
    • 基础篇 08 History、URL、Screen、Navigator、Location对象
    • 基础篇 09 文档对象模型(DOM)
    • 基础篇 10 Document对象
    • 基础篇 11 Element对象
    • 基础篇 12 Event对象
    • 基础篇 13 键盘、鼠标、触摸事件
    • 基础篇 15 CSS对象模型(CSSOM)
    • 进阶篇 01 Prototype对象和继承
    • 进阶篇 02 Promise对象
    • 进阶篇 07 迭代器(Iterator)
    • 进阶篇 08 Generator和Async函数
    • 进阶篇 09 JavaScript异步编程
    • Date对象和日期时间字符串格式
    • Canvas基础入门篇
    • Canvas进阶篇
    • SVG基础入门篇
    • 四种判断数据类型方法的优缺点
    • 深入理解JavaScript的浅拷贝和深拷贝
    • 谈谈JavaScript的作用域和上下文
    • 复制内容到剪贴板
    • 关于Javascript
  • NodeJs
    • 关于Node.Js
    • Node.Js:三种调试方法
    • Npm包管理器简介及一些机制
    • NPM:Package.Json详解(中文)
    • NPM:从零开始,开发一个软件包
    • NPM:常用命令
    • Node.Js:Fs(文件系统)
    • Node.Js:Global(全局变量)
    • Node.Js:HTTP
    • Node.Js:Module(模块)
    • Node.Js:Path(路径)
    • Node.Js:Readline(逐行读取)
  • Research
    • 极细边框(1px边框)实现方式
    • 如何监控前端异常?
    • H5页面跳转和刷新
    • Web主题切换和个性化定制方法总结
    • Vue SSR(服务端渲染)的简单实现
    • 基于Create React App打造代码规范化的React+Ts项目
    • H5可视化编辑
    • Web常用功能
    • Javascript加密混淆
    • Vue如何导入TypeScript
    • 移动端PDF预览
    • 纯CSS绘制箭头
    • 网站性能测量和优化方法
  • Tech
    • GOOGLE浏览器的搜索技巧
    • Curl的用法指南
    • Sublime3插件篇
    • Charles安装及使用
    • Nginx基础使用
    • 排序算法(Javascript)
    • 代码整洁之道(摘录笔记)
    • Java的24种设计模式与7大原则
    • 观察者和发布订阅模式
  • Tools
    • Git
      • Git基础教程
      • Git常见问题
    • Gitbook
      • Gitbook入门篇
      • Gitbook插件篇
      • Gitbook进阶篇
由 GitBook 提供支持
在本页
  • 什么是Typescript?
  • 为什么要用TypeScript?
  • Vue 引入 TypeScript
  • 配置 webpack
  • 添加 tsconfig.json
  • 添加 tslint.json
  • 让 ts 识别 .vue
  • 改造 .vue 文件
  • Vue常见选项的转换
  • @Component
  • Mixins
  • model
  • $emit
  • ref
  • 总结
  • 参考链接

这有帮助吗?

  1. Research

Vue如何导入TypeScript

上一页Javascript加密混淆下一页移动端PDF预览

最后更新于3年前

这有帮助吗?

什么是Typescript?

是微软开发一款开源的编程语言,它是 JavaScript 的一个超集,本质上是为 JavaScript 增加了静态类型声明。任何的 JavaScript 代码都可以在其中使用,不会有任何问题。TypeScript 最终也会被编译成 JavaScript,使其在浏览器、Node 中等环境中使用。由于最终运行的仍然是 JavaScript,所以 TypeScript 并不依赖于环境的支持,也并不会带来兼容性问题。

同时,TypeScript 也是 JavaScript ES6 的超集。

Vue3.0 将使用 TS 重写,重写后的 Vue3.0 将更好的支持 TS。

Google 的 Angular 2.0 也宣布采用 TypeScript 进行开发。

2019 年 TypeScript 将会更加普及,能够熟练掌握 TS,并使用 TS 开发过项目,将更加成为前端开发者的优势。

为什么要用TypeScript?

TypeScript 的类型注解是一种轻量级的为函数或变量添加约束的方式,它的优势在于静态类型检查。

概括来说主要包括以下几点:

  1. 静态类型检查

    即你编写的代码即使没有被执行到,一旦你编写代码时发生类型不匹配,语言在编译阶段即可发现。

  2. IDE 智能提示

    在TypeScript 这一类语言之前,JavaScript 的智能提示基本完全依赖 IDE 提供的猜测,这种猜测可能并不正确,并且也缺乏更多的辅助信息。TypeScript 不仅自己写的类库有丰富的类型信息,也可以对其他纯 JS 项目进行类型标注 (),便于使用者直接在 IDE 中浏览 API,效率大增。

  3. 代码重构

    有时候需要修改一些变量/属性/方法名,牵涉到属性和方法的时候,很多改动是跨文件的,不像普通变量可以简单定位 scope,属性方法名的重命名对于 JS 来说异常痛苦,一方面是修改本身就不方便,另一方面是改了还不确定该改的是不是改了,不该改的是不是也改了。

    而 TypeScript 的静态类型系统就可以较为完美的解决这个问题。

  4. 可读性

    类型就是最好的注释。对于阅读代码的人来讲,各种便利的类型一目了然,更容易明白作者的意图。

常见的变量类型定义:

// 布尔值
let isDone: boolean = false; // 相当于 js 的 let isDone = false;
// 变量定义之后不可以随便变更它的类型
isDone = true // 不报错
isDone = "我要变为字符串" // 报错

// 数字
let decLiteral: number = 6; // 相当于 js 的 let decLiteral = 6;

// 字符串
let name: string = "bob";  // 相当于 js 的 let name = "bob";

// 数组
 // 第一种,可以在元素类型后面接上 [],表示由此类型元素组成的一个数组:
let list: number[] = [1, 2, 3]; // 相当于 js 的let list = [1, 2, 3];
// 第二种方式是使用数组泛型,Array<元素类型>:
let list: Array<number> = [1, 2, 3]; // 相当于 js 的let list = [1, 2, 3];

// 在 TypeScript 中,我们使用接口(Interfaces)来定义 对象 的类型。
interface Person {
    name: string;
    age: number;
}
let tom: Person = {
    name: 'Tom',
    age: 25
};
// 以上 对象 的代码相当于 
let tom = {
    name: 'Tom',
    age: 25
};

// Any 可以随便变更类型 (当这个值可能来自于动态的内容,比如来自用户输入或第三方代码库)
let notSure: any = 4;
notSure = "我可以随便变更类型" // 不报错
notSure = false;  // 不报错

// Void 当一个函数没有返回值时,你通常会见到其返回值类型是 void
function warnUser(): void {
    console.log("This is my warning message");
}

// 方法的参数也要定义类型,不知道就定义为 any
function fetch(url: string, id : number, params: any): void {
    console.log("fetch");
}

不要使用如下类型Number,String,Boolean或Object, 这些类型指的是非原始的装盒对象,它们几乎没在JavaScript代码里正确地使用过。应该使用类型number,string,and boolean。

使用非原始的object类型来代替Object

不确定的类型用Any,它是可以随便变更类型的

Vue 引入 TypeScript

现有的项目,首先需要安装一些必要/以后需要的插件:

# 安装vue的官方插件
npm i vue-class-component vue-property-decorator -S

#ts-loader typescript 必须安装,其他的相信你以后也会装上的
npm i ts-loader typescript tslint tslint-loader tslint-config-standard -D

配置 webpack

  • 首先找到./build/webpack.base.conf.js。

  • 将entry中的入口文件xx.js改成xx.ts,顺便把项目文件中的xx.js也改成xx.ts,里面内容保持不变。

  • 找到resolve.extensions 里面加上.ts 后缀 (是为了之后引入.ts的时候不写后缀)

  • 找到module.rules 添加webpack对.ts的解析

    //webpack.base.conf.js
    module.exports = {
    	entry: {
    		'bxs-docs': './docs/docs.ts',
    		'bxs-demo': './docs/demo.ts'
    	},
    	...
    	resolve: {
    		//默认解析扩展路径,设置完成后再引入文件后可以节约后缀名
    		extensions: ['.js', '.vue', '.css', '.ts', '.md'],
    	},
    	module: {
    		rules: [
    			{
    				test: /\.ts$/,
    				exclude: /node_modules/,
    				enforce: 'pre',
    				loader: 'tslint-loader'
    			},
    			{
    				test: /\.tsx?$/,
    				loader: 'ts-loader',
    				exclude: /node_modules/,
    				options: {
    				appendTsSuffixTo: [/\.vue$/],
    				}
    			},
          		...
    		]
    	},
    	...
    }

ts-loader 会检索当前目录下的 tsconfig.json 文件,根据里面定义的规则来解析.ts文件(就跟.babelrc的作用一样)。tslint-loader 作用等同于 eslint-loader

添加 tsconfig.json

{
  // 编译选项
  "compilerOptions": {
    // 输出目录
    "outDir": "./output",
    // 是否包含可以用于 debug 的 sourceMap
    "sourceMap": true,
    // 以严格模式解析
    "strict": true,
    // 采用的模块系统
    "module": "esnext",
    // 如何处理模块
    "moduleResolution": "node",
    // 编译输出目标 ES 版本
    "target": "es5",
    // 允许从没有设置默认导出的模块中默认导入
    "allowSyntheticDefaultImports": true,
    // 将每个文件作为单独的模块
    "isolatedModules": false,
    // 启用装饰器
    "experimentalDecorators": true,
    // 启用设计类型元数据(用于反射)
    "emitDecoratorMetadata": true,
    // 在表达式和声明上有隐含的any类型时报错
    "noImplicitAny": false,
    // 不是函数的所有返回路径都有返回值时报错。
    "noImplicitReturns": true,
    // 从 tslib 导入外部帮助库: 比如__extends,__rest等
    "importHelpers": true,
    // 编译过程中打印文件名
    "listFiles": true,
    // 移除注释
    "removeComments": true,
    "suppressImplicitAnyIndexErrors": true,
    // 允许编译javascript文件
    "allowJs": true,
    // 解析非相对模块名的基准目录
    "baseUrl": "./",
    // 指定特殊模块的路径
    "paths": {},
    // 编译过程中需要引入的库文件的列表
    "lib": [
      "dom",
      "es2015",
      "es2015.promise"
    ]
  }
}

添加 tslint.json

在根路径下创建tslint.json文件。这里就很简单了,就是 引入 ts 的 standard 规范

{
  "extends": "tslint-config-standard",
  "globals": {
    "require": true
  }
}

让 ts 识别 .vue

由于 TypeScript 默认并不支持 *.vue 后缀的文件,所以在 vue 项目中引入的时候需要创建一个 vue-shim.d.ts 文件,放在项目对应使用目录下,例如 src/vue-shim.d.ts

declare module "*.vue" {
  import Vue from "vue";
  export default Vue;
}

//全局变量/方法声明
declare global {
  interface Window {
    SystemJS: any; // 如果不确定类型, 可定义为any
  }
}

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

而在代码中导入 *.vue 文件的时候,**需要写上 .vue 后缀。**原因还是因为 TypeScript 默认只识别 *.ts 文件,不识别 *.vue 文件:

import abcd from 'components/abcd.vue'

改造 .vue 文件

  • import .vue 的文件的时候,**要补全 .vue 的后缀,**否则会提示语法错误或找不到模块

  • 在script 标签上加上 lang="ts", 意思是让webpack将这段代码识别为typescript 而非javascript

    • components,注册子组件

    • filters,过滤器

    • directives,注册或获取全局指令。

    • ...

    • @Emit

    • @Inject

    • @Model

    • @Prop

    • @Provide

    • @Watch

Vue常见选项的转换

template、style模块不需要改,只需对script进行转换。

<template>
    <div></div>
</template>

<script lang="ts">
import Abcd from './Abcd.vue'
import { Vue, Component, Emit, Inject, Model, Prop, Provide, Ref, Watch, PropSync } from 'vue-property-decorator'
//或者
//import Vue from 'vue'
//import Component, { mixins } from 'vue-class-component'
//import { Emit, Inject, Model, Prop, Provide, Watch, PropSync, Ref } from 'vue-property-decorator'

// 一定要用Component修饰。详情查看下一章节
@Component({
    components: {
        Abcd
    }
})

//Demo相当于原来的name: 'Demo'
export default class Demo extends Vue {
    //data,直接写
    count: number = 0
    desc: string = '123'
    private tx: string = ''
	//用 public, private 等修飾詞來決定能不能從外部 (別的 class) 存取

	// @Prop(options: (PropOptions | Constructor[] | Constructor) = {})
    // @Prop装饰器接收一个参数,这个参数可以有三种写法:
    // Constructor,例如String,Number,Boolean等,指定 prop 的类型;
    // Constructor[],指定 prop 的可选类型;
    // PropOptions,可以使用以下选项:type,default,required,validator。
    @Prop()
    propA: number = 1

	// readonly是typeScriptr的属性,指定为只读
	// 感叹号是非null和非undefined的类型断言。如果不加,TypeScripts可能会报错"从未被初始化过"
    @Prop({
        type: [String, Number], //构造器
        default: 'default value', //默认值
        required: true, //必传
        validator() {} //检验器
    })
	readonly propA!: string | number
    //类型为Object,设置为any
    @Prop({
        default: () => {B
            return {}
        }
    })
    propB: any

	// @PropSync(propName: string, options: (PropOptions | Constructor[] | Constructor) = {})
    // @PropSync装饰器与@prop用法类似,二者的区别在于,@PropSync 装饰器接收两个参数:
    // propName: string 表示父组件传递过来的属性名;
    // options: Constructor | Constructor[] | PropOptions 与@Prop的第一个参数一致;
    // @PropSync 会生成一个新的计算属性。
    // @PropSync需要配合父组件的.sync修饰符使用
    @PropSync('name', { type: String })
    syncedName!: string

    // methods 直接在此定义
    private add() {
    	this.number++
    }
    
    // 生命周期直接定义为同名屬性
	created () {}
    mounted () {}

    // 计算属性
    get msg () {
        return 'computed ' + this.demo
    }

    // @Watch(path: string, options: WatchOptions = {})
    @Watch('child', {
        immediate: true,
        deep: true
    })
    onChildChanged (val: string, oldVal: string) {
        if (val !== oldVal) {
            console.log(val)
        }
    }
}
</script>

以上代码等价与:

<template>
    <div></div>
</template>
<script>
    
export default {
	name: "Demo",
    components: {
        Abcd
    },
	data () {
		return {
        	count: 0,
            desc: '123',
            private tx: ''
		}
	},
	props: {
		number: {
			type: Number,
            default: 1
		},
        propA: {
            type: [String, Number], //构造器
            default: 'default value', //默认值
            required: true, //必传
            validator() {} //检验器
        },
        propB: {
			type: Object,
            default: () => {B
            	return {}
            }
        },
		// @PropSync中定义的
        props: {
            name: {
                type: String
            }
        }
    },
	created () {},
    mounted () {},
	computed: {
        // @PropSync生成的计算属性
		syncedName: {
			get() {
                return this.name
            },
			set(value) {
				this.$emit('update:name', value)
			}
        },
		msg: {
			return 'computed ' + this.demo
		}
    },
	watch: {
        child: {
            immediate: true,
			deep: true,
			handler: 'onChildChanged'
        }
    },
	methods: {
		onChildChanged (val: string, oldVal: string) {
            if (val !== oldVal) {
                console.log(val)
            }
        }
    }
}
</script>

@Component

.vue文件中一定要用Component修饰,参数可不传。

@Component配置 vue-property-decorator不支持的属性,如: components,filters,directives等

<template>
  <span v-demo:foo.a="1+1">test</span>
</template>

<script lang="ts">
    import Abcd from './Abcd.vue'
    import Component from 'vue-class-component'
    @Component({
        components: {
            Abcd
        }, 
        filters: {},
        directives: {
            demo: {
				bind(el:any, binding:any, vnode:any, oldVnode: any) {
				}
            }
		}
    })
    export default class App extends Vue {}
</script>

Mixins

在TypeScript中, 我们可以这么写:

// 定义要混合的类 mixins/index.ts
import {Vue, Component} from 'vue-property-decorator'

// 一定要用Component修饰
@Component
export default class myMixins extends Vue {
    mixinVal: string = 'Hello Mixin'
}

然后, 在其他组件中使用它:

<template>
	<div></div>
</template>

<script lang="ts">
    import Vue from 'vue'
    import Component, { mixins } from 'vue-class-component'
    import mixinDemo from './mixin'

    @Component()
    export default class App extends mixins(mixinDemo) {
        mounted () {
            window.console.log('mixinVal => ', this.mixinVal)
        }
    }
</script>

model

model是vue中的一个选项(用得比较少),它允许一个自定义组件在使用 v-model 时定制 prop 和 event。默认情况下,一个组件上的 v-model 会把 value 用作 prop 且把 input 用作 event,但是一些输入类型比如单选框和复选框按钮可能想使用 value prop 来达到不同的目的。使用 model 选项可以回避这些情况产生的冲突。

import { Vue, Component, Model } from 'vue-property-decorator'

@Component
export default class YourComponent extends Vue {
  @Model('change', {
      type: Boolean
  }) readonly checked!: boolean
}

等价于:

export default {
  model: {
    prop: 'checked',
    event: 'change'
  },
  props: {
    checked: {
      type: Boolean
    }
  }
}

$emit

由@Emit修饰的函数,会在它们的原始参数之后,返回一个值。如果返回值是Promise,则在Promise执行后,再执行$emit。

如果事件的名称没有通过事件参数提供,则使用函数名。在这种情况下,camelCase名称将转换为kebabo -case。

如果@emit 对应的方法,还有别的参数,比如点击事件的 event,会在返回值之后。 也就是第三个参数。

import { Vue, Component, Emit } from 'vue-property-decorator'

@Component
export default class YourComponent extends Vue {
  count = 0

  @Emit()
  addToCount(n: number) {
    this.count += n
  }

  @Emit('reset')
  resetCount() {
    this.count = 0
  }

  @Emit()
  returnValue() {
    return 10
  }

  @Emit()
  onInputChange(e) {
    return e.target.value
  }

  @Emit()
  promise() {
    return new Promise(resolve => {
      setTimeout(() => {
        resolve(20)
      }, 0)
    })
  }
}

等价于:

export default {
  data() {
    return {
      count: 0
    }
  },
  methods: {
    addToCount(n) {
      this.count += n
      this.$emit('add-to-count', n)
    },
    resetCount() {
      this.count = 0
      this.$emit('reset')
    },
    returnValue() {
      this.$emit('return-value', 10)
    },
    onInputChange(e) {
      this.$emit('on-input-change', e.target.value, e)
    },
    promise() {
      const promise = new Promise(resolve => {
        setTimeout(() => {
          resolve(20)
        }, 0)
      })
      promise.then(value => {
        this.$emit('promise', value)
      })
    }
  }
}

ref

ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例。

@Ref(refKey?: string)。refKey值可以为String,如果省略传输参数,那么会自动将属性名作为参数。

@Ref与@Emit的区别:@Emit在不传参数的情况下会转为 dash-case,而 @Ref不会转,为原属性名。

import { Vue, Component, Ref } from 'vue-property-decorator'

import AnotherComponent from '@/path/to/another-component.vue'

@Component
export default class YourComponent extends Vue {
  @Ref() readonly anotherComponent!: AnotherComponent
  @Ref('aButton') readonly button!: HTMLButtonElement
}

等价于

export default {
  computed() {
    anotherComponent: {
      cache: false,
      get() {
        return this.$refs.anotherComponent as AnotherComponent
      }
    },
    button: {
      cache: false,
      get() {
        return this.$refs.aButton as HTMLButtonElement
      }
    }
  }
}

另外还有些:@Inject、@Provide,不常见,这里不例举了。。。

总结

vue+webpack导入typeScript,实践过程其实并不复杂:

  • 然后是将typescript安装到项目,新增一些配置文件,在webpack中将其引入;

  • 最后是vue中的一些选项:data、prop、created、methods、watch、computed等的写法转换。

参考链接

提供了内建的 TypeScript 工具支持。如果你是新创建项目,可以查看

:强化 Vue 组件,使用 TypeScript/装饰器 增强 Vue 组件。

:在 vue-class-component 上增强更多的结合 Vue 特性的装饰器

:TypeScript 为 Webpack 提供了 ts-loader,其实就是为了让webpack识别 .ts、 .tsx文件

跟:我想你也会在.ts .tsx文件 约束代码格式(作用等同于eslint)

:tslint 配置 standard风格的约束

接下来在根路径下创建tsconfig.json文件。这里有一份参考配置,完整的配置请点击 :

:对 Vue 组件进行了一层封装,让 Vue 组件语法在结合了 TypeScript 语法之后更加扁平化。主要是配置以下属性:

:在 vue-class-component 上增强了更多的结合 Vue 特性的装饰器,新增了这 7 个装饰器:

首先要对typescript有个初步了解,这个其实很简单,无非是对变量、函数、对象属性加了类型控制,可以查看前文中【常见的变量类型定义】,更详情的查看;

TypeScript
DefinitelyTyped
Vue CLI
TypeScript 支持
vue-class-component
vue-property-decorator
ts-loader
tslint-loader
tslint
tslint-config-standard
tsconfig.json
vue-class-component
vue-property-decorator
TypeScript 中文手册
Vue 如何導入 TypeScript
Vue & TypeScript 初体验
vue + typescript 项目起手式
TypeScript 中文手册