为什么会有这篇文章
2020年末,作为一个前端小菜鸟,去找工作说只会vue开发,感觉已经很没有优势了,除非将vue吃的很透,然后很清楚vue的源码的实现,做到精通的程度,那像我这样‘笨前端’,就要想一些方法,来弥补和大佬们之间的差距了,所以准备在这段时间,把nodejs和react框架学习一边,完成一套基于react和nodejs开发的后台管理系统,起码能做到对react和nodejs服务端有一定的了解,然后写这篇文章,仅仅是担心时间久了,忘了当初怎么写的,随意记录一下。
这篇帖子只针对nodejs开发,可能一篇的话会写的太长,其他的内容会分成另外几篇文章来写。然后说明一下就是这篇帖子,不是我自己个人去读nodejs的文档,自己写出来的,而是根据B站上的大佬的视频教程,一步步跟着实现的,文章尽量做到每个细节不出问题。
仅使用nodejs
顾名思义就是不使用诸如express,koa,egg,nest这些框架,实现简单的增删改查 + 登录的接口
首先项目的目录结构如下:
-bin --------------放置入口文件代码
--www.js ------入口文件
-src --------------这里面就是各个模块的代码
-config ------配置文件目录
--db.js --数据库配置文件
-controller----控制器方法文件夹
-db------------数据库访问目录
--mysql.js -创建数据库链接
-model --------各个路由的模型目录
-router -------路由目录
index.js ----------入口文件需要引入的server
package.json ------npm包配置
这里没有列出路由,控制器,模型下面的文件,后面创建的时候会说明位置
首先/bin/www.js作为入口文件,代码如下
const http = require('http') // 引入http模块
const PORT = 3000
const serverHandle = require('../index') // 引入index.js
const server = http.createServer(serverHandle) // 创建http服务
server.listen(PORT) // 监听服务器端口
console.log(`http://localhost/${PORT}`)
/index.js中
const handleBlogRouter = require('./src/router/blog')
const handleUserRouter = require('./src/router/user')
const querystring = require('querystring') // 解析query
// 获取postdata
const getPostData = (req)=>{
const promise = new Promise((resolve, reject)=>{
if(req.method !== 'POST' || req.headers['content-type'] !=='application/json') {
resolve({})
return
}
let postData = ''
req.on('data', chunk=>{
postData += chunk.toString()
})
req.on('end', ()=>{
if(!postData) return resolve({})
resolve(JSON.parse(postData))
})
})
return promise
}
const serverHandle = (req, res)=>{
res.setHeader('Content-type', 'application/json')
const url = req.url
req.path = url.split('?')[0]
req.query = querystring.parse(url.split('?')[1]) // 处理postdata
getPostData(req).then(data=>{
req.body = data
const blogResult = handleBlogRouter(req, res)
if(blogResult) {
blogResult.then(blogData=>{
return res.end(JSON.stringify(blogData))
})
}
const userResult = handleUserRouter(req, res)
if(userResult){
userResult.then(userData=>{
return res.end(JSON.stringify(userData))
})
}
}).catch(()=>{
res.writeHead(404, {"Content-type": "text/plain"})
res.write("404 Note Found \n")
res.end()
})}
module.exports = serverHandle
配置数据库信息,src/config/db.js
const ENV = process.env.NODE_ENV // 获取当前环境:生产环境dev,开发环境production
let mysql_config;if (ENV === 'dev') {
mysql_config = {
host: 'localhost',
user: 'root',
password: 'root',
port: 3306,
database: 'myblog'
}}
if (ENV === 'producttion') {
mysql_config = {
host: '127.0.0.1',
user: 'root',
password: 'root',
port: 3306,
database: 'myblog'
}}
module.exports = { mysql_config }
连接数据库,/src/db/mysql.js
const mysql = require('mysql')
const { mysql_config } = require('../config/db')// 创建数据库链接配置
const con = mysql.createConnection(mysql_config)
con.connect();
function exec(sql) {
const promise = new Promise((resolve, reject) => {
con.query(sql, (err, result) => {
if (err) {
reject(err)
return
}
resolve(result)
})
})
return promise
}
module.exports = { exec}
在router文件夹下新建 blog.js和user.js
/src/router/blog.js中实现增删改查操作路由
const { getList, getDetail, addNew, updateThis, deleteThis } = require('../controller/blog')
const { SuccessModel, ErrorModel } = require('../model/resModel')
const handleBlogRouter = (req, res) => {
const method = req.method
const id = req.query.id
let body = req.body
if (method === 'GET' && req.path === '/api/blog/list') {
const author = req.query.author || ''
const keyword = req.query.keyword || ''
const result = getList(author, keyword)
return result.then(listData=>{
return new SuccessModel(listData)
})
}
if (method === 'GET' && req.path === '/api/blog/detail') {
const result = getDetail(id)
return result.then(resData=>{
return new SuccessModel(resData)
})
}
if (method === 'POST' && req.path === '/api/blog/new') {
const result = addNew(body)
return result.then(insertData=>{
return new SuccessModel(insertData)
})
}
if (method === 'POST' && req.path === '/api/blog/update') {
const result = updateThis(id, body)
return result.then(updata=>{
if(updata) {
return new SuccessModel('更新成功')
}else {
return new ErrorModel('更新失败')
}
})
}
if (method === 'POST' && req.path === '/api/blog/delete') {
const author = req.query.author || ''
const result = deleteThis(id,author)
return result.then(data=>{
if(data) {
return new SuccessModel('删除成功')
}else {
return new ErrorModel('删除失败')
}
})
}}
module.exports = handleBlogRouter
/src/router/user.js中,登录路由和返回
const { userLogin } = require('../controller/user')
const { SuccessModel, ErrorModel } = require('../model/resModel')
const handleUserRouter = (req, res) => {
const method = req.method
const body = req.body
if (method === 'POST' && req.path === '/api/user/login') {
const {username, password} = body
const result = userLogin(username,password)
return result.then(data=>{
if(data) {
return new SuccessModel(data)
}else {
return new ErrorModel('登录失败,用户名不存在或密码错误')
}
})
}}
module.exports = handleUserRouter
在model文件夹下新建resModel.js,用来处理请求结果返回信息
/src/model/resModel.js
// 公共返回
class BaseModel {
constructor(data, message) {
if(typeof data ==='string') {
this.message = data
data = null
message = null
}
if(data){
this.data =data
}
if(message) {
this.message = message
}
}}
// 成功返回
class SuccessModel extends BaseModel {
constructor(data, message) {
super(data, message)
this.status = 0
}}
// 错误返回
class ErrorModel extends BaseModel {
constructor(data, message) {
super(data, message)
this.status = -1
}}
module.exports = { SuccessModel, ErrorModel}
在controller中新建blog.js和user.js
/src/controller/blog.js,处理增删改查
const { exec } = require('../db/mysql')
const getList = (author, keyword) => {
let sql = 'select * from blog where 1=1 '
if (author) {
sql += `and author=${author} `
} if (keyword) {
sql += `and title like '%${keyword}' `
}
sql += `order by createtime desc`
return exec(sql)}
const getDetail = (id) => {
const sql = `select * from blog where id=${id}`
return exec(sql).then(rows => {
return rows[0]
})}
const addNew = (data = {}) => {
const { title, content, author, cover } = data
const createtime = Date.now()
const sql = `insert into blog (title, content, createtime, updatetime, author, cover) values ('${title}','${content}', '${createtime}','${createtime}', '${author}', '${cover}')`
return exec(sql).then(result => {
return { id: result.insertId }
})}
const updateThis = (id, data = {}) => {
const { title, content, author, cover } = data
const updatetime = Date.now()
const sql = `update blog set title='${title}', content='${content}', updatetime='${updatetime}', author='${author}', cover='${cover}' where id=${id}`
return exec(sql).then(result => {
if(result.affectedRows > 0) {
return true
}else {
return false
}
})}
const deleteThis = (id, author) => {
const sql = `delete from blog where id=${id} and author=${author}`
return exec(sql).then(result=>{
return result.affectedRows>0
})}
module.exports = { getList, getDetail, addNew, updateThis, deleteThis}
/src/controller/user.js,处理用户登录
const { exec } = require('../db/mysql')
const userLogin = (username, password)=>{
const sql = `select username, name from user where username='${username}' and password='${password}'`
return exec(sql).then(result=>{
return result[0] || ''
})}
module.exports = { userLogin}
稍作总结:
使用node开发本质上写的还是JavaScript代码,他和JavaScript的区别,无非就是JavaScript=JavaScript API+ES规范,nodejs = nodejs API + ES规范,服务端和客户端更多的是思想上的转变;服务端拆分成MVC三部分,M:model处理数据返回,C:controller控制路由的方法,V:view暂时不考虑(因为我们没有视图),而路由router控制用户请求的路径,通过将各个路由分成模块,能够使项目结构更清晰。
最后,这个小项目中用到了nodemon热重启node服务,mysql、cross-env存取环境变量,详见package.json;
以上就是实现原生nodejs开发增删改查+用户登录的全部内容,登录部分后续会增加cookie验证,session缓存,以及密码加密功能,在下一篇文章了。
以上内容的完整代码已上传至github: 点击查看
数据库文件放在/src/db/myblog.sql