资讯专栏INFORMATION COLUMN

Vue.js实践:一个Node.js+mongoDB+Vue.js的博客内容管理系统

Dr_Noooo / 1979人阅读

摘要:项目来源以前曾用过搭建自己的博客网站,但感觉很是臃肿。所以一直想自己写一个博客内容管理器。正好近日看完了各个插件的文档,就用着尝试写了这个简约的博客内容管理器。关于后端后端是用作为服务器的,使用了框架。

项目来源

以前曾用过WordPress搭建自己的博客网站,但感觉WordPress很是臃肿。所以一直想自己写一个博客内容管理器。

正好近日看完了Vue各个插件的文档,就用着Vue尝试写了这个简约的博客内容管理器(CMS)。

嗯,我想完成的功能:

一个基本的博客内容管理器功能,如后台登陆,发布并管理文章等

支持markdown语法实时编辑

支持代码高亮

管理博客页面的链接

博客页面对移动端适配优化

账户管理(修改密码)

Demo

登陆后台按钮在页面最下方“站长登陆”,可以以游客身份登入后台系统。

源码 用到的技术和实现思路: 前端:Vue全家桶

Vue.js

Vue-Cli

Vue-Resource

Vue-Router

Vuex

后端:Node

Node.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.jssetup.js,有过WordPress经验应该知道,第一次进入博客是需要设置用户名密码和数据库的,这里的setup.js就是第一次登入时的页面脚本,而main.js则是剩余所有文件的入口

main.js
import 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文件夹内,这就是基于Expressnode服务器,在server文件夹内执行

node index

就可以启动Node服务器,默认侦听3000端口。

 关于 Webpack

Webpack的配置文件主体是有vue-cli生成的,但为了配合后端自动刷新、支持Sass和生成独立的css文件,稍微修改了一下:

webpack.config.js
const 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.js
import 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中。前面的spinnertoast元素分别是等待效果(转圈圈)的弹出层和信息的弹出层,和背景样式的切换。

关于后端

后端是用node.js作为服务器的,使用了express框架。

其中代码非常简单:

index.js
const 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.js
router.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

相关文章

  • 一些基于React、VueNode.jsMongoDB技术栈实践项目

    摘要:利用中间件实现异步请求,实现两个用户角色实时通信。目前还未深入了解的一些概念。往后会写更多的前后台联通的项目。删除分组会连同组内的所有图片一起删除。算是对自己上次用写后台的一个强化,项目文章在这里。后来一直没动,前些日子才把后续的完善。 欢迎访问我的个人网站:http://www.neroht.com/ 刚学vue和react时,利用业余时间写的关于这两个框架的训练,都相对简单,有的...

    tangr206 评论0 收藏0
  • 一个基于Vue.js+Mongodb+Node.js博客内容管理系统

    摘要:三更新内容在原来项目的基础上,做了如下更新数据库重新设计,改成以用户分组的数据库结构应数据库改动,所有接口重新设计,并统一采用和网易立马理财一致的接口风格删除原来游客模式,增加登录注册功能,支持弹窗登录。 这个项目最初其实是fork别人的项目。当初想接触下mongodb数据库,找个例子学习下,后来改着改着就面目全非了。后台和数据库重构,前端增加了登录注册功能,仅保留了博客设置页面,但是...

    wh469012917 评论0 收藏0
  • vue+node+mongodb 搭建一个完整博客

    摘要:开发一个完整博客流程前言前段时间刚把自己的个人网站写完,于是这段时间因为事情不是太多,便整理了一下,写了个简易版的博客系统服务端用的是框架进行开发技术栈目录结构讲解的配置文件放置代码文件项目参数配置的文件日志打印文件项目依赖模块 Vue + Node + Mongodb 开发一个完整博客流程 前言 前段时间刚把自己的个人网站写完, 于是这段时间因为事情不是太多,便整理了一下,写了个简易...

    Miracle_lihb 评论0 收藏0
  • 前端学习资源汇总

    摘要:建立该仓库的目的主要是整理收集学习资源,统一管理,方便随时查找。目前整合的学习资源只是前端方向的,可能会存在漏缺比较好的资源,需要慢慢的完善它,欢迎在该上补充资源或者提供宝贵的建议。 说明 平时的学习资源都比较的凌乱,看到好的资源都是直接收藏在浏览器的收藏夹中,这样其实并不方便,整理在云笔记上,也不方便查看修改记录,索性就整理在 github 上并开源出来,希望帮助大家能够更快的找到需...

    SnaiLiu 评论0 收藏0

发表评论

0条评论

Dr_Noooo

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<