项目泉源
之前曾用过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.js
和setup.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
文件夹内,这就是基于Express
的node
服务器,在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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>cms2simple</title>
<link rel="stylesheet" href="dist/index.css">
<link rel="stylesheet" href="dist/styles.css">
</head>
<body>
<div id="CMS2" style="height: 100%">
<my-canvas></my-canvas>
<spinner v-show="isLoading"></spinner>
<Toast v-show="isToasting"></Toast>
<router-view ></router-view>
</div>
<script src="/dist/main.js"></script>
</body>
</html>
能够看到路由掌握在body
元素下的router-view
中。前面的spinner
,toast
元素分别是守候结果(转圈圈)的弹出层和信息的弹出层,和背景款式的切换。
关于后端
后端是用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
异步数据视图的部份,不过这一部能讲的太多,就不在这里展开了,能够看官方文档后,参考源代码的解释。