前后端项目实战
1、前端技术栈:Vue 3 + Pinia + Vite + TypeScript + Element-Plus + Axios + Eslint + Sass + 其它…
- Pinia 不需要 Vuex 自定义复杂的类型去支持 TypeScript,天生对类型推断就非常友好,并且对 Vue Devtool 的支持也非常好,被称为下一代 Vuex。
- 学习 TypeScript 推荐视频(黑马程序员)
2、后端技术栈:Node.js + Koa 2 + MySQL + 其它…
- Koa 2 实现异步是通过 async/awaite,Koa 实现异步是通过 generator/yield,而 Express 实现异步是通过回调函数的方式。写 Node.js 建议使用 Koa2 > Koa > Express。
- 学习 Koa 2 推荐文档(aka 老师) + 配套视频
学习 Koa 2 推荐文档2(廖雪峰)
一、前端模块
小剧场
- 报错
GET http://127.0.0.1:5173/index/means1.png 404 (Not Found)- 报错截图
- 相关代码
- 问题
在使用v-for
循环渲染<img />
标签的时候,图片资源无法渲染。- 分析问题
- 可能是相对路径或者绝对路径问题导致图片资源加载失败。
- 可能是 vue 3 中对静态资源方式进入做了更改。
- 可能是其它问题。
- 解决问题 排除了路径原因后;使用 require 导入图片,失败;最后使用
new URL
解决了问题。- 解决问题具体操作 如下图:写法绕了一些,但确实是解决了问题。
搞定 !
如何使用 element-plus 表单验证单个字段
以上是整个表单。
一般情况下,我们使用 element-plus 框架提供的validate
方法会对整个表单都进行校验。
我希望在填写 “电话” 的时候,只使用校验规则的电话部分;在填写 “邮箱” 的时候,只使用校验规则的邮箱部分…
此时,我们可以使用 element-plus 提供的validateField
方法,就能很好地解决问题。
同时,使用clearValidate
方法可以达到清除部分校验规则的目的。
使用方法如下:
<el-form ref="ruleFormRef" :model="form" :rules="rules" label-width="110px" label-position="left">
<el-form-item label="电话" prop="phone">
<el-input v-model="form.phone" placeholder="请输入电话" />
<el-button type="primary" @click="saveValue('phone', ruleFormRef)">保存</el-button>
<el-button @click="inputValue('phone', 'close')">取消</el-button>
</el-form-item>
<el-form-item label="邮箱" class="item" prop="email">
<el-input v-model="form.email" placeholder="请输入邮箱" />
<el-button type="primary" @click="saveValue('email', ruleFormRef)">保存</el-button>
<el-button @click="inputValue('email', 'close')">取消</el-button>
</el-form-item>
</el-form>
<script lang="ts" setup>
import { reactive, ref } from "vue";
import type { FormInstance, FormRules } from "element-plus";
import { validateEmailNotEmpty, validatePhoneNotEmpty } from "@/utils/validator";
/* src/utils/validator.ts
export const validatePhoneNotEmpty = (rule: any, value: any, callback: any) => {
if (value === "") {
callback(new Error("Phone can not be empty"));
} else {
const reg =
/^(0|86|17951)?(13[0-9]|15[012356789]|166|17[3678]|18[0-9]|14[57])[0-9]{8}$/;
if (reg.test(value)) {
// 正确
callback();
} else {
// 错误
callback(new Error("Wrong format! Please check and enter again"));
}
}
};
export const validateEmailNotEmpty = (rule: any, value: any, callback: any) => {
if (value === "") {
callback(new Error("Email can not be empty"));
} else {
const reg = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
if (reg.test(value)) {
// 正确
callback();
} else {
// 错误
callback(new Error("Wrong format! Please check and enter again"));
}
}
};
*/
const ruleFormRef = ref<FormInstance>();
const form = reactive({
phone: "", // 电话
email: "", // 邮箱
})
// 校验规则
const rules = reactive<FormRules>({
phone: [
{
trigger: "blur",
validator: validatePhoneNotEmpty,
},
],
email: [
{
trigger: "blur",
validator: validateEmailNotEmpty,
},
]
})
// 重置该表单项,并移除校验结果
const clearValidate = (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.clearValidate();
};
// 保存
const saveValue = async (item: string, formEl: FormInstance | undefined) => {
if (!formEl) return;
if (item === "phone") {
formEl.validateField("phone", () => null); // 对单一字段 phone 进行校验
} else if (item === "email") {
formEl.validateField("email", () => null); // 对单一字段 email 进行校验
}
};
// 取消
const inputValue = (item: string, bool: string) => {
if(item === "phone"){
form.phone = "";
clearValidate(ruleFormRef.value); // 清除 phone 的校验规则
} else if(item === "email"){
form.email= "";
clearValidate(ruleFormRef.value); // 清除 email 的校验规则
}
}
</script>
二、后端模块
小剧场
写多条 SQL 语句的时候,不能嵌套着用
*
,否则在做数据合并时,如果字段重复,会报冲突的错误。
错误写法:
推荐写法:
小剧场
数据库的时间是正确的,但是获取的时候,时间少了 8 小时。如图:
MySQL 时间正确,但是后端获取到的时间就是错误的,所以在后端连接 MySQL 的配置中加了timezone: "08:00"
这样解决了后端获取时间错误的问题。搞定!
MySQL 语句
多表合并查询
应用:(根据某相同字段)查询多个表中数据的集合。
语法:select 字段,字段,字段 from 表a,表b where 表a.字段=表b.字段
例:select username,password,phone,note from user,message where user.id=message.user_id
或:select * from user,message where user.id=message.user_id
多表查询
1. 内联接
释义:A={ 1, 2, 3, 4, 5 } B= { 2, 5, 8 } A∩B = { 2, 5 }
sql 简单写法:
select a.列1, a.列2, b.列1, b.列2
from A a, B b
where a.主键 = b.外键
A:表名
B:表名
sql 标准写法:
select a.列1, a.列2, b.列1, b.列2
from A a
inner join B b
on a.主键 = b.外键
2. 外联接
左外联接
左边有的,右边没有的以 null 填充
sql 写法:
select ...
from ...
left join ...
on ...
例:
select S.studentName,R.subjectNo,R.student.Result
from student as s
left join result as R
on s.studentNo=R.studentNo;
右外联接
右边有的,左边没有的以 null 填充
3. 交叉联接(全联接 | 笛卡尔积)
释义:A={ A, B, C } B= { a, b } A*B = { Aa, Ab, Ba, Bb, Ca, Cb }
sql 简单写法:
select a.*, b.*
from A a, B b
sql 标准写法:
select a.*, b.*
from A a CROSS join B b
三、部署
此推荐和本文无直接关系:
番外:如何上线你的项目?这里提供了代码,可以快速体验项目上线的快感
云服务器上安装系统
点击这里可查看如何进入宝塔:
前后端项目部署到服务器
-
完成前端打包相关配置
-
打包完成后会生成 dist 文件夹
-
将打包后的
dist
文件夹整个粘贴到后端项目的public
文件夹下
-
在服务器的
/www/wwwroot
目录下新建一个文件夹,用于存放项目文件
-
将 【第3步提及到的】后端文件整个压缩,然后直接拖到【第4步提及到的】服务器新建的文件夹中,并解压(压缩包可删除)
-
至此,前后端均已上传到服务器上。下面的操作,要将项目跑起来,在浏览器中可以在线查看
-
在服务器的宝塔操作面板上,添加 Node 项目 → 选择项目目录【第4步创建的】
8. 启动服务
- ip + 端口 即为项目的访问路径
- 完毕
连接服务器数据库
- 将本地的数据库导出
- 导出到任意位置
-
在服务器的控制台中,在宝塔面板 → 数据库 中,添加数据库
-
点击 phpMyAdmin 后,访问线上数据库
-
导入
-
选择刚刚导出到本地的 .sql 文件,执行
-
稍等一会儿,就导入完毕了。
导入完毕后可能发会遇到一些报错,暂时不要理。
-
点击权限,选择所有人(不安全)
-
在本地创建新连接
-
对应输入信息后,如果测试连接成功,点击确定
-
已在本地连接上了服务器上的数据库
问题:部署到服务器上之前,在本地跑没有问题;打包部署到服务器上的时候,可以访问前端页面,但是一进行刷新操作,页面就会一片空白。
原因:前端路由配置有问题。
解决:调整前端的路由模式。
核心操作:修改 router 配置中的 createWebHistory 为 createWebHashHistory
问题:图片上传失败,并且在前端展示失败
解决办法:
- 在宝塔下载 Nginx → 点击设置 → 配置参数 → 保存
listen 8066; # 没有被占用的端口
server_name localhost; # 固定 localhost
index index.html index.htm index.php; # 不用改
root /www/wwwroot/koa2/public; # 服务器上的图片资源路径
try_files $uri $uri/ /index.html; # 固定内容,没有请自行添加
- 重启 nginx 服务
- 完毕后,以后的图片资源都可以在 :【1. 中 没有被占用的端口】/图片路径读取到图片资源。如:
- 网站为:
- 头像为:
- 拼接起来,即可成功访问到服务器中的图片:
要点:上传图片的到服务器时候,用的依旧是 api 接口的端口号;从服务器获取图片的时候,用的是 Nginx 配置中的端口号。
问题:隐藏域名后面的端口号
背景:绑定后域名,要加上端口号才能正常访问到网页。想要去除端口后,网页也能正常访问到。怎么优化?
方法:
- 在宝塔的网站模块,点击设置,配置一下
80 端口是默认端口,默认不显示。访问到 80 端口时,代理到了 8088 端口,实现了去除域名后+端口才能访问网页的问题。
- 保存配置,重启下服务,即可!
项目演示地址
前端的项目链接 (⑅˃◡˂⑅) 记得在 github 点赞收藏哦!
后端的项目链接 (⑅˃◡˂⑅) 记得在 github 点赞收藏哦!