前言
感谢 Jonny 的指导,尝试摸着石头过河。自己手写一个简单的图书管理系统作为我的入门项目。不知道能不能成功,也许中途就放弃也有可能。(毕竟人很懒)
技术栈
可能不太准确,毕竟还没太搞清楚。后续若有误会进行勘误。
前端
使用 Lit + TypeScript + Stylus + Pug 配合 Vite 打包器构建前端。
后端
使用 TypeScript + MySQL 数据库构建后端。
项目初始化
项目结构
- Library
- node_modules
- serve
- dist
- node_modeles
- src
- package-lock.json
- package.json
- tsconfig.json
- web
- dist
- layout
- node_modeles
- scripts
- styles
- index.html
- package-lock.json
- package.json
- tsconfig.json
- vite.config.json
- .gitignore
- biome.json
- package-lock.json
- package.json
- README.md
- tsconfig.json
安装 MySQL 数据库
请移步至文章了解 MySQL 数据库
下载依赖
总体依赖
代码格式化工具 biome
1
| npm install --save-dev @biomejs/biome
|
初始化 biome ,会生成一个 biome.json 文件,可以在其中进行配置
首先在扩展商店下载 biome 插件,然后进入 setting.json 设置保存自动格式化
1 2 3 4 5 6 7
| { "editor.defaultFormatter": "biomejs.biome", "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.fixAll.biome": "explicit" } }
|
若出现奇怪原因 biome 报错,可以尝试重启 biome 解决
- 在 VS Code 中按 Ctrl+Shift+P。
- 输入 Biome: Restart Biome Server。
后端依赖
安装 TypeScript 和 @types/node
1
| npm install -D typescript @types/node
|
安装 TS 运行工具,支持热更新
安装 MySQL2 驱动
添加脚本
1 2 3 4 5
| "scripts": { "dev": "tsx watch src/app.ts", "build": "tsc", "serve": "node dist/app.js" }
|
配置 tsconfig.json 文件,运行以下代码生成中文注释的配置文件
1
| tsc --init --locale zh-CN
|
前端依赖
添加脚本
1 2 3 4 5
| "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "vite", "build": "vite build" },
|
配置 tsconfig.json 文件,运行以下代码生成中文注释的配置文件
需要自行创建 vite.config.ts 文件,将配置写入其中
1
| tsc --init --locale zh-CN
|
安装 lit 核心框架
安装类型定义
1
| npm install -D @types/node
|
安装 styule 预处理器
安装 vite 插件用于支持 pug
1
| npm install -D vite-plugin-pug
|
安装 Vite 打包器,支持热更新
创建 vite.config.ts 配置文件,写入配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import { defineConfig } from "vite"; import pugVite from "vite-plugin-pug";
const HOST = "127.0.0.1"; const PORT = 5600;
export default defineConfig({ plugins: [pugVite()], server: { port: PORT, strictPort: true, host: HOST, }, build: { outDir: "dist", emptyOutDir: true, rollupOptions: { input: "index.html", }, }, });
|
实现功能: 向数据库中存入数据
前端部分语句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| ... const userdata: LoginData = { username: usernameInput.value, password: passwordInput.value, };
try { const response = await fetch("http://127.0.0.1:3000/api/addUser", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ username: userdata.username, password: userdata.password, }), });
const data = await response.json(); outPutdiv.innerHTML = `${JSON.stringify(data)}`; } catch (e) { outPutdiv.innerHTML = `未知错误:${e}`; } ...
|
后端部分语句
- 路由处理
1 2 3 4 5
| ... if (urlPath === "/api/addUser" && method === "POST") { addUser(req, res); } ...
|
- 处理数据并发送结果 (其实不管成不成功都会发送结果)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| let buffer = Buffer.alloc(0); req.on("data", (chunk) => { buffer = Buffer.concat([buffer, chunk]); }); req.on("end", () => { try { const body = buffer.toString(); const data = JSON.parse(body); const { username, passworld } = data; addAccount(username, passworld); res.writeHead(200, { "content-type": "application/json" }); res.end( JSON.stringify({ message: "Add succesfully", }), ); } catch (e) { res.writeHead(400, { "content-type": "application/json" }); res.end(JSON.stringify({ error: `Invalid JSON :${e}` })); } });
|
- 向数据库插入信息
1 2 3 4 5 6
| export async function addAccount(username: string, password: string) { await pool.query(` INSERT INTO library_db.user (username, passworld) VALUE ('${username}','${password}'); `); }
|
实现功能:从数据库中读取数据
前端部分语句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| ... const response = await fetch("http://127.0.0.1:3000/api/login", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ username: username, password: password, }), }); const data = await response.json(); if (response.ok) { outPutdiv.innerHTML = `登录成功:${JSON.stringify(data)}`; } else { outPutdiv.innerHTML = `登录失败:${data.error || data.message || JSON.stringify(data)}`; } ...
|
后端部分语句
- 路由处理
1 2 3 4 5
| ... else if (urlPath === "/api/login" && method === "POST") { logInPost(req, res); } ...
|
- 后端处理逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| ... const data = (await received(req)) as { username: string; password: string; }; const { username, password } = data; if (!username || !password) { res.writeHead(400, { "content-type": "application/json" }); res.end(JSON.stringify({ error: "用户名与密码不能为空" })); } if (!(await verifyUserPassword(username, password))) throw new Error("密码不正确"); else { res.writeHead(200, { "content-type": "application/json" }); res.end(JSON.stringify({ message: "密码正确,登录成功" })); } ...
|
- 数据库处理部分 (密码并未加密,极其简单的逻辑进行验证)
1 2 3 4 5 6 7 8 9 10 11 12
| const sql = "SELECT password FROM `user` WHERE username = ? LIMIT 1"; const [rows] = await pool.query(sql, [username]); const users = rows as any[]; if (users.length === 0) { return false; }
const storedHash = users[0].password;
if (storedHash !== plainTextPassword) { return false; } else return true;
|
后面不再贴代码上来了,因为其实上述代码经过好几次优化后已经不长这样了…
任务列表
后端开发
前端开发
结语
有好多东西都不懂…边做边学吧 (;′⌒`)