摘要:项目来源以前曾用过搭建自己的博客网站,但感觉很是臃肿。所以一直想自己写一个博客内容管理器。正好近日看完了各个插件的文档,就用着尝试写了这个简约的博客内容管理器。关于后端后端是用作为服务器的,使用了框架。
项目来源
以前曾用过WordPress搭建自己的博客网站,但感觉WordPress很是臃肿。所以一直想自己写一个博客内容管理器。
正好近日看完了Vue各个插件的文档,就用着Vue尝试写了这个简约的博客内容管理器(CMS)。
嗯,我想完成的功能:一个基本的博客内容管理器功能,如后台登陆,发布并管理文章等
支持markdown语法实时编辑
支持代码高亮
管理博客页面的链接
博客页面对移动端适配优化
账户管理(修改密码)
Demo登陆后台按钮在页面最下方“站长登陆”,可以以游客身份登入后台系统。
源码 用到的技术和实现思路: 前端:Vue全家桶Vue.js
Vue-Cli
Vue-Resource
Vue-Router
Vuex
后端:NodeNode.js
mongoDB (mongoose)
Express
工具和语言Webpack
ES6
SASS
整体思路:Node服务端不做路由切换,这部分交给Vue-Router完成
Node服务端只用来接收请求,查询数据库并用来返回值
所以这样做前后端几乎完全解耦,只要约定好restful数据接口,和数据存取格式就OK啦。
后端我用了mongoDB做数据库,并在Express中通过mongoose操作mongoDB,省去了复杂的命令行,通过Javascript操作无疑方便了很多。
Vue的各个插件:vue-cli:官方的脚手架,用来初始化项目
vue-resource:可以看作一个Ajax库,通过在跟组件引入,可以方便的注入子组件。子组件以this.$http调用
vue-router:官方的路由工具,用来切换子组件,是用来做SPA应用的关键
vuex:规范组件中数据流动,主要用于异步的http请求后数据的刷新。通过官方的vue-devtools可以无缝对接
文件目录│ .babelrc babel配置 │ .editorconfig │ .eslintignore │ .eslintrc.js eslintrc配置 │ .gitignore │ index.html 入口页面 │ package.json │ README.md │ setup.html 初始化账户页面 │ webpack.config.js webpack配置 │ ├─dist 打包生成 │ ├─server 服务端 │ api.js Restful接口 │ db.js 数据库 │ index.js │ init.json 初始数据 │ └─src │ main.js 项目入口 │ setup.js 初始化账户 │ ├─assets 外部引用文件 │ ├─css │ ├─fonts │ ├─img │ └─js │ ├─components vue组件 │ ├─back 博客控制台组件 │ ├─front 博客页面组件 │ └─share 公共组件 │ ├─router 路由 │ ├─store vuex文件 │ └─style 全局样式
前端的文件统一放到了src目录下,有两个入口文件,分别是main.js和setup.js,有过WordPress经验应该知道,第一次进入博客是需要设置用户名密码和数据库的,这里的setup.js就是第一次登入时的页面脚本,而main.js则是剩余所有文件的入口
main.jsimport Vue from "vue" import VueResource from "vue-resource" import {mapState} from "vuex" //三个顶级组件,博客主页和控制台共享 import Spinner from "./components/share/Spinner.vue" import Toast from "./components/share/Toast.vue" import MyCanvas from "./components/share/MyCanvas.vue" import store from "./store" import router from "./router" import "./style/index.scss" Vue.use(VueResource) new Vue({ router, store, components: {Spinner, Toast, MyCanvas}, computed: mapState(["isLoading", "isToasting"]) }).$mount("#CMS2")
而后所有页面分割成一个单一的vue组件,放在components中,通过入口文件main.js,由webpack打包生成,生成的文件放在dist文件夹下。
后端文件放在server文件夹内,这就是基于Express的node服务器,在server文件夹内执行
node index
就可以启动Node服务器,默认侦听3000端口。
关于 WebpackWebpack的配置文件主体是有vue-cli生成的,但为了配合后端自动刷新、支持Sass和生成独立的css文件,稍微修改了一下:
webpack.config.jsconst path = require("path") const webpack = require("webpack") const ExtractTextPlugin = require("extract-text-webpack-plugin") const CopyWebpackPlugin = require("copy-webpack-plugin") //萃取css文件,在此命名 const extractCSSFromVue = new ExtractTextPlugin("styles.css") const extractCSSFromSASS = new ExtractTextPlugin("index.css") module.exports = { entry: { main: "./src/main.js", setup: "./src/setup.js" }, output: { path: path.resolve(__dirname, "./dist"), publicPath: "/dist/", filename: "[name].js" }, resolveLoader: { moduleExtensions: ["-loader"] }, module: { rules: [ { test: /.vue$/, loader: "vue", //使用postcss处理加工后的scss文件 options: { preserveWhitespace: false, postcss: [ require("autoprefixer")({ browsers: ["last 3 versions"] }) ], loaders: { sass: extractCSSFromVue.extract({ loader: "css!sass!", fallbackLoader: "vue-style-loader" }) } } }, { test: /.scss$/, loader: extractCSSFromSASS.extract(["css", "sass"]) }, { test: /.js$/, loader: "babel", exclude: /node_modules/ }, { test: /.(png|jpg|gif|svg)$/, loader: "file", options: { name: "[name].[ext]?[hash]" } }, //字体文件 { test: /.woff(2)?(?v=[0-9].[0-9].[0-9])?$/, loader: "url-loader?limit=10000&mimetype=application/font-woff" }, { test: /.(ttf|eot|svg)(?v=[0-9].[0-9].[0-9])?$/, loader: "file-loader" } ] }, plugins: [ //取出css生成独立文件 extractCSSFromVue, extractCSSFromSASS, new CopyWebpackPlugin([ {from: "./src/assets/img", to: "./"} ]) ], resolve: { alias: { "vue$": "vue/dist/vue" } }, //服务器代理,便于开发时所有http请求转到node的3000端口,而不是前端的8080端口 devServer: { historyApiFallback: true, noInfo: true, proxy: { "/": { target: "http://localhost:3000/" } } }, devtool: "#eval-source-map" } if (process.env.NODE_ENV === "production") { module.exports.devtool = "#source-map" module.exports.plugins = (module.exports.plugins || []).concat([ new webpack.DefinePlugin({ "process.env": { NODE_ENV: ""production"" } }), new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }), new webpack.LoaderOptionsPlugin({ minimize: true }) ]) }
运行
npm start
后,node端开启了3000端口,接着运行
npm run dev
打开webpack在8080端口服务器,具有动态加载的功能,并且所有的http请求会代理到3000端口
关于Vue-Router因为写的是但也应用(SPA),服务器不负责路由,所以路由方面交给Vue-Router来控制。
router.jsimport Vue from "vue" import Router from "vue-router" //博客页面 import Archive from "../components/front/Archive.vue" import Article from "../components/front/Article.vue" //控制台页面 import Console from "../components/back/Console.vue" import Login from "../components/back/Login.vue" import Articles from "../components/back/Articles.vue" import Editor from "../components/back/Editor.vue" import Links from "../components/back/Links.vue" import Account from "../components/back/Account.vue" Vue.use(Router) export default new Router({ mode: "history", routes: [ {path: "/archive", name: "archive", component: Archive}, {path: "/article", name: "article", component: Article}, {path: "/", component: Login}, { path: "/console", component: Console, children: [ {path: "", component: Articles}, {path: "articles", name: "articles", component: Articles}, {path: "editor", name: "editor", component: Editor}, {path: "links", name: "links", component: Links}, {path: "account", name: "account", component: Account} ] } ] })文档首页 index.html
cms2simple
可以看到路由控制在body元素下的router-view中。前面的spinner,toast元素分别是等待效果(转圈圈)的弹出层和信息的弹出层,和背景样式的切换。
关于后端后端是用node.js作为服务器的,使用了express框架。
其中代码非常简单:
index.jsconst fs = require("fs") const path = require("path") const express = require("express") const favicon = require("serve-favicon") const bodyParser = require("body-parser") const cookieParser = require("cookie-parser") const db = require("./db") const resolve = file => path.resolve(__dirname, file) const api = require("./api") const app = express() // const createBundleRenderer = require("vue-server-renderer").createBundleRenderer app.set("port", (process.env.port || 3000)) app.use(favicon(resolve("../dist/favicon.ico"))) app.use(bodyParser.json()) app.use(bodyParser.urlencoded({extended: false})) app.use(cookieParser()) app.use("/dist", express.static(resolve("../dist"))) app.use(api) app.post("/api/setup", function (req, res) { new db.User(req.body) .save() .then(() => { res.status(200).end() db.initialized = true }) .catch(() => res.status(500).end()) }) app.get("*", function (req, res) { const fileName = db.initialized ? "index.html" : "setup.html" const html = fs.readFileSync(resolve("../" + fileName), "utf-8") res.send(html) }) app.listen(app.get("port"), function () { console.log("Visit http://localhost:" + app.get("port")) })
服务器做的事情很简单,毕竟路由在前端。在接受请求的时候判断一下数据库是否初始化,如果初始化就转向主页,否则转向setup.html,之所以没有直接sendfile是因为考虑到之后添加服务端渲染(虽然主页并没有啥值得渲染的,因为很简单)
express框架中使用了mongoose来连接mongoDB数据库,在接收请求时做对应的curd操作,比如这就是在接收保存文章时对应的操作:
api.jsrouter.post("/api/saveArticle", (req, res) => { const id = req.body._id const article = { title: req.body.title, date: req.body.date, content: req.body.content } if (id) { db.Article.findByIdAndUpdate(id, article, fn) } else { new db.Article(article).save() } res.status(200).end() })后记
当然还有很多没提及的地方,最早写这个博客管理器的时候用的还是vue 1.x,后来用2.0改写后文档一直没改,所以最近更新了一下,避免误解。
其实整个管理器最复杂的地方时vuex异步数据视图的部分,不过这一部能讲的太多,就不在这里展开了,可以看官方文档后,参考源代码的注释。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/80406.html
摘要:利用中间件实现异步请求,实现两个用户角色实时通信。目前还未深入了解的一些概念。往后会写更多的前后台联通的项目。删除分组会连同组内的所有图片一起删除。算是对自己上次用写后台的一个强化,项目文章在这里。后来一直没动,前些日子才把后续的完善。 欢迎访问我的个人网站:http://www.neroht.com/ 刚学vue和react时,利用业余时间写的关于这两个框架的训练,都相对简单,有的...
摘要:三更新内容在原来项目的基础上,做了如下更新数据库重新设计,改成以用户分组的数据库结构应数据库改动,所有接口重新设计,并统一采用和网易立马理财一致的接口风格删除原来游客模式,增加登录注册功能,支持弹窗登录。 这个项目最初其实是fork别人的项目。当初想接触下mongodb数据库,找个例子学习下,后来改着改着就面目全非了。后台和数据库重构,前端增加了登录注册功能,仅保留了博客设置页面,但是...
摘要:开发一个完整博客流程前言前段时间刚把自己的个人网站写完,于是这段时间因为事情不是太多,便整理了一下,写了个简易版的博客系统服务端用的是框架进行开发技术栈目录结构讲解的配置文件放置代码文件项目参数配置的文件日志打印文件项目依赖模块 Vue + Node + Mongodb 开发一个完整博客流程 前言 前段时间刚把自己的个人网站写完, 于是这段时间因为事情不是太多,便整理了一下,写了个简易...
阅读 975·2021-11-22 09:34
阅读 2160·2021-11-11 16:54
阅读 2195·2021-09-27 14:00
阅读 938·2019-08-30 15:55
阅读 1523·2019-08-29 12:46
阅读 598·2019-08-26 18:42
阅读 637·2019-08-26 13:31
阅读 3182·2019-08-26 11:52