TypeScript基础
1、环境安装于运行
基本概念
- ts要先编译转换成js,才能交给浏览器运行;就类似 Less 要转换成 CSS 一样
- 全局安装 ts
npm i -g typescript
// 检验是否安装成功
tsc -v
- tsc作用:负责将 ts 代码转为浏览器和 nodejs 能识别的 js 代码
// 手动敲击命令行编译
tsc 需要编译的ts文件
配置自动编译
- 运行tsc --init,生成 tsconfig.json 文件
- 修改 tsconfig.json 文件,设置存放转换成的js文件夹:
"outDir": "./js/" - 设置 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 值域中排除 null 和 undefined
例子:
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 脚手架
- 安装vue/cli(要3.0版本以上的才可以)
npm i -g @vue/cli
- 创建 vue 项目
vue create 项目名
- 安装 vue/typescript
vue add @vue/typescript
vue-cli 3.0 版本与3.0以下版本在搭建项目时的区别:
- 3.0 版本的文件目录少了很多 eg:
build、config - 那么如何像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,不过目录都差不多):


├─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 ...
上面代码可以看出来:
- 只定义了两个函数:
test和func,没有调用它们。 - 如果没有
@test,运行应该是没有任何输出的。
但是,解释器读到函数修饰符“@”的时候,后面步骤会是这样:
- 去调用
test函数,test函数的入口参数就是那个叫func的函数;
vue-property-decorator和vuex-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');