After working with WordPress, I always wanted to write my own lightweight and minimalist blog content manager (CMS).

After much delay, the device was finally completed before the summer school began.

Well, actually what I want to accomplish is:
  • A basic blog content manager function, such as background login, publish and manage articles, etc
  • Support markdown syntax editing
  • Support code highlighting
  • You can manage links to blog pages
  • Blog pages are optimized for mobile
  • Account Management (Changing passwords)
  • Page enough atmosphere, cool hey

So, in keeping with the usual cool, I wrote a starry theme backstage…

Landing page

Background Management Page

But the blog page ended up not having a starry night theme in the background, mainly because black wasn’t a good match.

So I think, since the front end is written with vue.js, then join Chao test xi vue.js especially yuxi blog style bar!

On this basis, the blog page has added a canvas animation written by myself, which should be elegant enough ~

Demo

Login background button at the bottom of the page “webmaster login”, you can log in to the background system as a tourist.

The source code

Technology and implementation ideas:

Front end: Vue family barrel
  • Vue.js
  • Vue-Cli
  • Vue-Resource
  • Vue-Validator
  • Vue-Router
  • Vuex
  • Vue-loader
The back-end
  • Node.js
  • mongoDB (mongoose)
  • Express
Tools and Languages
Overall idea:
  • The Node server does not render templates except for the home page, leaving rendering to the browser
  • The Node server does not perform any route switching, which is done by vue-Router
  • The Node server is only used to receive requests, query the database, and return values

So this is almost completely decoupled from the front and back ends, as long as you agree on restful data interfaces and data access formats.

At the back end, I used mongoDB to do the database, and operated mongoDB through Mongoose in Express, which omitted the complex command line. Undoubtedly, it was much more convenient to operate through Javascript.

A brief description of Vue plugins:
  • Vue-cli: Official scaffolding used to initialize the project
  • Vue-resource: Can be viewed as an Ajax library that can be easily injected into child components by introducing them after the component. The child component is called with this.$HTTP
  • Vue-validator: Validates forms
  • Vue-router: An official routing tool used to switch between subcomponents. It is the key for SPA applications
  • Vuex: Control the flow of data in components, making data flow more clear and traceable. This is done seamlessly through the official Vue-DevTools
  • Vue-loader: loader for Vue files in webpack
Upper file directory

I put the front-end files in the SRC directory, where mian. Js is the entrance to webpack.

All pages are divided into a single VUE component, placed in ComponentSS, generated by webpack through the entry file mian. Js, and the generated files are placed in the public folder.

The back-end files are placed in the Server folder, which is the Express-based Node server, and executed in the Server folder

Start the Node server, listening on port 3000 by default.

About the Vue – Cli

I just used simple template, which is much simpler to configure than the default template, but already has browser auto-refresh, production code compression, and so on.

vue init simple CMS-of-BlogCopy the code

Here is the webpack configuration file generated by vue-CLI, with only a few minor changes I made.

React.js doesn’t use scaffolding like vue-cli. It’s simple and elegant.

Webpack.config.js
var path = require('path')
var webpack = require('webpack')
module.exports = {
    entry: './src/main.js',
    output: {
        path: path.resolve(__dirname, './public'),
        publicPath: '/public/',
        filename: 'build.js',
    resolveLoader: {
        root: path.join(__dirname, 'node_modules'),
    module: {
        loaders: [
                test: /\.vue$/,
                loader: 'vue'
                test: /\.js$/,
                loader: 'babel',
                exclude: /node_modules/
                test: /\.json$/,
                loader: 'json'
                test: /\.html$/,
                loader: 'vue-html'
                test: /\.(png|jpg|gif|svg)$/,
                loader: 'url',
                query: {
                    limit: 10000,
                    name: '[name].[ext]?[hash]'
            , {
                test: /\.(woff|svg|eot|ttf)\??.*$/,
                loader: 'url-loader?limit=50000&name=[path][name].[ext]'
    babel: {
        presets: ['es2015'],
    devServer: {
        historyApiFallback: true,
        noInfo: true
    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({
            output: {
                comments: false,
            compress: {
                warnings: false
        }),
        new webpack.optimize.OccurenceOrderPlugin()Copy the code

You can see that you have helped us to automatically refresh the browser, hot load and production environment code compression is written, it is very considerate.

However, in the actual project, I still encountered a troublesome problem. Here is the script in package.json

"scripts": {
    "dev": "webpack-dev-server --inline --hot",
    "build": "cross-env NODE_ENV=production webpack --progress --hide-modules",
    "watch": "webpack --progress --color --watch",
    "server": "supervisor ./server/www"Copy the code


run


After that, the browser opens a server on port 8080. However, this server is used to serve the front-end page. In other words, starting the server from here instead of starting the Node server will cause the data cannot be exchanged. This port, however, automatically refreshes the browser after a file is modified.

Then, by executing separately

npm run watch
npm run serverCopy the code

To listen for file changes and restart the Node server, at which point the browser will not automatically refresh. I tried a few things but it didn’t work out. Used to automatic browser refresh, this kind of situation is quite painful.

So this is it:

A server that uses port 8080 when modifying styles, and a browser that manually refreshes on port 3000 when modifying data interaction.

It’s not very convenient, but at least you don’t have to restart the Node server yourself with the Supervisor…

About the Vue – the Router

Since it is written but also applied (SPA), the server is not responsible for routing, so vue-Router takes control of routing.

Here is the root component, where routing control is located, and the component is mounted under the body element:

main.js
let router = new VueRouter()
router.map({
    '/': {
        component: Archive
    'login': {
        component: Login
    '/article': {
        component: Article
    '/console': {
        component: Console,
        subRoutes: {
            '/': {
                component: ArticleList
            '/editor': {
                component: Editor
            '/articleList': {
                component: ArticleList
            '/menu': {
                component: Links
            'account': {
                component: Account
                let App = Vue.extend({
    data(){
        return {}
    components: {Waiting,Pop,NightSky,MyCanvas},
    http: {
        root: '/'
    computed: {
        waiting: ()=>store.state.waiting,
        pop:()=>store.state.popPara.pop,
        bg:()=>store.state.bg,
    store
    router.start(App, 'body')Copy the code
The corresponding document home page is index.html


  
    Blog-CMS
    
  
  
    
    
    
    
    
  

    Copy the code

You can see that the routing control is in the router-view under the body element. The previous waiting, pop, and Component elements are the pop-up layer for waiting effects (i.e. loops), the pop-up layer for information, and the background style switch, respectively.

In fact, this index. HTML is generated by Express through Jade. There is no HTML file in the actual project, so I put the generated HTML here for easy display.

Vue-loader is Vue’s official Webpack support tool, used to write components in a file. In the previous directory, there were many partitioned Vue files, each of which was a separate component.

For example, here is a pop-up layer component:

Pop.vue
@import ".. /SCSS/Pop.scss";Copy the code

Every Vue file has three (optional) sections: Template, Script, and style. This makes sense because it combines HTML, JS, and CSS.

The popover component, via Vuex, gets the parameters passed by other components. The parameters are an object, including the pop-up layer presentation information and the callback function when OK or cancel is hit.

Since the editor doesn’t support sass syntax in vue files, I put sass files outside and import them with @import.

About the Vue – the Resource

Vue-resource can be thought of as an Ajax library that integrates with Vue to create XHR and get XHR’s response.

Because of its high integration with Vue, it is easy to use in Vue components.

Article.vue
@import ".. /SCSS/Article.scss";Copy the code

The Article. Vue component creates and sends an XHR GET request during the Created life cycle. After obtaining the result, the attribute in the Response object is assigned to the corresponding attribute in data, and vue automatically updates the view.

The key to the markdown syntax that blogs support is also in this component.


       
{{{content | marked}}}
Copy the code

It is very convenient to use markdown’s filter to convert the output HTML directly into HTML structure.

On the backend

The back end uses Node.js as the server, using the most popular Express framework.

The body is generated by Express and is itself very lean. The main changes in practice are the addition of GET and POST requests sent by various front ends.

router.get('/article', function (req, res, next) {
    var id = req.query.id
    db.Article.findOne({_id: id}, function (err, doc) {
        if (err) {
            return console.log(err)
        } else if (doc) {
            res.send(doc)
        }
    })
})Copy the code

For example, this request handles get requests from the front end, using Mongoose to query the database and return data.

The front-end page controls the asynchronous operation through promises, putting the resulting data into the component’s data object, and Vue detects changes and updates the view.

The initialization file of the database is stored in init.js. When it is run for the first time, a new user named admin will be created. The initial password is 111, which can be changed in the console account management.

Afterword.

There are many, many things that are not mentioned in this article. After all, the blog framework is a relatively big thing.

After writing the blog manager, I feel quite a lot, have a deeper understanding of the data binding, componentization and data flow in Vue. Js, and have an elegant practice with the Node.js backend.

So, after learning something, practice is very necessary. A lot of the front end is the process of constantly stepping pits. All the way to step on the pit and climb the pit, actually quite a sense of achievement.

School is about to start. If I have time, I will analyze several important Vue components in this article, and share my experience on the use of Vue.

Good don’t want to go to school T_T…