在 Vue 中使用 TypeScript(以及 RealWorld使用)
在 Vue 中使用 TypeScript(以及 RealWorld使用)学习原因关于 TypeScript 相关资源和信息越来越多已经有越来越多的基于 JS 来写的项目,使用 TS 来编写。框架npm 包TS 未来可能会成为一个技能标配语法项目实战经验创建支持 TypeScript 的 Vue 项目通过 Vue CLI 创建 支持 TS 的 Vue 项目在一个已有的 Vue 项目中配置 TypeS
在 Vue 中使用 TypeScript(以及 RealWorld使用)
学习原因
- 关于 TypeScript 相关资源和信息越来越多
- 已经有越来越多的基于 JS 来写的项目,使用 TS 来编写。
- 框架
- npm 包
- TS 未来可能会成为一个技能标配
- 语法
- 项目实战经验
创建支持 TypeScript 的 Vue 项目
- 通过 Vue CLI 创建 支持 TS 的 Vue 项目
- 在一个已有的 Vue 项目中配置 TypeScript 支持
通过 Vue CLI 创建
$ vue create vue-ts
? Please pick a preset: Manually select features
# 这一步把 TypeScript 勾选上即可,其它功能根据自己需要进行选择
# CSS Pre-processors - CSS 预处理器(如:SCSS LESS)
? Check the features needed for your project: Babel, TS, Router, Vuex, Linter
# 选择了 TS 后会询问,是否使用 class 的语法风格定义组件(React中的定义方式)
# 如果你喜欢使用 class 的方式定义组件,则输入 Yes,不喜欢的话就 No
? Use class-style component syntax? Yes
# 是否使用 Babel 编译 TS 语法
# TypeScript 本身就有编译功能,默认会把 JavaScript 代码转换为 ECMAScript 3 版本兼容的代码
# 如果你需要现代模式、自动检测 polyfill、转换 JSX 语法等功能,则建议开启这个选项
# 当选择以后,TypeScript 本身只会把代码转为 ESNext,也就是最新版的 ECMAScript 规范
# 然后由 Babel 把 ECMAScript 转换为低版本 JavaScript,例如 ECMAScript 5,以及自动检测 polyfill、转换 JSX 等功能
# 说白了就是让 TypeScript 转换和 Babel 转换相结合起来一起使用
? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? Yes
? Use history mode for router? (Requires proper server setup for index fallback in production) No
# 根据需要选择 校验风格
# TSLint(deprecated) 已经备注了不建议选择
? Pick a linter / formatter config: Standard
# 选择校验的时机(代码保存的时候,代码提交的时候)
# 建议都选上,更严谨
? Pick additional lint features: Lint on save, Lint and fix on commit
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? No
npm 包
{
"dependencies": {
// 这两个是支持用class编写组件的
"vue-class-component": "^7.2.3",
"vue-property-decorator": "^8.4.2",
},
"devDependencies": {
// eslint 校验TS代码规范,不建议使用 tslint
"@typescript-eslint/eslint-plugin": "^2.33.0",
"@typescript-eslint/parser": "^2.33.0",
"@vue/eslint-config-typescript": "^5.0.2",
// vue cli 帮助打包构建 .ts 文件 的插件
"@vue/cli-plugin-typescript": "~4.5.0",
// ts
"typescript": "~3.9.3",
},
}
TS 配置文件 tsconfig.json
通常情况下不需要对这些配置进行更改。
{
// 配置选项
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"experimentalDecorators": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"types": [
"webpack-env"
],
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
// include 指定需要被编译的文件
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
// exclude 排除不需要被编译的文件
"exclude": [
"node_modules"
]
}
ESLint 配置文件 .eslintrc.js
它默认用来校验 JS 代码规范。
module.exports = {
root: true,
env: {
node: true
},
extends: [
'plugin:vue/essential',
// 在vue中校验standard风格代码规范
'@vue/standard',
// 在vue中校验typescript风格代码规范
'@vue/typescript/recommended'
],
parserOptions: {
ecmaVersion: 2020
},
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
}
}
src 源码
- .js 文件都变为了 .ts 文件
- src 根目录多了两个 .d.ts 文件(shims-tsx.d.ts、shims-vue.d.ts)
- shims-tsx.d.ts
- 主要是方便你使用在 ts 中使用 jsx 语法的,如果不使用 jsx 语法,可以无视
- shims-vue.d.ts
- 主要用于 TS 识别.vue 文件,TS 默认并不支持导入 vue 文件,这个文件告诉 TS 导入.vue 文件都按
VueConstructor<Vue>
(Vue 构造函数)处理- 这样配置可以实现快速跳转到引入的文件
- 但是这样会衍生两个问题
- 导入vue文件必须写 .vue 后缀
- 就算导入的 .vue 文件路径错误,静态检测也不会检测到错误,鼠标放到错误路径上会看到全部指向
*.vue
,正确的就会显示真实路径。
- 主要用于 TS 识别.vue 文件,TS 默认并不支持导入 vue 文件,这个文件告诉 TS 导入.vue 文件都按
- shims-tsx.d.ts
错误路径:
正确路径:
- components/HelloWorld.vue 单文件组件中,使用了 ts 语法 编写脚本
- 并且使用了 class 的方式编写(创建项目时选择的)
<!-- lang="ts" 把script中的内容当作 TypeScript 对待 -->
<script lang="ts">
// 通过这些成员,定义Vue组件
import { Component, Prop, Vue } from 'vue-property-decorator'
// @ 是装饰器语法,不建议用
@Component
export default class HelloWorld extends Vue {
@Prop() private msg!: string;
}
</script>
已有的Vue项目中支持 TypeScript
使用 @Vue/cli 安装 TypeScript 插件:
vue add @vue/typescript
? Use class-style component syntax? Yes
? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? Yes
# 是否需要把所有 .js 文件转换为 .ts 资源
# 不建议使用,转换的过程会修改已有的 .js 资源代码,可能会带来问题,建议手动启用 TypeScript 之后手动把 .js 处理为 .ts
? Convert all .js files to .ts? No
# 是否允许编译 .js 文件模块,不建议开启,既然选择了 TypeScript,就最好全部更换为 TypeScript,而不要两者混搭,反而变得不伦不类
? Allow .js files to be compiled? No
安装后项目中增加的内容和 Vue CLI 创建项目中增加的内容差不多。
定义组件的方式
常规方式 直接导出选项对象
在不支持 TS 的环境中:
<script>
export default {
name: 'Foo',
data() {
return {}
},
created: 100
}
</script>
这种方式,只有在运行期间才知道定义的选项格式是否正确。
例如 代码中的 created 应该是一个函数类型,此时随便定义一个数字,并不会提示错误。
指定使用 TS 脚本后,TS 和 Vetur 插件就会显式智能提示,created 类型错误
Options APIs 使用 Vue.extend / Vue.component 定义
在使用TS语法时,通过 Vue.extend / Vue.component (下面只以Vue.extend为例)创建组件。
- 需要引入 Vue
import Vue from 'vue'
- 内部定义的方式和常规方式一样
<script lang="ts">
// Options APIs
import Vue from 'vue'
export default Vue.extend({
name: 'Foo',
data () {
return {
message: 'Hello world'
}
}
})
</script>
使用 Vue.extend 的原因
- 常规方式是直接导出一个对象。
- 此时 TypeScript 无法推断出对应的类型
- 所以必须使用调用 Vue 方法的方式,声明选项对象的类型,确保 TypeScript 能够有正常的类型推断
- 后面使用类的方式定义组件,通过继承 Vue 创建 class,也是为了向TS声明类的类型
import Vue from 'vue'
const Component = Vue.extend({
// 类型推断已启用
})
const Component = {
// 这里不会有类型推断,
// 因为 TypeScript 不能确认这是 Vue 组件的选项
}
插件智能提示 & 编译提示
// Foo.vue 示例代码1
<script lang="ts">
export default {
created: 123 // created 应该是函数类型
}
</script>
// Foo.vue 示例代码2
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({
created: 123 // created 应该是函数类型
})
</script>
在 vscode 中如果安装了插件 vetur,就会在开发时,实时校验并智能提示类型错误。
.vue 后缀的文件中,设置了 ts 脚本类型(lang=“ts”),不使用 vue.extend 插件也会进行类型推断并提示,如 “常规方式”中的图示。
而官方讲的使用 Vue.extend 才可以正确推断类型,指的是编译阶段。
禁用插件 vetur,就取消了编辑器的识别 .vue 文件的代码高亮和类型推断功能。
不论使用 示例1 还是 示例2 的代码,编辑器都不会提示 created 类型错误。
此时打包或编译项目,区别就体现出来了:
- 示例代码1 会在 编译到
src/router/index.ts
文件时,校验到路由组件中使用了 Foo 组件,进而校验到 Foo 组件中的 created 类型错误。- 结果定位到的是父级组件,需要主动继续定位
- 当前项目是 Vue CLI 创建的,所以最终还是会被校验出来。
- 如果是在已有项目中增加支持TS的情况,可能编译阶段就会因为没有在 Foo.vue 文件中校验出错误,导致编译成功,这样就只能在浏览页面(也就是运行时)才会在控制台看到报错。
- 示例代码2 会在编译到
Foo.vue
文件时,立即校验到错误。- 可以快速定位,在编译时发现错误。
以上是为了区分为什么要使用 Vue.extend 定义组件。
它的目的是可以在编译阶段正确推断类型,而不用等到运行阶段。
编辑器的vetur插件也会进行推断,容易混淆对这个目的的理解。
编辑器还有其他很多校验错误,但是不影响编译甚至运行的情况,这里只需要知道,为了使用TS的类型推断功能,要使用 Vue.extend 或 下面基于 class 的定义方式就可以了。
.ts 文件中使用类型推断
编写 Vue 组件不一定使用 .vue 格式的文件,也可以使用 .js 或 .ts,最终导出一个构造函数或组件选项对象。
这可能也是要求使用 Vue.extend 的另一个解释。
尝试使用 .ts 文件编写组件,摆脱 vetur 的影响:
import Vue from 'vue'
const Bar = {
name: 'Bar',
render (h: any) {
return h('div', 'hello bar')
},
created: 123
}
// export default Bar // 编译到父组件时报错
export default Vue.extend(Bar) // 编译当前文件时报错
Class APIs 基于类的Vue组件定义
上面使用 Vue.extend 方法,组件内部采用原生的 vue 写法。
如果您在声明组件时更喜欢基于类的 API,则可以使用官方维护的 vue-class-component 装饰器
官方并没有表态哪种方式更高级,选择只取决于编码习惯(类似React)。
在 TypeScript 下,Vue 的组件可以使用一个继承自 Vue 类型的子类表示。
- 需要引入 Vue
import Vue from 'vue'
- 需要引入 Component
import Component from 'vue-class-component'
- 导出的类继承自 Vue
- 使用 @Component (类的装饰器)注册
- 装饰器是一个函数,用于扩展类
- 可以调用它,接收的参数就是组件的选项对象
- 也可以不调用它,只用来声明
- 如果不使用装饰器标识,在chrome浏览器Vue插件中看不到组件的选项信息
- 装饰器是一个函数,用于扩展类
- 可以在类的内部直接定义 data、 methods、 computed(使用getter/setter)、生命周期函数
- 其他特性例如 components、props、filters、directives 等,需要使用装饰器参数传入。
- 使用 class 风格的组件声明方式,并没有特别的好处,只是提供给开发者多种编码风格的选择性。
// Class APIs
import Vue from 'vue'
import Component from 'vue-class-component' // 官方库
// props 使用,要先创建一个实例,然后让组件继承它
const FooProps = Vue.extend({
props: {
propMessage: String
}
})
// 声明并导出一个继承自Vue的类
// 类名:组件的 name
// @Component 修饰符注明了此类为一个 Vue 组件
// 不调用方式
// @Component
// export default class Foo extends Vue {
// // ...
// }
// 调用方式
@Component({
// 所有的组件选项都可以放在这里
// data、methods、computed、hooks 以外的选项必须在这里定义
// props 要使用实例继承实现
data () {
return {
count: 100
}
}
})
export default class Foo extends FooProps {
// 在这里可以直接声明实例的data、methods、computed、hooks
// 其他所有选项,需在装饰器函数Component(options)中定义:components、props等
// data:初始化 data 的成员
msg = 'world'
// 使用 prop 初始化的 data 的成员(注意props的定义方式)
helloMsg = 'Hello, ' + this.propMessage
// hooks:钩子函数
mounted () {
this.say()
}
// computed:通过get 和 set 将计算的属性声明为类属性访问器
get computedMsg () {
return 'computed ' + this.msg
}
// methods:直接定义方法
say () {
console.log('Hello ' + this.msg)
}
}
vue-class-component 在使用 props 时稍显麻烦,需要在外面创建定义了props的实例,然后让组件继承它。
可以使用 vue-property-decorator 的 @Prop 装饰器更方便的在类内部定义。
装饰器
装饰器 是 ES 草案中的一个新特性,提供一种更好的面向切面编程(AOP)的体验,不过这个草案最近(当前2020年)有可能发生重大调整,所以暂不推荐使用。
Class APIs + vue-property-decorator
vue-property-decorator
不是Vue 官方的库,完全依赖于vue-class-component
。
在 vue-class-component 的基础上扩展,并且提供了很多装饰器比如 @Prop
和@Watch
等,可以更方便的使用 class 的方式定义组件。
import { Vue, Component, Prop } from 'vue-property-decorator'
@Component
export default class Property extends Vue {
@Prop(String) readonly text: string | undefined
msg = 'This is ' + this.text
}
总结
推荐使用 Options APIs 方式。
- 上面几种方式实现的结果都是一样的。
- Vue 本身不强制使用 class 方式定义组件选项,使用 Options APIs 更简单。
- class 方式定义的实例成员都在类的内部直接定义,Options APIs 中定义选项,是分类放置,看起来更清晰更集中
- 变量放在 data 中
- 方法放在 methods 中
- 计算属性放在 computed 中
- 等
- class 的方式需要用到装饰器(@Component)
- 装饰器语法目前还不稳定,目前还没有正式发版(当前2020年),草案变动也很大
- 所以,建议正式发版后使用,当前不建议使用装饰器结合类的方式定义组件。
- 其实 Vue.js 3.0 早期是想要放弃 Class APIs 的,不过无奈想要兼容,所以才保留下来了。
RealWorld
介绍
RealWorld 用于了解如何使用我们支持的任何前端技术(React,Vue等)和后端技术(Node,Go等)构建完全相同的案例(Medium.com,称为Conduit))。
它是个万能案例,提供了页面模板、免费的遵循相同的API规范的数据接口。
学习框架或技术后,通过实现 RealWorld 案例的实践,加深对学习内容的了解。
使用方法
-
Fork our starter kit - Fork RealWorld 的入门模板
-
FRONTEND_INSTRUCTIONS - 前端使用说明
- 数据接口
- 接口基础路径: https://conduit.productionready.io/api
- 接口文档
- 路由
- 样式
- 布局 HTML 模板
- Header 头部
- Footer 尾部
- 页面 HTML 模板
- Home 首页
- Login/Register 登录注册页面
- Profile 用户页面
- Settings 设置页面
- Create/Edit Article 创建编辑文章页面
- Article 文章详情页面
- 数据接口
-
然后手动创建这些HTML模板的文件,调用提供的接口,实现案例中的功能。
体验 TypeScript
使用 RealWorld 模板进行初始配置
先将 RealWorld 的模板复制到页面中:
- /public/indexhtml 中复制 Header 中的样式引入 link
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
<!-- RealWorld -->
<!-- Import Ionicon icons & Google Fonts our Bootstrap theme relies on -->
<link href="//code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css" rel="stylesheet" type="text/css">
<link href="//fonts.googleapis.com/css?family=Titillium+Web:700|Source+Serif+Pro:400,700|Merriweather+Sans:400,700|Source+Sans+Pro:400,300,600,700,300italic,400italic,600italic,700italic" rel="stylesheet" type="text/css">
<!-- Import the custom Bootstrap 4 theme from our hosted CDN -->
<link rel="stylesheet" href="//demo.productionready.io/main.css">
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
- views 创建 layout/index.vue,内部定义布局组件
- script 使用 ts 编写
- 复制 Header 中的 nav 头部导航栏
- 中间是子路由出口 router-view
- 复制 Footer 模板内容
<template>
<div>
<!-- 头部导航栏 -->
<nav class="navbar navbar-light">
<div class="container">
<a class="navbar-brand" href="index.html">conduit</a>
<ul class="nav navbar-nav pull-xs-right">
<li class="nav-item">
<!-- Add "active" class when you're on that page" -->
<a class="nav-link active" href="">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="">
<i class="ion-compose"></i> New Post
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="">
<i class="ion-gear-a"></i> Settings
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="">Sign up</a>
</li>
</ul>
</div>
</nav>
<!-- 子路由出口 -->
<router-view />
<!-- 底部内容 -->
<footer>
<div class="container">
<a href="/" class="logo-font">conduit</a>
<span class="attribution">
An interactive learning project from
<a href="https://thinkster.io">Thinkster</a>. Code & design
licensed under MIT.
</span>
</div>
</footer>
</div>
</template>
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({
name: 'layout'
})
</script>
<style></style>
创建首页、注册/登录页,同layout类似。
清空 App.vue:
<template>
<div id="app">
<router-view/>
</div>
</template>
<style></style>
修改路由配置:
import Vue from 'vue'
import VueRouter, { RouteConfig } from 'vue-router'
Vue.use(VueRouter)
const routes: Array<RouteConfig> = [
{
path: '/',
component: () => import('@/views/layout/index.vue'),
children: [
{
path: '', // 默认子路由
name: 'home',
component: () => import('@/views/home/index.vue')
},
{
path: 'login',
name: 'login',
component: () => import('@/views/login/index.vue')
}
]
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
现在可以运行查看效果:npm run serve
vscode 自定义代码段
vscode 提供代码段快速创建模板。
vetur 提供了vue的自定义代码段,但是当前使用TS,每次还要修改 script 标签及内容。
创建使用 TS 的 Vue 模板代码段:
- F1 显示所有命令,搜索 snippets(首选项:配置用户代码片段)
- 选择对应的语言(Vue),打开了vue.json
{
// [key]:代码段的名称 - 每个代码段都在名称下定义
// prefix:前缀 - 触发代码段的入口,使用时输入它会有智能感知提示
// body:正文 - 代码段的主体,需要设置的代码放在这里
// 字符串之间换行使用\r\n换行符隔开
// 多行语句也可以使用字符串数组方式
// 注意:如果值里包含特殊字符需要进行转义
// $0:代码段插入后,光标的所在位置
// descrition:代码段描述 - 使用智能感知时的描述
".vue for ts": {
"prefix": "<vue> with ts",
"body": [
"<template>",
" <div>",
" ${0}",
" </div>",
"</template>",
"<script lang=\"ts\">",
"import Vue from 'vue'",
"export default Vue.extend({",
"\r",
"})",
"</script>",
"\r",
"<style></style>",
],
"description": "使用TS语法"
}
}
使用 TS 实现案例的注册功能
编写请求方法
安装HTTP请求依赖 npm i axios
。
简单封装一下 axios:
// src/utils/request.ts
import axios from 'axios'
// 创建 axios 实例
const request = axios.create({
// 接口基础路径
baseURL: 'https://conduit.productionready.io/api'
})
export default request
封装用户认证相关接口请求接口的方法,这里开始使用 TS:
一般情况下,用户注册方法接口的参数可以是接口的请求体(data),但是这样就可以传任何类型,包括不是接口期望接收的结构和类型。
这也是前后端联调时经常发生的情况:接口传参传错。
TypeScript 可以通过interface(接口)的方式定义数据类型。
- 接口定义数据类型,{}不是对象,而是接口的语法
- 接口名首字母大写
- 接口的分隔符可以使用逗号或分号,也可以不使用分隔符
@typescript-eslint/member-delimiter-style
校验风格要求必须使用分隔符- 可以选择在ESLint配置中关闭这条规则,或者编写分割符
// src/api/auth.ts
/**
* 用户认证相关接口模块
*/
import request from '@/utils/request'
// 用户注册接口传参
export interface User {
user: {
username: string
email: string
password: string
}
}
// 用户注册
export const register = (data: User) => {
request({
method: 'POST',
url: '/users',
data: data
})
}
效果:
- 格式错误会智能提示
- 输入的时候会自动提示接口中定义的字段
建议:
在第一次使用的地方定义 interface,也可以集中在一个文件中定义。
提供接口的导出(export),方便其他地方使用
继续扩展 register 的返回值。
一般情况,会将请求的接口直接返回,这样开发的时候需要开发者自己了解返回的数据结构,频繁的看接口文档,或查看实际请求结果。
为返回值添加类型声明,这样在使用时,就会有智能提示,减少很多工作。
// src/api/auth.ts
/**
* 用户认证相关接口模块
*/
import request from '@/utils/request'
// 用户注册接口传参
export interface User {
user: {
username: string
email: string
password: string
}
}
// 用户注册接口返参
export interface RegisterData {
user: {
email: string
token: string
username: string
bio: string
image: string
}
}
// 用户注册
export const register = async (data: User): Promise<RegisterData> => {
const res = await request({
method: 'POST',
url: '/users',
data: data
})
return res.data
}
注意:
- request 调用的是 axios ,它是异步请求,所以要获取 axios 请求结果并返回,需要使用 async/await
- 而 TypeScript 要求 async 异步函数的返回类型必须是 Promise,当直接指定返回类型时会报错
- 需要使用泛型,指定 Promise 类型的类型声明
错误示例:
最终使用效果(智能提示):
编写表单事件
- data 中定义表单数据
- 绑定表单的 submit 事件为自定义方法 onSubmit
- 注意使用 prevent 阻止表单默认行为
- 绑定表单中的数据 v-model
- 导入 register 方法
- 编写 onSubmit 方法
<template>
<div class="auth-page">
<div class="container page">
<div class="row">
<div class="col-md-6 offset-md-3 col-xs-12">
<h1 class="text-xs-center">Sign up</h1>
<p class="text-xs-center">
<a href="">Have an account?</a>
</p>
<ul class="error-messages">
<li>That email is already taken</li>
</ul>
<form @submit.prevent="onSubmit">
<fieldset class="form-group">
<input
v-model="user.username"
class="form-control form-control-lg"
type="text"
placeholder="Your Name"
/>
</fieldset>
<fieldset class="form-group">
<input
v-model="user.email"
class="form-control form-control-lg"
type="text"
placeholder="Email"
/>
</fieldset>
<fieldset class="form-group">
<input
v-model="user.password"
class="form-control form-control-lg"
type="password"
placeholder="Password"
/>
</fieldset>
<button class="btn btn-lg btn-primary pull-xs-right">
Sign up
</button>
</form>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue'
import { register } from '@/api/auth.ts'
export default Vue.extend({
name: 'login',
data() {
return {
user: {
username: 'aaasfgdfghfdaaaaa',
email: 'aaasdsda@bfhrtgergb.com',
password: '1234abcd'
}
}
},
methods: {
async onSubmit() {
// 获取表单数据
// 表单验证
// 提交表单
const data = await register({
user: this.user
})
console.log(data)
// 根据返回结果处理后续操作
this.$router.push({
name: 'home'
})
}
}
})
</script>
<style></style>
RealWorld 注册接口使用注意:
- 接口会校验参数格式,校验失败会返回422。
- 这是真实注册,注册过的账号不能重复使用,会报
has already been taken
,也会返回422,可以用写登录多次尝试接口。
更多推荐
所有评论(0)