中间件|【实战篇】egg+vue+mongodb实践开发在线文档管理平台——水墨文档

大厂技术高级前端Node进阶
点击上方 程序员成长指北,关注公众号

回复1,加入高级Node交流群
中间件|【实战篇】egg+vue+mongodb实践开发在线文档管理平台——水墨文档
文章图片

转载自:围的围
https://segmentfault.com/a/1190000037621367
最近有小伙伴经常问实战内容,这篇不错,可以学习下!
前言 团队中会遇到在线文档管理的需求,包括技术文档,接口文档, excel 文档,和产品原型的托管等需求,一直没有找到合适的开源项目来满足需求,所以动手实现了个文档管理系统(实现起来并不复杂,该教程只是提供思路,并非最佳实践)
Github: https://github.com/huangwei9527/Ink-wash-docs
演示地址:http://www.inkwash.online/
功能列表
  • [x] 登录注册
  • [x] 工作台|文档列表
  • [x] 文档编辑预览(支持:md, excel,html产品原型托管)
  • [x] 协作编辑
  • [x] 访问权限设置
  • [x] 团队管理
  • [x] 点赞收藏
  • [x] 模板管理
  • [x] 浏览历史
  • [x] 回收站
  • [ ] 文件夹形式阅读(接口文档)
  • [ ] 编辑历史版本
系统界面预览 中间件|【实战篇】egg+vue+mongodb实践开发在线文档管理平台——水墨文档
文章图片
阅读前准备 1、了解 vue 技术栈开发 2、了解 koa3、了解 egg4、了解 mongodb
技术栈 前端:vue: 模块化开发少不了angular,react,vue三选一,这里选择了vue。vuex: 状态管理sass: css预编译器element-ui:不造轮子,有现成的优秀的vue组件库当然要用起来。
服务端:egg.js:企业级框架,按照一套统一的约定进行应用开发,开发十分高效。mongodb:一个基于分布式文件存储的数据库,比较灵活。egg-alinode:阿里提供的免费nodejs服务器性能监控。
工程搭建 这里我们将前后端项目放在同一个目录下管理,分别用 egg 脚手架和 vue-cli3 生成初始化项目,拷贝合并到同一个目录下,记得合并下 package.json 内容。(脚手架生成项目就不介绍了,按照文档来就是了),合并后将 vue 项目 src 目录改为 web ,如下:
··· · |-- app// egg 初始化app目录 |-- config// egg 初始化app目录 |-- public// vue 静态资源目录 |-- web// 原 src 目录,改成 web 用作前端项目目录 · ···

这样的话 我们需要再把我们vue webpack打包配置稍作一下调整,首先是把原先的编译指向src的目录改成 web,其次为了 npm run build 能正常编译 web 我们也需要为 babel-loader 再增加一个编译目录:
  • 根目录新增 vue.config.js ,目的是为了改造 vue 项目入口,改为: web/main.js
    module.exports = { pages: { index: { entry: "web/main.js" } } }

  • babel-loader 能正常编译 web 目录, 在 vue.config.js 新增如下配置
// 扩展 webpack 配置 chainWebpack: config => { config.module .rule('js') .include.add(/web/).end() .use('babel') .loader('babel-loader') .tap(options => { // 修改它的选项... return options }) }

  • package.json 新增前端项目打包命令
"dev-web": "vue-cli-service serve", "build-web": "vue-cli-service build",

至此前后端项目初始化工作就完了,前端开发启动npm run dev-web 后端开发启动 npm run dev
工程目录结构
|-- app--------服务器端项目代码 |--controller--------用于解析用户的输入,处理后返回相应的结果 |--extend--------框架的扩展 |--middleware--------编写中间件 |--model--------Schema数据模型 |--public--------用于放置静态资源 |--service--------用于编写业务逻辑层 |--router.js--------用于配置 URL 路由规则 |-- config--------egg 配置文件 |--config.default.js--------默认配置 |--config.local.js--------开发环境配置 |--config.prod.js--------生产环境配置 |--plugin.js--------配置需要加载的插件 |-- web--------前端项目界面代码 |--common--------前端界面对应静态资源 |--components--------组件 |--config--------配置文件 |--filter--------过滤器 |--pages--------页面 |--router--------路由配置 |--store--------vuex状态管理 |--service--------axios封装 |--App.vue--------App |--main.js--------入口文件 |--permission.js--------权限控制 |-- docs--------预留编写项目文档目录 |-- vue.config.js--------vue webpack配置文件 |-- package.json ... ...

完成项目目录初始化后,接下来先把 mongodb 全局得一些中间件、扩展方法给配置上,为接口开发做好准备工作
mongodb配置 1、安装 mongoose模块
npm install egg-mongoose --save

2、配置 config 文件
// config/plugin.js exports.mongoose = { enable: true, package: 'egg-mongoose', }; // config/config.default.js config.mongoose = { url: 'mongodb://127.0.0.1:27017/inkwash', options: {}, };

全局中间件和扩展配置 1、统一处理接口
后端接口开发中我们需要一个统一得返回格式,可以在 context 对象下扩展个返回数据 function 用于统一处理接口 response data
app 下新建文件夹 extend 新建 context.js
// app/extend/context.js module.exports = { /** * 返回客户端的内容 * @param status // 接口是否成功 * @param body // 返回数据 * @param msg // 返回信息提示 * @param code // 返回状态码 */ returnBody (status = true, body = {}, msg = 'success', code = 200) { this.status = code; this.body = { status: status, body: body, msg, code: code } } }

// 调用 const { ctx } = this; ctx.returnBody(true, {}, "成功");

2、添加统一处理错误得中间件
app文件夹下新建 middleware 文件夹,新建 error_handler.js , 并配置 congfig 全局中间件配置
// app/middleware/error_handler.js module.exports = () => {return async function errorHandler(ctx, next) { try { await next(); } catch (err) { // 所有的异常都会在app上出发一个error事件,框架会记录一条错误日志 ctx.app.emit('error', err, ctx); const status = err.status || 500; // 如果时生产环境的时候 500错误的详细错误内容不返回给客户端 const error = status === 500 && ctx.app.config.env === 'prod' ? '网络错误' : err.message; ctx.body = { msg: error, status: false, body: {}, code: status }; } }; }; // app/middleware/error_handler.js // config/config.default.js 配置全局中间件 config.middleware = [ 'errorHandler'];

jwt鉴权登录认证 1、安装 egg-jwt token生成以及验证包
npm install egg-jwt --save

2、安装完成后在根目录下的 config/plugin.js 配置一下,如:
'use strict'; /** @type Egg.EggPlugin */ module.exports = { jwt: { enable: true, package: "egg-jwt" }, mongoose: { enable: true, package: 'egg-mongoose', } };

3、接下来在 config/config.default.js 里面继续配置:
config.jwt = { secret: "123456"//自定义 token 的加密条件字符串 };

4、在 context 上扩展两个 function , getTokencheckToken 用于生成 token 和验证 token
// app/extend/context.js async getToken(data) { return await this.app.jwt.sign(data, this.app.config.jwt.secret, {expiresIn: 30* 24 * 60 * 60 + 's'}); }, async checkToken(token) { return await this.app.jwt.verify(token, this.app.config.jwt.secret) }

5、编写个中间件实现登录验证拦截 在 app/middleware 文件夹下新建 auth.js
// app/middleware/auth.js module.exports = () => { return async function(ctx, next) { let token = ''; if ( ctx.headers.authorization && ctx.headers.authorization.split(' ')[0] === 'Bearer' ) { token = ctx.headers.authorization.split(' ')[1]; } else if (ctx.query.accesstoken) { token = ctx.query.accesstoken; } else if (ctx.request.body.accesstoken) { token = ctx.request.body.accesstoken; } let user; try{ user = await ctx.checkToken(token); }catch (e) { ctx.returnBody(false,{}, 'Token 无效,请重新登录', 401); } if (!user) { ctx.returnBody(false,{}, 'Token 无效,请重新登录', 401); return; } ctx.request.user = user; await next(); }; };

好了以上配置完成后就开始接下来的核心注册功能相关操作了。
  • 首先我在根目录下的 app/router.js 创建访问路由:
import { Application } from 'egg'; export default (app: Application) => { const { controller, router, jwt } = app; //正常路由 router.post('/auth/register', controller.auth.register); // 只有在需要验证 token 的路由上添加jwt router.post('/user/infor',jwt, controller.user.infor); };

  • 接下来我去编写我的控制器,在根目录下的 app/controller/home.ts 编写内容:这里使用了两个我们在 app/extend/context.js 上扩展的两个通用方法
  1. 通过 ctx.getToken (用户信息 object 对象)将用户信息通过 jwt 生成 token 返回给前端
  2. 通过 ctx.returnBody 返回数据
// app/controller/auth.js const Controller = require('egg').Controller class AuthController extends Controller { async login() { //... 略 } async register() { const { ctx, service } = this; const { username, password, email } = ctx.request.body let userData = https://www.it610.com/article/await ctx.service.user.createUser(username, password, email); userData = userData.toObject(); let userDataStr = JSON.parse(JSON.stringify(userData)); // 生成token let token =await ctx.getToken(userDataStr); ctx.returnBody(true, {access_token: token, userInfo: userData},"注册成功!") } }module.exports = AuthController;

  • 前端请求的时候需要在 headers 里面上默认的验证字断 Authorization 就可以了,如:
axios({ method: 'get', url: 'http://127.0.0.1:7001/user/info', headers:{ // 切记 token 不要直接发送,要在前面加上 Bearer 字符串和一个空格 'Authorization':`Bearer ${token}` } })

  • 接口从 token 获取加密信息
  1. app/extend/context.js 再扩展个 getUser 方法获取 token 加密信息
// app/extend/context.js // 获取用户信息 async getUserData() { var token = this.headers.authorization ? this.headers.authorization : ''; token = token.substring(7) //把Bearer 截取掉,解析的时候不需要加上Bearer let user = {} try { user = this.app.jwt.verify(token, this.app.config.jwt.secret); } catch (err) { user = {} } return user; }

  1. 实现获取个人信息接口
// app/controller/user.js 'use strict'; const Controller = require('egg').Controller; class UserController extends Controller { async info() { let {ctx} = this; let user = await this.ctx.getUserData() ctx.returnBody(true, user) } }module.exports = UserController;

至此我们就实现了 jwt 生成 token , 然后通过前端传过来的 token 获取当前登录用户的信息,jwt 登录授权这块应该是讲完了,其他的业务接口应该实现起来难度不大
md文档编辑 文档编辑器使用 Vdito r, 一款浏览器端的 Markdown 编辑器,支持所见即所得(富文本)、即时渲染(类似 Typora )和分屏预览模式 安装 Vditor
npm install vditor --save

在代码中引入并初始化对象

excel表格编辑 安装 x-data-spreadsheet
npm install x-data-spreadsheet


import Spreadsheet from "x-data-spreadsheet"; // If you need to override the default options, you can set the override // const options = {}; // new Spreadsheet('#x-spreadsheet-demo', options); const s = new Spreadsheet("#x-spreadsheet-demo") .loadData({}) // load data .change(data => { // save data to db }); // data validation s.validate()

axure原型托管 原型 axure 页面托管,参考 WuliHub 让用户上传生成的 html 压缩包,然后解压到静态资源目录,返回访问地址就 ok , 前端拿到原型地址用内嵌 iframe 渲染出来就 ok
打包编译&&静态资源设置 1、配置前端 vue 页面打包命令
// kage.json script新增打包命令 "build-web": "vue-cli-service build",

2、运行 npm run build-web根目录会生成 dist 前端代码静态文件,因为 egg 支持设置多个静态资源目录,这里就直接配置根目录下的dist 文件夹为静态目录, 配置 config
// config/config.default.js config.static = { prefix: '/',// 将静态资源前缀改为'/'(默认是 '/public') dir: [ path.join(__dirname, '../app/public'), path.join(__dirname, '../dist') ] }

打包完成后启动 egg , 就可以通过 http://localhost/:7001/index.html 访问到前端页面了
因为直接访问 http://localhost/:7001404可以再配置个路由重定向,将跟路由 '/' 重定向到 '/index.html'
// app/router.js // 重定向到index页面 app.router.redirect('/', '/index.html', 302);

部署 服务端部署运行 start 命令
npm run start

性能监控 node 服务性能监控这块可以使用阿里免费开源的 alinode1、安装 egg-alinode
npm i egg-alinode

2、插件配置
// config/plugin.js exports.alinode = { enable: true, package: 'egg-alinode', };

3、配置config
// config/config.default.js exports.alinode = { enable: true, appid: 'my app id', secret: 'my app secret', };

这样就可以了,监控数据可以在阿里 Node.js 性能平台控制台看到监控面板
Node 社群

我组建了一个氛围特别好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你对Node.js学习感兴趣的话(后续有计划也可以),我们可以一起进行Node.js相关的交流、学习、共建。下方加 考拉 好友回复「Node」即可。
中间件|【实战篇】egg+vue+mongodb实践开发在线文档管理平台——水墨文档
文章图片

【中间件|【实战篇】egg+vue+mongodb实践开发在线文档管理平台——水墨文档】“分享、点赞、在看” 支持一波????

    推荐阅读