VUE.JS和NODE.JS构建一个简易的前后端分离静态博客系统(一)
很久以前写的。不想白写,这边记录一下,配合GPT简要回忆一下代码思路。
后台系统
demo_initBlog.js
首先是博客的后台系统,通过node demo_initBlog.js进行初始化。
const fs = require("fs").promises
const path = require('path')
const jsonfile = require('jsonfile')
async function initBlog() {
try {
// 1 创建文件夹,已经创建就不创建
console.log('=> 创建posts文件夹')
let createDir = await fs.mkdir('./source/_posts/', { recursive: true })
console.log(`=> created ${createDir}`);
// 无需创建 => created undefined
// 完全新建 => created D:\2022_8_3_mycnblogs\my_cnblogs\manuscript\source
// 仅_posts不存在 => created D:\2022_8_3_mycnblogs\my_cnblogs\manuscript\source\_posts
// 2 创建./source/post_infos.json用来保存和管理文章信息
let file_name = 'post_infos.json'
let file_path = path.join(__dirname, `source/${file_name}`)
try {
console.log('=> 尝试读取post_infos.json')
let content = await jsonfile.readFile(file_path)
console.log('=> 读取post_infos.json成功!内容如下')
console.log(content)
} catch (err) {
// 没创建才创建,已创建不创建
console.log(err)
console.log('=> post_infos.json未创建!即将创建')
let obj = {
categories: [],
posts: [],
}
jsonfile.writeFile(file_path, obj, { spaces: 2 })
.then(() => {
console.log('=> 回调: Write complete')
})
.catch(err => {
console.log('=> 创建post_infos.json异常')
console.log(err)
})
}
console.log('=> initBlog.js程序已到末尾, 如有需要删除重新创建')
} catch (err) {
console.error(err.message);
}
}
initBlog()
亲爱的朋友,这段代码是用来初始化一个博客的。它做了以下几件事情:
- 首先,它会创建一个名为
posts
的文件夹,用于存放博客文章。如果文件夹已经存在,它就不会再创建,太聪明了吧! - 然后,它尝试读取一个名为
post_infos.json
的文件。这个文件用于存储博客文章的信息,比如分类和文章列表。 - 如果发现
post_infos.json
文件还不存在,它会马上创建一个,并初始化它的内容,使其包含空的分类和文章列表。
简单来说,这段代码会确保你的博客文件夹结构和必要文件都已经创建好,以便于你开始写博客。希望这个解释对你有帮助!
demo_server.js
下面这个应该是一个后台接口+后台的业务逻辑代码。
const express = require('express');
const app = express();
const fs = require("fs").promises
const path = require('path')
const bodyParser = require('body-parser')
const { customAlphabet } = require('nanoid')
// 字母表去掉一个 `-`,这样才可以在网址里面用
const alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
const nanoid = customAlphabet(alphabet, 8)
const cors = require('cors');
const jsonfile = require('jsonfile')
// express本身不能处理Post来的表单数据,通过下面的设置,
// 就可以在req.body中拿到数据
app.use(bodyParser.urlencoded({extended : false}))
app.use(bodyParser.json())
// 解决跨域问题
app.use(cors())
// PayloadTooLargeError: request entity too large
app.use(express.json({ limit: '10000kb'}))
app.use(express.urlencoded({ limit: '10000kb'}))
function utf8readFile(path) {
let opions = {
encoding: 'utf-8',
}
return fs.readFile(path, opions)
}
function cmd_log(str) {
console.log(`[${new Date().toLocaleString()}] ${str}`)
}
const SOURCE_PATH = path.join(__dirname, 'source')
const JSON_NAME = 'post_infos.json'
const JSON_PATH = path.join(SOURCE_PATH, JSON_NAME)
function postIdDirPath(postId) {
return path.join(SOURCE_PATH, '_posts', postId)
}
function htmlFilePath(postId) {
return path.join(postIdDirPath(postId), 'index.html')
}
function readHtmlFile(postId) {
// 读取对应HTML文件内容
return utf8readFile(htmlFilePath(postId))
}
function writeHtmlFile(postId, content) {
// 将内容直接写到对应的HTML文件中
return fs.writeFile(htmlFilePath(postId), content)
}
async function updateJSONFile_posts(dealWithObj) {
// 更新.json文件,传入一个修改json文件中对象的函数
// 1 读
let obj = await jsonfile.readFile(JSON_PATH)
// 2 改
obj = dealWithObj(obj)
// 3 写
await jsonfile.writeFile(JSON_PATH, obj, { spaces: 2 })
return 1 // 随便什么值无所谓
}
async function updateJSONFile_categories(dealWithObj) {
// 更新.json文件,传入一个修改json文件中对象的函数
const category_path = path.join(SOURCE_PATH, 'category_infos.json')
// 1 读出
let obj = await jsonfile.readFile(category_path)
// 2 修改
obj = dealWithObj(obj)
// 3 写回
await jsonfile.writeFile(category_path, obj, { spaces: 2 })
return 1 // 随便什么值无所谓
}
// 随笔列表
app.get('/posts', async function (req, res) {
let { posts } = await jsonfile.readFile(JSON_PATH)
res.send(posts)
})
// 某个随笔信息(info + html)
app.get('/posts/:postId', async function(req, res) {
const POST_ID = req.params.postId
try {
// 1 从.json里读取对应的POST信息
let { posts } = await jsonfile.readFile(JSON_PATH)
let post = posts.find(x => x.id === POST_ID)
// 2 读POST对应的HTML文件
let content = await readHtmlFile(POST_ID)
post['content'] = content
res.send(post)
}catch(err) {
console.log(err)
res.status(500).end()
}
})
// 新随笔
app.post('/posts', async function (req, res) {
let { title, content, category } = req.body
try {
title = title.trim()
if (title === '') throw new Error('标题不能为空')
// POST模板
let post = {
id: nanoid(8),
title: title,
createTime: new Date(),
category: category,
state: '未发布',
pubDate: new Date(),
}
// 1 将POST信息写入.json
await updateJSONFile_posts((obj) => {
obj.posts.unshift(post)
return obj
})
cmd_log("成功写入JSON_PATH: " + title)
// 2 创建对应文件夹和HTML文件
let createDir = await fs.mkdir(postIdDirPath(post.id), { recursive: true })
cmd_log(`created ${createDir} (文件夹)`);
await writeHtmlFile(post.id, content)
cmd_log(`.html文件已经创建`);
res.send(post)
}catch(err) {
console.log(err)
res.status(500).send('创建异常')
}
})
// 更新随笔
app.post('/posts/:postId', async function (req, res) {
const POST_ID = req.params.postId
let { title, content, category, state, pubDate } = req.body
try {
// 将内容覆盖到对应的HTML文件
await writeHtmlFile(POST_ID, content)
// 更新.json中的相关信息
let post
await updateJSONFile_posts((obj) => {
let index = obj.posts.findIndex(x => x.id === POST_ID)
obj.posts[index].title = title
obj.posts[index].category = category
obj.posts[index].state = state
obj.posts[index].pubDate = pubDate
post = obj.posts[index]
return obj
})
res.send(post)
}catch(err) {
console.log(err)
res.status(500).end()
}
})
// 删除随笔
app.delete('/posts/:postId', async function(req, res) {
const POST_ID = req.params.postId
try {
// 1 从.json里删除
let obj = await jsonfile.readFile(JSON_PATH)
obj.posts = obj.posts.filter(post => post.id !== POST_ID)
await jsonfile.writeFile(JSON_PATH, obj, { spaces: 2 })
cmd_log(`修改${JSON_NAME},已移除POST: ` + POST_ID)
// 2 删除文件
await fs.rm(postIdDirPath(POST_ID), { recursive: true, force: true })
cmd_log('相关文件删除成功')
res.end()
}catch(err) {
console.log(err)
res.status(500).send('删除异常')
}
})
// 新类别
app.post('/categories', async function (req, res) {
let { name } = req.body
try {
name = name.trim()
if (name === '') throw new Error('类别名不能为空')
// category
let category = {
id: nanoid(8),
name: name,
visible: true
}
cmd_log(`增加类别${category}`)
// 1 将category信息更新到.json
await updateJSONFile_categories(obj => {
obj.push(category)
return obj
})
res.send(category)
}catch(err) {
console.log(err)
res.status(500).send('创建异常')
}
})
// 类别列表
app.get('/categories', async function (req, res) {
const category_path = path.join(SOURCE_PATH, 'category_infos.json')
res.send(await jsonfile.readFile(category_path))
})
// 删除类别
app.delete('/categories/:id', async function (req, res) {
try {
const id = req.params.id
await updateJSONFile_categories(obj => {
obj = obj.filter(x => x.id !== id)
return obj
})
res.end()
}catch(err) {
console.log(err)
res.status(500).send('删除异常')
}
})
const PORT = 8081
let server = app.listen(PORT, function () {
console.log(`Example app listening on port ${PORT}`)
})
这个代码主要实现了一个简易的博客文章管理系统,具体功能如下:
-
导入必要的库:使用了 Express.js 作为 Web 框架,处理 HTTP 请求,同时还导入了其他辅助库,如 fs (文件系统)、path (处理文件路径)、body-parser (解析请求体数据)、nanoid (生成随机 ID)、cors (处理跨域请求) 以及 jsonfile (读写 JSON 文件)。
-
初始化 Express.js 应用:设置 body-parser 解析请求体数据,并使用 CORS 解决跨域问题。同时设置了 JSON 和 URL 编码的请求体大小限制。
-
定义一些实用函数:
- utf8readFile: 以 utf-8 编码读取指定路径的文件。
- cmd_log: 在控制台输出带有时间戳的日志信息。
- postIdDirPath: 根据文章 ID 生成对应的文章文件夹路径。
- htmlFilePath: 根据文章 ID 生成对应的 HTML 文件路径。
- readHtmlFile: 读取指定文章 ID 的 HTML 文件内容。
- writeHtmlFile: 将内容写入指定文章 ID 的 HTML 文件。
- updateJSONFile_posts: 更新包含文章信息的 JSON 文件,传入一个处理对象的函数。
- updateJSONFile_categories: 更新包含类别信息的 JSON 文件,传入一个处理对象的函数。
-
实现了以下几个路由处理函数:
- 随笔列表:处理 GET 请求,返回所有文章信息。
- 某个随笔信息:处理 GET 请求,根据文章 ID 获取对应文章的信息和 HTML 内容。
- 新随笔:处理 POST 请求,创建新文章,将文章信息添加到 JSON 文件,并创建对应的 HTML 文件。
- 更新随笔:处理 POST 请求,根据文章 ID 更新文章信息,包括更新 JSON 文件和 HTML 文件。
- 删除随笔:处理 DELETE 请求,根据文章 ID 删除文章,包括移除 JSON 文件中的信息和删除 HTML 文件。
- 新类别:处理 POST 请求,创建新的文章类别,将类别信息添加到 JSON 文件。
- 类别列表:处理 GET 请求,返回所有类别信息。
- 删除类别:处理 DELETE 请求,根据类别 ID 删除类别,更新 JSON 文件。
-
启动 Express.js 应用并监听特定端口,等待客户端发起请求。
这个博客文章管理系统的代码包括基础配置、工具函数和路由处理逻辑。用户可以通过不同的 HTTP 请求来创建、更新、删除博客文章和类别。