前端学习笔记 5:大事件
本文将学习一个示例项目(大事件)的前端搭建过程。
1.准备工作
1.1.创建工程
创建一个名称为big-event
的 Vue3 项目,具体可以参考这篇文章。
1.2.安装插件
安装 ElementPlus:
npm install element-plus --save
安装好后还需要在main.js
中导入相关模块和样式:
import './assets/main.scss'
import {
createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')
安装 Axios:
npm install axios
安装 sass:
npm install sass -D
这是一个 CSS 扩展。
1.3.调整目录
-
删除components目录下的内容
-
删除App.vue中的内容,只保留script和template标签
-
在
src
下新建如下目录:
api:存放接口调用的js文件
utils:存放工具js文件
拷贝request.js到util目录
views:存放页面的.vue文件
- 删除assets目录中的内容, 将资料中的静态资源文件全部拷贝到该目录下
2.注册
2.1.页面
在views
下添加 Login.view 作为登录页,并在 App.vue
中使用:
<script setup>
import LoginVue from '@/views/Login.vue'
</script>
<template>
<LoginVue/>
</template>
为注册表单绑定响应式数据:
<script setup>
// ...
// 注册表单的响应式数据
const registData = ref({
username: '',
password: '',
repassword: ''
})
</script>
<template>
<el-form ... :model="registData">
<el-form-item>
<el-input ... v-model="registData.username"></el-input>
</el-form-item>
<el-form-item>
<el-input ... v-model="registData.password"></el-input>
</el-form-item>
<el-form-item>
<el-input ... v-model="registData.repassword"></el-input>
</el-form-item>
</el-form>
</template>
为注册表单定义验证规则:
// 注册表单的验证规则
const rules = ref({
username: [
{
required: true, message: '请输入用户名', trigger: 'blur' },
{
min: 5, max: 16, message: '长度应该在5~16个字符', trigger: 'blur' },
],
password: [
{
required: true, message: '请输入密码', trigger: 'blur' },
{
min: 5, max: 16, message: '长度应该在5~16个字符', trigger: 'blur' },
],
repassword: [{
validator: checkPass, trigger: 'blur' }],
})
其中再次输入密码(repassword)的验证规则是自定义验证规则:
// 检查密码是否一致的验证函数
const checkPass = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入密码'))
}
else if(value !== registData.value.password){
callback(new Error('密码不一致'))
}
else{
callback()
}
}
将验证规则绑定到表单:
<el-form ... :rules="rules">
<!-- ... -->
<el-form-item prop="username">
<!-- ... -->
</el-form-item>
<el-form-item prop="password">
<!-- ... -->
</el-form-item>
<el-form-item prop="repassword">
<!-- ... -->
</el-form-item>
<!-- ... -->
</el-form>
2.2.接口调用
这里对应的服务端 jar 包是 big-event.jar,对应的数据库表结构是 big-event.sql。
jar 包中登录数据库的帐号密码是
root:1234
,如果不符,需要手动自行修改。
可以通过以下命令运行服务端:
java -jar .\big-event-1.0-SNAPSHOT.jar
该服务端提供的接口可以查阅接口文档。
在/src/api/user.js
下创建接口调用函数:
import request from '@/utils/request.js'
export const registService = (registData) => {
return request.post("/user/register", registData, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
}
需要注意的是,这里通过 POST 方法传递的参数是以x-www-form-urlencoded
方式传递的,也就是在方法体中以 URL 编码方式传递,并非 Axios 默认的 JSON 格式传递,所以需要添加上额外参数指定请求头信息(content-type)。
除了上述方式,也可以使用另一种方式传递 x-www-form-urlencoded
编码的 POST 参数:
export const registService = (registData) => {
var params = new URLSearchParams()
for(let key in registData){
params.append(key, registData[key])
}
return request.post("/user/register", params)
}
两者效果上是相同的。
在login.vue
中使用该函数完成注册,并绑定按钮点击事件:
<script setup>
// 注册用户
import { registService } from '@/api/user.js'
const regist = async () => {
let result = await registService(registData.value)
if (result.code === 0) {
alert("注册成功!")
}
else {
alert(result.msg ? result.msg : "注册失败!")
}
}
</script>
<template>
<el-button class="button" type="primary" auto-insert-space @click="regist">
注册
</el-button>
</template>
2.3.跨域
上边的示例实际上是有问题的,运行后观察控制台会出现类似下面的报错信息:
Access to XMLHttpRequest at 'http://localhost:8080/user/register' from origin 'http://localhost:5173' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
该信息说明了:程序尝试从源 http://localhost:5173
进行 Ajax 请求到 http://localhost:8080/user/register
,这种请求被 CORS 规则所阻止。
实际上 CORS 是一种浏览器端的 AJAX 跨域访问的安全策略,只要是对不同源(协议\域名\端口任一不同)的 AJAX 调用,都会被浏览器阻止(除非服务端允许跨域访问)。
目前我们的前端程序(Vue)是运行在 Node.js 服务上的,所以可以通过在 Node.js 服务(Vue)上添加代理,让浏览器先访问 Node.js 服务,再通过 Node.js 服务将请求转发到 Jar 包运行的服务,这样就可以规避浏览器的跨域限制。
首先,修改request.js
,让所有 AJAX 调用都以/api
开头:
// const baseURL = 'http://localhost:8080';
const baseURL = '/api'
其次,修改 Vue 配置文件vite.config.js
:
export default defineConfig({
// ...
// 代理配置
server: {
proxy: {
'/api': {
target: 'http://localhost:8080', // 后端服务器地址
changeOrigin: true, // 是否改变请求域名
rewrite: (path) => path.replace(/^\/api/, '')//将原有请求路径中的api替换为''
}
}
}
})
这里的正则
/^\/api/
的作用是将以/api
开头的路径中的/api
替换为空字符串。
3.登录
3.1.实现
在user.js
中添加登录接口的调用函数:
export const loginService = (loginData) => {
return request.post("/user/login", loginData, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
}
在login.vue
中添加登录逻辑:
// 登录
const login = async () => {
let result = await loginService(registData.value)
if (result.code === 0) {
alert("登录成功!")
}
else {
alert(result.message ? result.message : "登录失败!")
}
}
给登录按钮绑定点击事件:
<el-button class="button" type="primary" auto-insert-space @click="login">登录</el-button>
为登录表单绑定响应式数据(与注册表单的响应式数据复用):
<el-form ... :model="registData" :rules="rules">
<el-form-item prop="username">
<el-input ... v-model="registData.username"></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input ... v-model="registData.password"></el-input>
</el-form-item>
</el-form>
这里对验证规则也进行了复用。
最后,还需要在切换注册或登录页面时对响应式数据清空:
// 重置注册登录数据
const resetRegistData = ()=>{
registData.value = {
username: '',
password: '',
repassword: ''
}
}
<el-link type="info" :underline="false" @click="isRegister = false; resetRegistData()">
← 返回
</el-link>
<!-- ... -->
<el-link type="info" :underline="false" @click="isRegister = true; resetRegistData()">
注册 →
</el-link>
3.2.拦截器优化
一般的,接口都会用统一的返回标识符表示业务的成功和失败,比如这里的result.data.code
。可以利用这一点对响应拦截器进一步优化,在响应拦截器中判断业务接口的成功和失败,如果失败,直接输出失败信息,这样就不需要在前端的业务代码中对接口失败进行逐一处理。
修改request.js
,添加处理业务失败时的逻辑:
instance.interceptors