目录
将三条目录下对应得三段代码分别为三个文件放到一个目录下,然后node执行就可以看效果了
就是这段时间不是在实习吗,然后想着再看看后端相关的,就是对数据的增删改查至少想要了解一下,这些其实学校也学到,但是真的就是了解了解,在工作中怎么运用起来还是需要自己再理一理,然后这篇文章就是我用最简单的js写三个文件,一个用来封装增删改查的函数,一个搭建后端服务,一个就写前端界面调用后端接口,完成对数据库的操作
一、封装函数操作数据库
想要操作mysql,就会有对应的库,就不是自己写sql语法来的,而是用别人封装好的一些库,用js语法实现对数据库的操作,类似java啊啥的也是,用maven下载好相应的包后,用对用的语法操作数据库,js的话就是npm install mysql2这样,然后直接创建函数开始操作数据库
下面的操作就是创建了一个pool对象,然后这个pool就可以对数据库进行操作了,执行sql查询语句,对数据库进行增删改查,主要是这个查询接口,将数据库中的字段和分页当前页和当前页显示数作为参数进行查询,默认显示当前页就是不传参数,后面根据搜索就是添加参数
javascript
const mysql = require('mysql2/promise')
// 调用pool.query()执行SQL,然后返回结果,插入时返回结果result指的新增那条记录
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'root',
database: 'test_db',
connectionLimit: 10
})
async function createUser(name, age, email) {
const [result] = await pool.query(
'INSERT INTO user(name, age, email) VALUES(?, ?, ?)',
[name, age, email]
)
console.log(`新增成功: ${result.insertId}`)
return result.insertId
}
async function getUserList(queryParams) {
const { page = 1, pageSize = 10, name, age, email } = queryParams
const whereArr = []
const params = []
if (name !== undefined && name !== '') {
whereArr.push('name LIKE ?') // LIKE就是模糊查询
params.push(`%${name}%`) // 包含的就可以查
}
if (age !== undefined && age !== '') {
whereArr.push('age = ?')
params.push(age)
}
if (email !== undefined && email !== '') {
whereArr.push('email LIKE ?')
params.push(`%${email}%`)
}
let whereSql = ''
if (whereArr.length > 0) {
whereSql = 'WHERE ' + whereArr.join(' AND ')
}
const offset = (Number(page) - 1) * Number(pageSize)
const sqlList = `
SELECT * FROM user
${whereSql}
LIMIT ? OFFSET ?
`
const [list] = await pool.query(sqlList, [...params, Number(pageSize), offset])
const sqlCount = `
SELECT COUNT(*) AS total FROM user
${whereSql}
`
const [countRes] = await pool.query(sqlCount, params)
const total = countRes[0].total
return {
list,
total
}
}
async function findUserById(id) {
const [rows] = await pool.query('SELECT * FROM user WHERE id = ?', [id])
console.log('查询单条:',rows[0])
return rows[0]
}
async function deleteUser(id) {
const [result] = await pool.query('DELETE FROM user WHERE id = ?', [id])
console.log(`删除成功:${result.affectedRows}`)
return result.affectedRows
}
async function updateUser(id, name, age, email) {
const [result] = await pool.query(
'UPDATE user SET name = ?, age = ?, email = ? WHERE id = ?',
[name, age, email, id]
)
console.log(`更新成功:${result.affectedRows}`)
const [rows] = await pool.query('SELECT * FROM user WHERE id = ?', [id])
console.log('更新后查询单条:',rows[0])
return rows[0]
}
module.exports = {
createUser,
getUserList,
findUserById,
deleteUser,
updateUser
}
就是node自带了很多变量:module、require, __filename, __dirname等都是可以直接使用,不用在什么import,或者require('')导入
二、封装http服务
因为要显示网页,网页的数据要从外部获取就得用http服务,创建一个http对象(有两参数:请求来的req,和相应回去的res),res.setHeader就是设置一些请求相应的一些规则,
并在这个开启的http服务中,匹配前端传来的地址后面接的api标识和规定对应标识执行相应的功能,没啥玄乎的,就是判断字符串的变化,可以执行不同操作数据库的内容
javascript
const http = require('http')
const fs = require('fs')
const path = require('path')
const url = require('url')
const db = require('./try3')
const server = http.createServer(async (req, res) => {
// req 请求对象,res 响应对象
res.setHeader('Access-Control-Allow-Origin', '*') // 允许所有域名访问
res.setHeader('Content-Type', 'application/json') // 返回json数据
const urlObj = url.parse(req.url, true)
const pathname = urlObj.pathname
if (pathname === '/' && req.method === 'GET') {
res.setHeader('Content-Type', 'text/html;charset=utf-8') //返回html网页,
// 后端设置这个setHeader是:告诉前端:我返回的是JSON
// 前端设置这个是:告诉后端:我发送的是JSON
const html = fs.readFileSync(path.join(__dirname, './index2.html'), 'utf8')
res.end(html)
return
}
function getBody() { // 获取请求体,将一段一段的数据拼成字符串,然后转成json
return new Promise(resolve => {
let buf = ''
req.on('data', chunk => buf += chunk)
req.on('end', () => resolve(JSON.parse(buf)))
})
}
try {
// 查询所有
if (pathname === '/api/list' && req.method === 'GET') {
const query = urlObj.query // 获取请求参数
const result = await db.getUserList(query)
res.end(JSON.stringify({
code: 200,
msg: 'success',
data: result.list,
total: result.total,
page: Number(query.page) || 1,
pageSize: Number(query.pageSize) || 10
}))
}
// 新增
else if (pathname === '/api/add' && req.method === 'POST') {
const body = await getBody()
const id = await db.createUser(body.name, body.age, body.email)
res.end(JSON.stringify({code: 200, msg: 'success', data: {id}}))
}
//修改
else if (pathname === '/api/update' && req.method === 'POST') {
const body = await getBody()
const data = await db.updateUser(body.id, body.name, body.age, body.email)
if (data) {
res.end(JSON.stringify({ code: 200, msg: '更新成功', data }))
} else {
res.end(JSON.stringify({ code: 400, msg: '更新失败' }))
}
}
// 删除
else if (pathname === '/api/delete' && req.method === 'POST') {
const body = await getBody()
const row = await db.deleteUser(body.id)
res.end(JSON.stringify({ code: 200, msg: '删除成功', data: {row}}))
}
else {
res.end(JSON.stringify({ code: 404, msg: '接口不存在' }))
}
} catch (error) {
res.end(JSON.stringify({ code: 500, msg: '服务器错误' }))
}
})
const PORT = 3000
server.listen(PORT, () => {
console.log(`服务器启动成功,监听端口:${PORT}`)
})
三、前端渲染
前端渲染页面与调用对应标识所对应的后端函数,后端函数封装有操作数据库的功能
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>用户管理CRUD</title>
<style>
body {
max-width: 1200px;
margin: 30px auto;
padding: 0 20px;
}
.search-box {
margin-bottom: 15px;
padding: 12px;
border: 1px solid #eee;
border-radius: 6px;
display: flex;
gap: 8px;
align-items: center;
flex-wrap: wrap;
}
.mask {
position: fixed;
inset: 0;
background: rgba(0,0,0,.4);
display: none;
align-items: center;
justify-content: center;
}
.form-box {
background: #fff;
border: 1px solid #ccc;
padding: 16px;
border-radius: 8px;
width: 360px;
}
.close {
text-align: right;
}
.item {
margin: 10px 0;
display: flex;
align-items: center;
}
.item label {
width: 70px;
}
input {
padding: 6px;
flex: 1;
width: auto;
}
button {
padding: 7px 12px;
cursor: pointer;
border: 1px solid #bbb;
background: #fff;
border-radius: 4px;
}
button:hover {
background-color: #f5f5f5;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 15px;
}
th, td {
border: 1px solid #aaa;
padding: 9px;
text-align: center;
}
th {
background-color: #f8f8f8;
}
.page-box {
margin-top: 12px;
display: flex;
gap: 8px;
align-items: center;
}
select {
padding: 6px;
}
</style>
</head>
<body>
<h2>用户管理页面操作</h2>
<div class="search-box">
<input id="searchName" type="text" placeholder="姓名">
<input id="searchAge" type="text" placeholder="年龄">
<input id="searchEmail" type="text" placeholder="邮箱">
<button onclick="queryUser()">搜索</button>
<button onclick="resetSearch()">重置</button>
<button onclick="showForm()">新增</button>
</div>
<!-- 遮罩+弹窗结构 -->
<div class="mask" id="mask">
<div class="form-box">
<div class="close"><button onclick="closeForm()">X</button></div>
<h4>新增/编辑用户</h4>
<div class="item">
<label>ID: </label>
<input id="uid" placeholder="自动生成" disabled>
</div>
<div class="item">
<label>姓名: </label>
<input id="name" placeholder="请输入姓名">
</div>
<div class="item">
<label>年龄: </label>
<input id="age" placeholder="请输入年龄">
</div>
<div class="item">
<label>邮箱: </label>
<input id="email" placeholder="请输入邮箱">
</div>
<div class="item">
<button onclick="button()">保存</button>
<button onclick="closeForm()">取消</button>
</div>
</div>
</div>
<table>
<thead>
<tr>
<th>序号</th>
<th>ID</th>
<th>姓名</th>
<th>年龄</th>
<th>邮箱</th>
<th>操作</th>
</tr>
</thead>
<tbody id="tableBody"></tbody>
</table>
<!-- 分页区域 -->
<div class="page-box" id="pageBox"></div>
<script>
const baseUrl = 'http://localhost:3000';
let tableDataList = []
// 分页参数
let pageInfo = {
page: 1,
pageSize: 10,
total: 0
}
// 搜索参数
let searchParams = {
name: '',
age: '',
email: ''
}
// 加载列表,拼接全部查询参数 + 分页
async function loadList() {
let url = baseUrl + '/api/list'
const searchStr = new URLSearchParams()
if (searchParams.name) searchStr.append('name', searchParams.name)
if (searchParams.age) searchStr.append('age', searchParams.age)
if (searchParams.email) searchStr.append('email', searchParams.email)
// 追加分页参数
searchStr.append('page', pageInfo.page)
searchStr.append('pageSize', pageInfo.pageSize)
if (searchStr.size > 0) {
url += "?" + searchStr.toString()
}
const res = await fetch(url)
const json = await res.json()
tableDataList = json.data
pageInfo.total = json.total
const html = tableDataList.map((item, index) => {
const num = (pageInfo.page - 1) * pageInfo.pageSize + index + 1
return `
<tr>
<td>${num}</td>
<td>${item.id}</td>
<td>${item.name}</td>
<td>${item.age}</td>
<td>${item.email}</td>
<td>
<button onclick="update(${item.id})">编辑</button>
<button onclick="del(${item.id})">删除</button>
</td>
</tr>
`
}).join('')
document.getElementById('tableBody').innerHTML = html
renderPage()
}
// 渲染分页按钮
function renderPage() {
const totalPage = Math.ceil(pageInfo.total / pageInfo.pageSize)
let pageHtml = `
<span>每页</span>
<select id="sizeSelect" onchange="changePageSize()">
<option value="5" ${pageInfo.pageSize===5?'selected':''}>5</option>
<option value="10" ${pageInfo.pageSize===10?'selected':''}>10</option>
<option value="20" ${pageInfo.pageSize===20?'selected':''}>20</option>
</select>
<span>条</span>
<span>共${pageInfo.total}条</span>
<button onclick="prevPage()" ${pageInfo.page <= 1 ? 'disabled' : ''}>上一页</button>
<span>第${pageInfo.page}/${totalPage || 1}页</span>
<button onclick="nextPage()" ${pageInfo.page >= totalPage ? 'disabled' : ''}>下一页</button>
`
document.getElementById('pageBox').innerHTML = pageHtml
}
// 切换每页条数
function changePageSize() {
pageInfo.pageSize = Number(document.getElementById('sizeSelect').value)
pageInfo.page = 1
loadList()
}
// 上一页
function prevPage() {
if (pageInfo.page > 1) {
pageInfo.page--
loadList()
}
}
// 下一页
function nextPage() {
const totalPage = Math.ceil(pageInfo.total / pageInfo.pageSize)
if (pageInfo.page < totalPage) {
pageInfo.page++
loadList()
}
}
// 填充表单(编辑)
function fillForm(row) {
document.getElementById('uid').value = row.id
document.getElementById('name').value = row.name
document.getElementById('age').value = row.age
document.getElementById('email').value = row.email
}
// 清空表单
function clearForm() {
document.getElementById('uid').value = ''
document.getElementById('name').value = ''
document.getElementById('age').value = ''
document.getElementById('email').value = ''
}
// 新增显示弹框
function showForm() {
document.getElementById('mask').style.display = 'flex'
}
// 关闭弹框
function closeForm() {
document.getElementById('mask').style.display = 'none'
clearForm()
}
// 编辑显示弹框(根据id查找数据)
function update(id) {
const row = tableDataList.find(item => item.id === id)
if (!row) return
document.getElementById('mask').style.display = 'flex'
fillForm(row)
}
// 新增和编辑的保存
async function button() {
const id = document.getElementById('uid').value
if (id) { // 编辑
const data = {
id:id,
name: document.getElementById('name').value,
age: Number(document.getElementById('age').value),
email: document.getElementById('email').value
}
const res = await fetch(baseUrl + '/api/update', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
})
const ret = await res.json()
alert(ret.msg)
closeForm()
loadList()
} else { // 新增
const data = {
name: document.getElementById('name').value,
age: Number(document.getElementById('age').value),
email: document.getElementById('email').value
}
const res = await fetch(baseUrl + '/api/add', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
})
const ret = await res.json()
alert(ret.msg)
closeForm()
loadList()
}
}
// 删除
async function del(id) {
if (!confirm('确认删除?')) return
await fetch(baseUrl + '/api/delete', {
method: 'POST',
headers: { 'Content-Type':'application/json'},
body: JSON.stringify({id})
})
loadList()
}
// 搜索,搜索时重置回第一页
function queryUser() {
searchParams.name = document.getElementById('searchName').value.trim()
searchParams.age = document.getElementById('searchAge').value.trim()
searchParams.email = document.getElementById('searchEmail').value.trim()
pageInfo.page = 1
loadList()
}
// 重置搜索条件
function resetSearch() {
document.getElementById('searchName').value = ""
document.getElementById('searchAge').value = ""
document.getElementById('searchEmail').value = ""
searchParams.name = ''
searchParams.age = ''
searchParams.email = ''
pageInfo.page = 1
loadList()
}
loadList()
</script>
</body>
</html>
这样三个文件完成了各自分工,操作数据库的函数文件,http服务文件,前端渲染文件实现了一个简洁的管理用户界面
感觉越学越回去了,学到后面了解了vue,nestjs,各种图形库,但是用着用着就像看看底层咋来的,这个简单的html + js希望能够在2026年帮你看一看,不过现在ai确实太强大,像这种代码,两分钟不到就可以生成,也可以自己理一理