TypeScript基础(五):TS Vue 项目安装,装饰器的作用、原理和实现,TS 在 Vue 中的用法

我们先创建一个新的 vue 项目:

然后会出来三个选项:

第一个选项是你之前的个人配置。如果你已经用过 vue-cli 创建项目,并且保存了配置项,它就会出来第一个。

第二个选项 default,是默认配置。

第三个选项 Manually select features 是手动配置。

如果我们要用 ts 的话,就选择:Manually select features。

然后它就会出来一些配置给你选择:

我们选完之后,回车。

接下来,它会问你,要不要 class 风格的组件。

我们原先的风格是 let app = new Vue()

class 风格就是 class App extends Vue { ... }

这里我们当然选择 Yes。

然后它就会问你要不要单独的去做 ts 的 polyfills。

我们也可以要。

然后会继续问你要不要 history 的路由模式,这里可以要。

然后会问你使用什么样的 css 预编译器,这里我们选 Less。

然后会问你代码格式化检测你选哪种?这里我们当然选 TSLint。

然后这里是选择代码的语法检查方式。

Lint on sava 是保存就检测。

Lint and fix on commit 是 fix 和 commit 的时候检测。

这里我们可以不需要。

然后它会问你,Babel、ESLint、etc. 这些文件的配置是单独的一个文件配置,还是都放到 package.json 里面。

我们这里选单独的配置文件。

然后这里它会问你,是否需要保存上面的所有配置。

如果你保存了,就是最开始 vue create xxx 的时候,出现的第一个配置项,可以看到我上面的名字是 mumu。

然后在以后的项目中,你就可以直接根据这次的配置去生成项目了。

这里我就选 No 了。

然后它就会自动的去下载依赖模块了。

这里我用的是 Vue CLI 最新的 4.1.2 版本。

接下来,我们先说 ts 里面一个及其重要,及其实用的一个东西,就是 ts 的装饰器。

其实装饰器不算是一个语法角度的东西,它其实是一种设计模式

那么 ts 的装饰器是干什么的呢?

ts 的装饰器可以为一个类,也就是 class,来附加一些功能

其实在 Vue CLI 的 ts 项目中,它自己就带了一个装饰器。

@Coponent 的 @ 符号就是装饰器。

Vue 的作者尤大大其实想推广2个东西:

1,他想推广 ts,他希望用 vue 的人都能拿 ts 去写项目。

2,他希望你用 class 来写组件。这是因为他其实在逐渐的像一个方向靠拢,就是他希望 vue 能够越来越多的变成一个程序应有的写法。

如果你在最开始 Vue CLI 3.x 的时候就用了 ts,应该就会很清楚。

那么我们回到正题,这里为什么会有 2 个这样的东西呢?

@Component
export default class HelloWorld extends Vue { }

这个其实只是一个过渡期的形式。

也就是说,只要你愿意,你仍然可以在 @Component 里面写,就像原来的 vue 一样

然后你也可以写在 class 里面:

这两种写法,vue 都是认可的。

所以 @Component 的存在其实并不是必须的。

它的作用其实是尤大大希望它能够帮我们来完成 vue 的过渡。

因为,毕竟用这个东西的人,大部分都是有 vue 经验的,所以他给你提供了一种方案:你依然可以像原来一样写 vue,然后你也可以用类的方式来写。

而且,它们两个还是共存的。

共存的意思就是:虽然 aaa 和 bbb 它们是分散在两边来写的,如果需要的话,其实在 template 里面,aaa 和 bbb 照样还是可以用的。

上面我们说了 @ 符号,就是装饰器,它可以帮助我们给类去装饰,去附加东西。

那么这里的 @Component 装饰器,它装饰的就是自己下面那个 HelloWorld 这个 class。

那么我们了解了装饰器之后,它怎么用呢?

装饰器听起来高大上,其实原理非常的简单。

装饰器它其实就是个函数。

其实这个 fn 你就可以当做一个装饰器。

它的用法,就是加一个 @ 符号:

然后我们编译的时候,就可以看到它报错了。

第一个报错是说,你的 fn 缺少参数,我是要给你传参的,但是你没有接收。

第二个报错是插件的冲突问题,这里和后面编译的时候都可以忽略。解决方法是创建一个 tsconfig.json 或 jsconfig.json 文件,配置:

{"compilerOptions": {"experimentalDecorators": true}} 就可以了。

那么参数没有接收是什么意思呢?

因为装饰器这个东西,它毕竟是需要干活的,所以在这个时候,它会给你传一个参数进来。

这个参数,就是你要去处理的那个 class

那么我们给 fn 添加一个参数,并打印出来看看是什么:

可以看到,函数 fn 接收的参数,就是一个 function User 。也就是 class User 的这个构造器本身

那么我们就可以给它加东西了:

但是编译后,它会报错,说 User 身上并没有 a 这个东西。

其实,ts 它一定是要在编译期就得有这个东西。

但是运行时是没有问题的。

可以看到,上图中 12 已经出来了。

但是 ts 检查不到。

所以装饰器的用法不是这么简单,直接往上一放就完事的,这个是过不了 ts 的语法检查的。

其实装饰器不止能用于 class,它还能用于属性或者方法

我们可以先给一个属性加上:

可以看到,它又报错了。原因也是因为参数不对。

因为如果你是给类去加一个装饰器,它确实只有一个参数,就是构造函数,也就是类本身

但如果我们去给属性加的时候,这里它报错,是说签名对不上。其实它这里的提示并不全面,就只会说签名对不上。

其实我们的 fn 函数里面现在要接受 3 个参数

第 1 个参数,具体的对象。它是一个 User 类型。

第 2 个参数,你这个属性的名字。它是一个 string 类型。

第 3 个参数,这个属性的值。它是一个 any 类型。

可以看到 value 是 undefined。

原因是我们现在加这个装饰器的时候,它的实例是没有建出来的,所以它就没有 name 这个值。

然后我们现在给方法加上:

然后它也会报错,原因也是因为参数不对。

然后我们加上参数,并打印出来:

那如果我们想自己实现一个装饰器该怎么做呢?

比如我们在上面最开始举的例子:

现在我们有一个装饰器 addA,还有一个类 User。

我们希望它可以帮助这个类,添加一个 a 属性。

首先,我们上面说过,给类添加装饰器,它这里接收的参数就是一个构造函数,也就是类本身。

那么我们怎么通过构造函数 constructor 来给它加个 a 呢?

我们可以直接通过 constructor.prototype.a 这样的方式来加

因为我们都知道所有的类,都是有原型的,那有了原型,自然就能加东西。

我们打印 obj.a 的时候却报错了。但是 12 却可以打印出来。

报错原因很简单,它说在 User 上面,并没有 a 这个属性。

为什么不存在?因为我们并没有申明这个 a 啊。

所以,我们需要申明一下:

所以,如果你想要去做这个事的话,就需要让它这个东西,本身要声明一下,就可以了。

但是现在会有一个问题,这个 a 我们是写死的 12。

所以我们希望 addA 它是可以传一个参数的。那么我们就在后面添加一个参数 num: number。

然后你会发现它报错了,说 addA 这个函数期待 2 个参数,但是我只得到了 1 个。

这里就很奇怪了,你看 @addA(12) 和 @addA(5) 明明我们都传了啊?

这里需要注意一下,因为实际上装饰器它真的只能接受一个参数。

我们刚才是直接这样用的:

@addA
class User {  }

我们并没有在 @addA 后面加什么东西,说白了,就是我们根本就没把它当函数用。

所以,直接在 addA 函数后面加第二个参数是不行的。

其实我们可以利用高阶函数,也就是对外,我们返回一个函数。

这是因为 @addA 不加括号就能执行,那如果我们在加一个括号,它其实就是给里面那个函数的。

但是它又报错了,说 12 和 5,你是不能放到 Function 类型里面的。

现在就很奇怪了,我们传的参数 12 和 5,明明是给内层函数的。

但它报的错,却说我们的参数赋值给到外层函数去了。

其实装饰器参数是这样的,我们这里的 addA,

它外层接收的是 num: number。

内层接收的是 constructor: Function。

@addA(12)
class User { }

它会自动的帮我们编译成:

@addA(12)(User)
class User { }

所以装饰器其实并不神奇,它就是一个 function,并且是由 ts 来调用。

我们写的 @addA(12) 其实它会自动的帮我们变成 @addA(12)(User)。

这个就是装饰器的本质。

发布了62 篇原创文章 · 获赞 3 · 访问量 4774

猜你喜欢

转载自blog.csdn.net/weixin_43921436/article/details/104082531