前言
学习了前端许久,是时候开始做出一个能真正上线的全栈项目了,文章将记录开发的过程详细步骤,在开发中使用的是前端vue.js后端node.js并连接Mysql数据库,最后再将项目通过阿里云给咱们学生免费提供的服务器进行部署上线
正文
第一篇章的主要内容是前期的准备工作,以及应用登录页面的制作,其中包含
- 登录的前端页面(移动端适配、使用vant框架、路由配置等)
- 前后端数据传输与交互(axios数据交互)
- 后端的简单处理(web框架Koa、koa路由、解决跨域、解析post、连接数据库)
- 以及Mysql数据库的构建(vscode拓展的可视化插件)
前端
在开始编写应用的功能之前需要做好准备工作
移动端做适配的操作
lib-flexible 是一个非常流行的移动端适配库,它通过设置一些基础的样式来让页面在不同尺寸的屏幕上能够灵活地调整布局。它确保了用户在不同尺寸的移动设备上都能获得良好的浏览体验。
终端输入npm i lib-flexible
在main.js上引入使用import 'lib-flexible/flexible.js'
reset.css
为全局设置标准样式
CSS Tools: Reset CSS (meyerweb.com)
在main.js上引入使用import './assets/reset.css'
使用框架vant
Vant 是一个轻量、可靠的移动端 Vue 组件库,由有赞团队开发并开源。它提供了一整套基于 Vue.js 的高质量组件,非常适合用于移动端应用的开发。
介绍 - Vant 4 (vant-ui.github.io)
终端输入npm i vant
在main.js上引入使用import 'vant/lib/index.css';
需要使用到什么组件就添加使用什么组件(在官方文档中有详细介绍) 例如这样使用,切记app.mount在最底下
import { Button,Form, Field, CellGroup } from 'vant';
app.use(Button);
app.use(Form);
app.use(Field);
app.use(CellGroup);
app.mount('#app')
给 CSS 加点料
Less (Leaner Style Sheets 的缩写) 是一门向后兼容的 CSS 扩展语言。
Less 快速入门 | Less.js 中文文档 - Less 中文网 (bootcss.com)
终端输入npm install less -D
便可以直接使用
<style lang="less" scoped>
</style>
完成了准备工作就开始写页面(登录和注册页面)
配置路由
配置路由Home.vue和Login.vue,并在主页面App.vue中引出
可以在创建vue模板时自动配置路由也可以手动配置
main.js中引用
import router from './router'
app.use(router)
创建路由,以及组件页面
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/login',
name: 'Login',
component: () => import('../views/Login.vue')
},
{
path: '/home',
name: 'Home',
component: () => import('../views/Home.vue')
},
]
})
export default router
在主页面App.js中router-view显示出子组件页面
使用vant搭建登录页面
在vant中使用到From表单组件来搭建并实现主要的功能
使用到官方文档中的基础用法,复制使用给出的代码,再进行修改
<van-form @submit="onSubmit">
<van-cell-group inset>
<van-field
v-model="username"
name="用户名"
label="用户名"
placeholder="用户名"
:rules="[{ required: true, message: '请填写用户名' }]"
/>
<van-field
v-model="password"
type="password"
name="密码"
label="密码"
placeholder="密码"
:rules="[{ required: true, message: '请填写密码' }]"
/>
</van-cell-group>
<div style="margin: 16px;">
<van-button round block type="primary" native-type="submit">
提交
</van-button>
</div>
</van-form>
整个组件绑定了一个@submit="onSubmit"事件
于是来定义这个onSubmit事件
const onSubmit = async (values) => {
// console.log('submit', values);//向后端发请求,将账号密码传给后端
const res = await axios.post('./user/login',values)
console.log(res);
if(res.code === 800){
router.push('/home')
}
};
定义了一个异步函数onSubmit,接收参数values,这个参数来自于表单提交时收集的username和password
异步函数声明:async关键字意味着这是一个异步函数,它可以包含await表达式,等待异步操作完成。
发送POST请求:使用axios.post方法发送一个POST请求到./user/login的URL,其中values作为请求体的数据被发送。await关键字确保函数会等待axios.post的Promise解析完成,然后将解析的结果赋值给res变量。
处理响应:console.log(res);在控制台输出服务器的响应,方便进行调试。如果响应中的code属性等于800,表示服务器端认为请求成功(这里的800是自定义的成功状态码)。
路由跳转:如果登录成功,router.push('/home');被调用,使应用程序导航到/home路由。
编写页面的css样式
觉得vant中设置的Form表单组件并不符合你的审美,可以找到想修改的类名再深度修改
找到了再深度修改(直接修改不了的哈哈)
:deep(.van-field__label){//深度修改
width: 45px;
}
与后端数据交互(使用到axios)
完成了前端工作,接着将得到的数据给服务器发送一个http请求,给后端接收数据,让后端判断接下来的响应
除了使用axios库还可以使用XMLHttpRequest()、jquery或者直接fetch你的接口,写过文章介绍过他们
JS中的AJAX,异步创建交互式网页的技术 - 掘金 (juejin.cn)
使用axios Axios中文文档 | Axios中文网 (axios-http.cn)
安装axios在终端输入npm install axios
为网络请求新建api文件夹并引入axios:import axios from "axios";
在vue的src目录下新建文件夹api/index.js来专门处理API请求
import axios from "axios";
import { showToast } from 'vant';
axios.defaults.baseURL = "http://localhost:3000";
axios.defaults.headers.post["Content-Type"] = "application/json";
// 请求拦截
// 响应拦截
axios.interceptors.response.use(res => {
if(res.status !== 200){//程序错误
showToast('服务器异常');
return Promise.reject(res);//抛出异常
}else {
if(res.data.code !== 800){//逻辑错误
showToast(res.data.msg)
return Promise.reject(res);
}
}
return res.data;
});
export default axios;
配置axios的全局默认设置
使得所有的POST请求都将以JSON格式发送,并且所有请求都将自动附加http://localhost:3000作为URL的基础路径。
import { showToast } from 'vant';
- 从
vantUI框架中导入了showToast函数。vant是一个轻量级的移动端Vue组件库,showToast用于快速展示一个提示消息。
axios.defaults.baseURL = "http://localhost:3000";
- 设置了
axios的baseURL默认值。这意味着所有通过axios发起的请求都将自动以http://localhost:3000作为URL的起始部分。这可以简化在请求中指定完整URL的工作,只需提供相对于baseURL的部分即可。
axios.defaults.headers.post["Content-Type"] = "application/json";
- 设置了
axios在发送POST请求时的默认Content-Type头部。Content-Type头部告诉服务器请求体中数据的格式。在这里,设置为application/json意味着请求体将包含JSON格式的数据。当使用axios发送POST请求时,如果你没有显式地更改Content-Type,那么它将使用这个默认值。
配置请求拦截和响应拦截
请求拦截在前端朝后端发送请求(这里先待定...),响应拦截是后端向前端发送响应的请求
配置了axios库的响应拦截器,用于处理来自服务器的HTTP响应。响应拦截器是在响应到达客户端后,但在响应被传递给then或catch处理函数之前运行的。
// 响应拦截
axios.interceptors.response.use(res => {
- 设置了
axios的响应拦截器。axios.interceptors.response.use方法接受两个函数作为参数,第一个是成功的回调,第二个是错误的回调。这里只定义了成功的回调,因为错误的回调默认为undefined。
if(res.status !== 200){//程序错误
showToast('服务器异常');
return Promise.reject(res);//抛出异常
} else {
- 检查响应的状态码是否为200。如果不是200,这通常意味着服务器发生了错误。在这种情况下,使用
showToast显示一个错误消息“服务器异常”,并且通过return Promise.reject(res)将响应作为异常抛出。这会导致任何等待这个请求的.then链被跳过,直接进入.catch错误处理。
if(res.data.code !== 800){//逻辑错误
showToast(res.data.msg)
return Promise.reject(res);
}
}
- 如果状态码是200,进一步检查响应数据中的
code字段是否等于800(800是成功代码,之前交代了800是直接设定好的)。如果不是800,这可能意味着业务逻辑上的错误,而不是网络或服务器错误。此时,showToast将显示响应数据中的msg字段,这通常包含了错误的详细信息。同样,通过return Promise.reject(res)抛出异常。
return res.data;
});
- 如果响应状态码是200且
code字段等于800,这意味着请求成功。此时,拦截器返回res.data,这是服务器返回的实际数据。这将被传递给后续的.then处理函数。
export default axios;
- 最后,这段代码导出了配置好的
axios实例,这样其他模块可以使用这个已经配置了响应拦截器的axios实例来进行网络请求。
后端
收到了前端发来的数据,接着设置后端来解析数据和请求,并返回请求
初始化化境
使用node.js写后端,先初始化终端输入npm init -y
使用web框架Koa
Koa 是一个新的 web 框架,由 Express 幕后的原班人马打造, 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。 通过利用 async 函数,Koa 帮你丢弃回调函数,并有力地增强错误处理。 Koa 并没有捆绑任何中间件, 而是提供了一套优雅的方法,帮助您快速而愉快地编写服务端应用程序。
终端输入npm i koa
Koa (koajs) -- 基于 Node.js 平台的下一代 web 开发框架 | Koajs 中文文档 (bootcss.com)
在index.js中引入Koa,并实现监听3000端口,至此最简单的交互完成
const Koa = require('koa')
const app = new Koa()
app.listen(3000, () => {
console.log('项目已启动')
})
记得需要将后端代码运行起来
终端输入npx nodemon index 可以一直监听代码的更新情况
使用koa路由
可以在npm官网找一个koa路由,可以找到koa-route
koa-route - npm (npmjs.com)
也可以找到别的路由,有很多很多,这里使用到@koa/router
@koa/router - npm (npmjs.com)
@koa/router中间件的使用
终端输入npm i @koa/router
新建rotes/user.js文件,这个文件专门用来管理后端的路由
使用Koa框架来定义一个处理用户登录请求的路由处理器。
const router = require('@koa/router')()
- 加载了Koa的路由器模块并创建了一个新的路由器实例。
@koa/router是一个中间件,用于处理HTTP请求的路由。
const { userLogin } = require('../controllers/index')
- 从
../controllers/index模块中导入了userLogin函数,这个函数是连接数据库的方法,负责验证用户登录。
router.prefix('/user')
- 设置了路由前缀为
/user,这意味着所有添加到此路由器的路由路径都会自动加上这个前缀。
router.post('/login', async(ctx) => {
- 定义处理POST请求的路由,路径为
/login。由于前面设置了路由前缀,所以实际的请求路径应该是/user/login。
接下来的代码块是处理函数,当请求到达时会被调用:
// 从请求体中解析到前端传递的参数,去数据库查询是否存在该用户,账号密码是否合法
const { username, password } = ctx.request.body
- 将从前端表单提交的请求体中解构出
username和password。
try{
const result = await userLogin(username, password)
console.log(result);
-
使用
try...catch语句来捕获异步操作中可能出现的错误。 -
await userLogin(username, password)调用了连接数据库的方法userLogin函数,验证用户登录。
if(result.length > 0){//找到了
const data = {
id: result[0].id,
nickname: result[0].nickname,
username: result[0].username,
}
ctx.body = {
code: 800,
data: data,
msg: '登录成功'
}
}else{//没找到
ctx.body = {
code: 801,
msg: '用户名或密码错误',
data: 'error'
}
}
}catch(err){
ctx.body = {
code: 802,
data: err,
msg: '服务器异常'
}
}
- 根据
userLogin函数的返回结果,设置ctx.body来构建响应。如果查询结果长度大于0,说明找到了匹配的用户,返回一个带有用户信息的响应,状态码800表示成功。如果没有找到用户或密码不正确,则返回状态码801和错误信息。如果在执行过程中发生异常,返回状态码802和异常详情。
调用@koa/router路由
看看官方文档,照葫芦画瓢将@koa/router路由引入index.js
const userRouter = require('./routes/user')
app.use(userRouter.routes())
app.use(userRouter.allowedMethods())
userRouter.routes()是一个方法它会检查每个入站请求,看它是否匹配任何已定义的路由,并将请求转发给相应的处理函数。allowedMethods()是@koa/router提供的一种方法,用于处理HTTP方法不支持的情况,即当客户端请求的HTTP方法不在你定义的路由中时,它会自动发送一个错误响应,通常是405 Method Not Allowed。
解决跨域问题
跨域问题是由于浏览器的同源策略(Same-Origin Policy)引起的,这是一种安全措施,用来防止恶意网站读取另一个网站的数据。同源策略要求协议、域名和端口都相同,否则就认为是不同的源。
在使用 Koa 框架开发服务时,可能会遇到需要处理来自不同源的请求,这就需要解决跨域问题。@koa/cors 是一个 Koa 中间件,可以帮助你轻松实现跨域资源共享(CORS)。
在koa处理跨域问题最简单的方法:告诉浏览器要接收我们的数据
使用别人封装好的方法 Koa 中间件@koa/cors
终端下载npm i @koa/cors
在index引入使用
const cors = require('@koa/cors')
app.use(cors())//允许跨域,且先允许跨域再进行路由
还有其他几种库和方法可以用来解决 Koa 应用中的跨域问题:
- koa2-cors:这是一个基于
node-cors开发的 Koa CORS 中间件,允许开发者通过配置选项来设置 CORS 相关的响应头。- 手动设置响应头:开发者可以在 Koa 中间件中手动设置 CORS 相关的响应头,例如
Access-Control-Allow-Origin、Access-Control-Allow-Methods等,来允许特定的源访问资源或允许特定的 HTTP 方法。这种方法比较灵活,但需要手动处理预检请求(OPTIONS 请求。- 使用 nginx 作为反向代理:通过配置 nginx 服务器,可以将所有前端请求先发送到 nginx,再由 nginx 转发到 Koa 应用。这样,nginx 可以处理跨域问题,而 Koa 应用则不需要做任何 CORS 相关的配置。
- 其他CORS 模块:Koa 社区中还有其他一些 CORS 模块,例如
koa-cors,这些模块提供了设置 CORS 响应头的功能,允许开发者根据需要配置跨域策略4。- document.domain:这种方法适用于具有相同主域但不同子域之间的跨域问题。通过设置
document.domain为相同的主域,可以使得不同子域下的页面能够相互访问资源。- window.postMessage:这是一种用于在不同源的窗口之间安全地传递消息的 HTML5 特性。它可以用来实现跨域通信,但主要用于在不同源的 iframe 之间传递消息。
让koa可以解析post请求
Koa 默认情况下不包含解析请求体的中间件,特别是对于 POST 请求,你需要使用第三方库来解析 JSON、字符串或表单编码的数据。@koa/bodyparser 是一个流行的 Koa 中间件,它可以帮助你轻松实现
@koa/bodyparser - npm (npmjs.com)
终端下载npm i @koa/bodyparser
在index引入@koa/bodyparser Koa 中间件
const { bodyParser } = require("@koa/bodyparser");
app.use(bodyParser());
打造一个可以连接数据库的方法
新建一个../controllers/index模块,专门来管理后端对数据库的增删改查,其中包含userLogin登录功能的查找、userFind用户名是否存在的查找、userRegister新用户注册的数据注入
依然使用到第三方封装的方法mysql2
MySQL2 | Quickstart (sidorares.github.io)
终端下载npm install --save mysql2
创建连接池,设置连接池的参数
连接池通过重用以前的连接来帮助减少连接到 MySQL 服务器所花费的时间,当你完成它们时让它们保持打开而不是关闭。 将mysql的数据存放在新文件夹config/index.js中以便后续的信息维护与修改
const { database } = require('../config/index')
// 创建连接池,设置连接池的参数
const pool = mysql.createPool({
host: database.HOST,
user: database.USERNAME,
password: database.PASSWORD,
database: database.DATABASE,
port: database.PORT,
connectionLimit: 10
});
连接池的使用
const allService = {
async query(sql){
try{
//通过连接池来创建连接
const conn = await pool.getConnection()
// 对连接执行某些操作
const [rows,err] = await conn.query(sql)
// 不要忘记释放连接!
pool.releaseConnection(conn);
return Promise.resolve(rows)
}catch(err){
return Promise.reject(err)
}
}
}
allService 对象:
allService是一个包含query异步方法的对象。这个方法用于执行SQL查询。query方法首先尝试从数据库连接池pool获取一个连接。pool.getConnection()是一个异步操作,返回一个连接对象。- 使用获取的连接执行SQL查询,
conn.query(sql)执行传入的SQL语句,并返回一个包含查询结果的数组rows和可能发生的错误err。 - 一旦查询完成,无论成功与否,都会调用
pool.releaseConnection(conn)来释放连接,这是数据库连接池管理的一部分,确保连接可以被其他请求重用。 - 如果查询成功,
query方法会返回一个解析为查询结果的Promise。 - 如果在执行过程中发生错误,
catch块会捕获错误并返回一个被拒绝的Promise。
userLogin登录功能的查找
const userLogin = (username,password) => {
const _sql = `select * from users where username="${username}" and password="${password}";`
return allService.query(_sql)
}
module.exports = {
userLogin
}
userLogin 函数:
userLogin是一个接受username和password作为参数的函数,用于处理用户登录。- 它构建了一个SQL查询字符串
_sql,用于从users表中选择匹配给定用户名和密码的用户记录。这里使用了模板字符串来插入参数值。 - 然后,
userLogin使用连接池调用allService.query并传入构建的SQL查询字符串。它返回由allService.query返回的Promise,该Promise将解析为查询结果或在发生错误时被拒绝
再将userLogin函数抛出给user.js
设置数据库
在连接池中已将notebook数据库连接:
连接池中引入的配置'../config/index'
const config = {
database:{
DATABASE:'notebook',
USERNAME:'root',
PASSWORD:'******',
PORT:'3306',
HOST:'localhost'
}
}
module.exports = config
在登录中已经能够发送sql语句进行查询
创建数据库并引入数据表
下载好mysql和navicat
没有navicat也不要紧,在vscode中有拓展应用呢
接着创建并连接服务
创建notebook数据库(你切记,这个数据库名称必须和前面设置的相对应)
先点击左上角新建数据库,再输入新建数据库名称
数据引入库中便完成了
试试看
输入数据库中有的数据
状态码为800,说明项目正常运行,并可以看到页面跳转至Home.vue
输入数据库中有的数据
页面不跳转,并且显示了showToast弹出框提示用户名或者密码错误
触发响应拦截 状态码为801,说明前后端代码没得问题,逻辑错误,因为数据库中不存在这个数据
结语
万事开头难,在全栈开发的第一篇章中初步配置了前端后端,并连上了数据库,也算是完成了一个小全栈项目,不过目前只写了一个登录页面,接下来要完成注册的功能新用户的数据注入、登录页面的交互功能,应用的主页面等等。