如何在Node.js+MongoDB环境中实现Quill编辑器?含博客实践疑问
Node.js + MongoDB 环境下用 Quill 搭建博客可视化编辑方案
嘿,你的这个方案完全可行!Quill 本身就围绕 Delta 格式设计,这种轻量结构化的内容存在 MongoDB(文档型数据库)里简直天作之合,再用 quill-render 转成 HTML 展示也完全没问题。我来一步步给你拆解实现步骤,帮你搞定个人博客的可视化编辑功能:
一、先确认方案可行性
Quill 的 Delta 是它的核心数据格式,本质是描述内容变化的 JSON 对象,相比直接存 HTML 更灵活、体积更小,而且方便后续修改和扩展。MongoDB 作为文档数据库,天然支持存储这种 JSON 结构的数据。quill-render 确实能把 Delta 准确转换成前端能展示的 HTML,所以你的思路完全没问题,放心推进就行!
二、具体实现步骤
1. 前端集成 Quill 编辑器
首先得把 Quill 加到你的前端页面里:
- 可以通过 npm 安装:
npm install quill,然后在代码里导入;或者直接用 CDN 引入 Quill 的 JS 和 CSS 文件 - 给编辑器准备一个容器:
<div id="editor"></div> - 初始化编辑器,配置工具栏(尤其是要开启图片上传功能):
import Quill from 'quill'; import 'quill/dist/quill.snow.css'; const quill = new Quill('#editor', { theme: 'snow', toolbar: [ ['bold', 'italic', 'underline', 'strike'], ['blockquote', 'code-block'], [{ 'header': 1 }, { 'header': 2 }], [{ 'list': 'ordered'}, { 'list': 'bullet' }], [{ 'color': [] }, { 'background': [] }], ['link', 'image'], // 必须包含图片按钮 ['clean'] ] });
2. 处理图片上传(关键!别用默认的 base64)
默认情况下,Quill 插入图片会转成 base64 存在 Delta 里,这种方式体积大,存数据库非常不划算。咱们得改成上传到服务器/云存储,只存图片 URL:
- 监听 Quill 的图片上传事件,或者自定义工具栏的图片按钮行为:
// 自定义图片上传逻辑 const toolbar = quill.getModule('toolbar'); toolbar.addHandler('image', function () { const input = document.createElement('input'); input.setAttribute('type', 'file'); input.setAttribute('accept', 'image/*'); input.click(); input.onchange = async function () { const file = input.files[0]; const formData = new FormData(); formData.append('image', file); // 调用后端上传接口 const res = await fetch('/api/upload-image', { method: 'POST', body: formData }); const data = await res.json(); // 把返回的图片URL插入编辑器 const range = quill.getSelection(); quill.insertEmbed(range.index, 'image', data.imageUrl); quill.setSelection(range.index + 1); }; });
3. 后端实现图片上传接口(Node.js + Express)
用 Express 写一个接口处理图片上传,这里以本地存储为例(也可以换成阿里云OSS、七牛云等):
- 先安装依赖:
npm install multer(处理文件上传的中间件) - 写上传接口:
const express = require('express'); const multer = require('multer'); const path = require('path'); const router = express.Router(); // 配置multer存储路径和文件名 const storage = multer.diskStorage({ destination: function (req, file, cb) { cb(null, path.join(__dirname, '../public/uploads')); // 确保uploads文件夹存在 }, filename: function (req, file, cb) { cb(null, Date.now() + '-' + file.originalname); // 避免文件名重复 } }); const upload = multer({ storage: storage }); router.post('/upload-image', upload.single('image'), (req, res) => { // 返回图片的访问URL,比如http://localhost:3000/uploads/xxx.jpg const imageUrl = `/uploads/${req.file.filename}`; res.json({ imageUrl }); }); module.exports = router;
别忘了在 Express 里配置静态文件服务,让前端能访问上传的图片:app.use(express.static('public'));
4. 存储 Delta 到 MongoDB
接下来把编辑器的 Delta 内容和博客其他字段(标题、分类等)存到 MongoDB:
- 定义 MongoDB 的 Schema(用 Mongoose):
const mongoose = require('mongoose'); const blogSchema = new mongoose.Schema({ title: { type: String, required: true }, contentDelta: { type: Object, required: true }, // 直接存Delta对象 createdAt: { type: Date, default: Date.now }, updatedAt: { type: Date, default: Date.now } }); module.exports = mongoose.model('Blog', blogSchema);
- 写保存博客的后端接口:
const Blog = require('../models/Blog'); router.post('/save-blog', async (req, res) => { try { const { title, contentDelta } = req.body; const blog = new Blog({ title, contentDelta }); await blog.save(); res.json({ success: true, blogId: blog._id }); } catch (err) { res.status(500).json({ success: false, message: err.message }); } });
- 前端提交内容时,调用这个接口:
// 点击保存按钮时触发 async function saveBlog() { const title = document.getElementById('title').value; const contentDelta = quill.getContents(); // 获取编辑器的Delta内容 const res = await fetch('/api/save-blog', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title, contentDelta }) }); const data = await res.json(); if (data.success) { alert('博客保存成功!'); } }
5. 从 MongoDB 取出 Delta 并渲染为 HTML
用 quill-render 把 Delta 转换成 HTML,用于博客展示:
- 安装 quill-render:
npm install quill-render - 写获取博客并渲染的接口:
const quillRender = require('quill-render'); const Blog = require('../models/Blog'); router.get('/get-blog/:id', async (req, res) => { try { const blog = await Blog.findById(req.params.id); if (!blog) { return res.status(404).json({ message: '博客不存在' }); } // 把Delta转成HTML const htmlContent = quillRender(blog.contentDelta); res.json({ title: blog.title, contentHtml: htmlContent, createdAt: blog.createdAt }); } catch (err) { res.status(500).json({ message: err.message }); } });
6. 前端展示博客内容
拿到后端返回的 HTML 后,直接插入页面即可:
async function loadBlog(blogId) { const res = await fetch(`/api/get-blog/${blogId}`); const data = await res.json(); document.getElementById('blog-title').textContent = data.title; document.getElementById('blog-content').innerHTML = data.contentHtml; }
三、几个重要的注意事项
- XSS 防护:用户输入的内容可能包含恶意脚本,一定要在渲染前过滤!可以用
xss包对生成的 HTML 进行处理:const safeHtml = xss(htmlContent); - Quill 版本兼容:前后端尽量用相同版本的 Quill 和 quill-render,避免 Delta 格式不兼容导致渲染错误
- 图片存储优化:如果博客访问量较大,建议用云存储(OSS、Cloudinary等)代替本地存储,更稳定也方便扩容
- 备份 Delta:尽量一直存储 Delta 格式,而不是只存渲染后的 HTML,这样以后如果需要修改编辑器或者换渲染方式,都能灵活处理
内容的提问来源于stack exchange,提问作者Sam Min Wong
相关产品推荐
相关产品推荐

