vue输入框限制输入
inb4: This is not another "setting up" a new project with Vue and TypeScript tutorial. Let's do some deep dive into more complex topics!
inb4:这不是另一个使用Vue和TypeScript教程“设置”新项目的方法。 让我们深入研究更复杂的主题!
typescript
is awesome. Vue
is awesome. No doubt, that a lot of people try to bundle them together. But, due to different reasons, it is hard to really type your Vue
app. Let's find out what are the problems and what can be done to solve them (or at least minimize the impact).
typescript
很棒。 Vue
很棒。 毫无疑问,很多人试图将它们捆绑在一起 。 但是,由于不同的原因,很难真正键入您的Vue
应用。 让我们找出问题所在以及可以解决的方法(或至少将影响最小化)。
We have this wonderful template with Nuxt
, Vue
, Vuex
, and jest
fully typed. Just install it and everything will be covered for you. Go to the docs to learn more.
我们有一个非常漂亮的模板 , Nuxt
完整键入了Nuxt
, Vue
, Vuex
和jest
。 只需安装它,一切将为您覆盖。 转到文档以了解更多信息。
And as I said I am not going to guide you through the basic setup for three reasons:
正如我所说,由于以下三个原因,我不会指导您完成基本设置:
There are a lot of tools to get started with a single click like Nuxt
and vue-cli
with typescript
plugin
有很多的工具,开始使用一个单一的点击喜欢Nuxt
和vue-cli
与typescript
插件
We already have wemake-vue-template
where every bit of setup that I going to talk about is already covered
我们已经有了wemake-vue-template
,其中我要谈论的所有设置都已涵盖
The first broken expectation when you start to work with Vue
and typescript
and after you have already typed your class components is that and
tags are still not typed. Let me show you an example:
当您开始使用Vue
和typescript
并在键入类组件之后,第一个破破的期望是和
标记仍未键入。 让我给你看一个例子:
Hello, {{ usr }}!
I have made two typos here: {{ usr }}
instead of {{ user }}
and $style.headr
instead of $style.header
. Will typescript
save me from these errors? Nope, it won't.
我在这里做了两个错别字: {{ usr }}
而不是{{ user }}
和$style.headr
而不是$style.header
。 typescript
会帮助我避免这些错误吗? 不,不会。
What can be done to fix it? Well, there are several hacks.
该如何解决? 好吧,这里有几个技巧。
One can use Vetur
with vetur.experimental.templateInterpolationService
option to type-check your templates. Yes, this is only an editor-based check and it cannot be used inside the CI yet. But, Vetur
team is working hard to provide a CLI to allow this. Track the original issue in case you are interested.
可以将Vetur
与vetur.experimental.templateInterpolationService
选项一起使用,以对模板进行类型检查。 是的,这只是基于编辑器的检查,尚不能在CI中使用。 但是, Vetur
团队正在努力提供CLI以允许这样做。 如果您有兴趣,请跟踪原始问题 。
The second option is two write snapshot tests with jest
. It will catch a lot of template-based errors. And it is quite cheap in the maintenance.
第二种选择是使用jest
两次写快照测试。 它将捕获很多基于模板的错误。 而且它在维护上非常便宜。
So, the combination of these two tools provides you a nice Developer Experience with fast feedback and a reliable way to catch errors inside the CI.
因此,这两种工具的结合为您提供了良好的开发人员体验,并提供了快速的反馈和可靠的方式来捕获CI中的错误。
Typing css-module
s is also covered by several external tools:
键入css-module
s也可以通过几种外部工具来完成:
typings-for-css-modules-loader
为CSS模块输入类型
css-modules-typescript-loader
css-modules-typescript-loader
The main idea of these tools is to fetch css-module
s and then create .d.ts
declaration files out of them. Then your styles will be fully typed. It is still not implemented for Nuxt
or Vue
, but you can tract this issue for progress.
这些工具的主要思想是获取css-module
s,然后.d.ts
创建.d.ts
声明文件。 然后,您的样式将被完全键入。 Nuxt
或Vue
仍未实现该Nuxt
,但是您可以继续解决这个问题 。
However, I don't personally use any of these tools in my projects. They might be useful for projects with large code bases and a lot of styles, but I am fine with just snapshots.
但是,我并没有在项目中亲自使用任何这些工具。 对于具有大型代码库和许多样式的项目,它们可能很有用,但仅快照就可以了。
Styleguides with visual regression tests also help a lot. @storybook/addon-storyshots
is a nice example of this technique.
带有视觉回归测试的样式指南也有很大帮助。 @storybook/addon-storyshots
是此技术的一个很好的例子。
The next big thing is Vuex
. It has some built-in by-design complexity for typing:
下一件大事是Vuex
。 它具有一些内置的设计复杂性来进行键入:
const result: Promise = this.$store.dispatch('action_name', { payload: 1 })
The problem is that 'action_name'
might no exist, take other arguments, or return a different type. That's not something you expect for a fully-typed app.
问题在于'action_name'
可能不存在,采用其他参数或返回其他类型。 对于全类型应用程序,这不是您期望的。
What are the existing solutions?
现有的解决方案是什么?
vuex-class
is a set of decorators to allow easy access from your class-based components to the Vuex
internals.
vuex-class
是一组装饰器,可让您轻松地从基于类的组件访问Vuex
内部。
But, it is not typed safe since it cannot interfere with the types of state, getters, mutations, and actions.
但是,它的类型不安全,因为它不会干扰状态,吸气剂,突变和动作的类型。
Of course, you can manually annotate types of properties.
当然,您可以手动注释属性类型。
But what are you going to do when the real type of your state, getters, mutations, or actions will change? You will have a hidden type mismatch.
但是,当状态,吸气剂,突变或动作的真实类型发生变化时,您将怎么办? 您将遇到隐藏的类型不匹配。
That's where vuex-simple
helps us. It actually offers a completely different way to write your Vuex
code and that's what makes it type safe. Let's have a look:
这就是vuex-simple
帮助我们的地方。 实际上,它提供了一种完全不同的方式来编写Vuex
代码,这就是使其安全的原因。 我们来看一下:
import { Action, Mutation, State, Getter } from 'vuex-simple'
class MyStore {
// State
@State()
public comments: CommentType[] = []
// Getters
@Getter()
public get hasComments (): boolean {
return Boolean(this.comments && this.comments.length > 0)
}
// Mutations
@Mutation()
public setComments (payload: CommentType[]): void {
this.comments = updatedComments
}
// Actions
@Action()
public async fetchComments (): Promise {
// Calling some API:
const commentsList = await api.fetchComments()
this.setComments(commentsList) // typed mutation
return commentsList
}
}
Later this typed module can be registered inside your Vuex
like so:
以后,可以像下面这样在您的Vuex
内部注册该类型的模块:
import Vue from 'vue'
import Vuex from 'vuex'
import { createVuexStore } from 'vuex-simple'
import { MyStore } from './store'
Vue.use(Vuex)
// Creates our typed module instance:
const instance = new MyStore()
// Returns valid Vuex.Store instance:
export default createVuexStore(instance)
Now we have a 100% native Vuex.Store
instance and all the type information bundled with it. To use this typed store in the component we can write just one line of code:
现在,我们有了一个100%本机Vuex.Store
实例,并捆绑了所有类型信息。 要在组件中使用这种类型的存储,我们可以只编写一行代码:
import Vue from 'vue'
import Component from 'nuxt-class-component'
import { useStore } from 'vuex-simple'
import MyStore from './store'
@Component({})
export default class MyComponent extends Vue {
// That's all we need!
typedStore: MyStore = useStore(this.$store)
// Demo: will be typed as `Comment[]`:
comments = typedStore.comments
}
Now we have typed Vuex
that can be safely used inside our project. When we change something inside our store definition it is automatically reflected to the components that use this store. If something fails — we know it as soon as possible.
现在,我们输入了可以在项目中安全使用的Vuex
。 当我们在商店定义中更改某些内容时,它会自动反映到使用该商店的组件中。 如果出现故障-我们会尽快知道。
There are also different libraries that do the same but have different API. Choose what suits you best.
也有不同的库执行相同的操作,但具有不同的API。 选择最适合您的东西。
When we have Vuex
correctly setup, we need to fill it with data. Let's have a look at our action definition again:
正确设置Vuex
,需要用数据填充它。 让我们再次看一下我们的动作定义:
@Action()
public async fetchComments (): Promise {
// Calling some API:
const commentsList = await api.fetchComments()
// ...
return commentsList
}
How can we know that it will really return a list of CommentType
and not a single number
or a bunch of AuthorType
instances?
我们怎样才能知道它真的会返回一个列表CommentType
而不是一个单一number
或一串AuthorType
实例?
We cannot control the server. And the server might actually break the contract. Or we can simply pass the wrong api
instance, make a typo in the URL, or whatever.
我们无法控制服务器。 服务器实际上可能违反合同。 或者,我们可以简单地传递错误的api
实例,在URL中输入错误或其他。
How can we be safe? We can use runtime typing! Let me introduce io-ts
to you:
我们如何安全? 我们可以使用运行时输入! 让我向您介绍io-ts
:
import * as ts from 'io-ts'
export const Comment = ts.type({
'id': ts.number,
'body': ts.string,
'email': ts.string,
})
// Static TypeScript type, that can be used as a regular `type`:
export type CommentType = ts.TypeOf
What do we do here?
我们在这里做什么?
We define an instance of ts.type
with fields that we need to be checked in runtime when we receive a response from server
我们定义了ts.type
的实例, ts.type
包含当我们从服务器收到响应时需要在运行时检查的字段
And later we can use it our api
calls:
然后我们可以使用它通过我们的api
调用:
import * as ts from 'io-ts'
import * as tPromise from 'io-ts-promise'
public async fetchComments (): Promise {
const response = await axios.get('comments')
return tPromise.decode(ts.array(Comment), response.data)
}
With the help of io-ts-promise
, we can return a Promise
in a failed state if the response from server does not match a ts.array(Comment)
type. It really works like a validation.
借助io-ts-promise
,如果服务器的响应与ts.array(Comment)
类型不匹配,我们可以以失败状态返回Promise
。 它真的像验证一样工作。
fetchComments()
.then((data) => /* ... */
.catch(/* Happens with both request failure and incorrect response type */)
Moreover, return type annotation is in sync with the .decode
method. And you cannot put random nonsense there:
此外,返回类型注释与.decode
方法同步。 而且您不能在这里随意胡扯:
With the combination of runtime and static checks, we can be sure that our requests won't fail because of the type mismatch. But, to be 100% sure that everything works, I would recommend using contract-based testing: have a look at pact
as an example. And monitor your app with Sentry
.
结合运行时检查和静态检查,我们可以确保我们的请求不会因为类型不匹配而失败。 但是,为100%确保一切正常,我建议使用基于合同的测试:以pact
为例。 并使用Sentry
监视您的应用程序。
The next problem is that this.$router.push({ name: 'wrong!' })
does not work the way we want to.
下一个问题是this.$router.push({ name: 'wrong!' })
无法按照我们想要的方式工作。
I would say that it would be ideal to be warned by the compiler that we are routing to the wrong direction and this route does not exist. But, it is not possible. And not much can be done: there are a lot of dynamic routes, regex, fallbacks, permissions, etc that can eventually break. The only option is to test each this.$router
call in your app.
我要说的是,最好由编译器警告我们正在朝错误的方向布线,并且该路由不存在。 但是,这是不可能的。 不能做的事情很多:最终可能会破坏很多动态路由,正则表达式,后备,权限等。 唯一的选择是测试应用程序中的每个this.$router
调用。
Speaking about tests I do not have any excuses not to mention @vue/test-utils
that also has some problems with typing.
说到测试,我没有任何借口,更不用说@vue/test-utils
,它在输入方面也存在一些问题。
When we will try to test our new shiny component with typedStore
property, we will find out that we actually cannot do that according to the typescript
:
当我们尝试使用typedStore
属性测试新的闪亮组件时,我们会发现实际上无法根据typescript
:
Why does this happen? It happens because mount()
call does not know anything about your component's type, because all components have a VueConstructor
type by default:
为什么会这样? 发生这种情况是因为mount()
调用对您的组件类型VueConstructor
,因为默认情况下所有组件都具有VueConstructor
类型:
That's where all the problems come from. What can be done? You can use vuetype
to produce YouComponent.vue.d.ts
typings that will tell your tests the exact type of the mounted component.
那就是所有问题的根源。 该怎么办? 您可以使用vuetype
生成YouComponent.vue.d.ts
类型,这些类型将告诉您的测试所安装组件的确切类型。
You can also track this issue for the progress.
您也可以跟踪此问题以获取进度。
But, I don't like this idea. These are tests, they can fail. No big deal. That's why I stick to (wrapper.vm as any).whatever
approach. This saves me quite a lot of time to write tests. But spoils Developer Experience a little bit.
但是,我不喜欢这个主意。 这些是测试,它们可能会失败。 没什么大不了的。 这就是为什么我坚持使用(wrapper.vm as any).whatever
方式的原因。 这为我节省了大量时间来编写测试。 但是会破坏开发人员的经验。
Make your own decision here:
在这里做出自己的决定:
Use vuetype
all the way
一直使用vuetype
Use any
as a fallback
使用any
作为后备
The average level of typescript
support in Vue
ecosystem increased over the last couple of years:
在过去几年中, Vue
生态系统中的typescript
支持平均水平有所提高:
Nuxt
firstly introduced nuxt-ts
and now ships ts
builds by default
Nuxt
首先引入了nuxt-ts
,现在默认情况下提供了ts
构建
Vue@3
will have improved typescript
support
Vue@3
将改进typescript
支持
But, it is production ready at the moment. These are just things to improve! Writing type-safe Vue
code really improves your Developer Experience and allows you to focus on the important stuff while leaving the heavy-lifting to the compiler.
但是,目前已经可以生产了。 这些只是需要改进的地方! 编写类型安全的Vue
代码确实可以改善您的开发人员体验,并让您可以专注于重要的内容,而无需费力地进行编译。
What are your favourite hacks and tools to type Vue
apps? Let's discuss it in the comment section.
您最喜欢键入Vue
应用程序的黑客和工具是什么? 让我们在评论部分中讨论它。
翻译自: https://habr.com/en/post/458632/
vue输入框限制输入