一個基於vue二、koa2和mongodb的博客

博客終於差很少寫完了,雖然仍是可能有一堆bug, 不過我火燒眉毛要寫一篇博文來分享了= =
javascript

博客前臺展現

博客前臺展現

博客後臺展現

博客後臺展現

博客後臺編輯

項目地址

你們一塊兒來star、fork呀,歡迎提出各類改進意見,我知道確定還有一堆問題😵
域名尚未備案,後面就變成imhjm.com😆,敬請期待css

項目起源

其實我一直在使用hexo搭建的博客,也一直用得還挺順手,可是它只是個靜態站點,當我須要一些定製化的需求,我沒辦法作更多的改變,而且搭在github page上有時不穩定,並且以爲搭建一個網站而後本身來作各類優化是一件cool的事情,能夠嘗試各類新技術,能夠接觸到各個層面上的優化,因此寫一個博客的念頭就開始了。
其實vue-blog這個項目開了很久了,可是由於斷斷續續開發,過一段時間就以爲以前的代碼寫得很差就開始重寫,並且也是邊寫來邊學習新技術,vue2全家桶,koa2, webpack2,這些都是以前我沒怎麼了解的技術,可是至少經過這個博客項目,我對它們也有了新的認識
這裏也要感謝一下@Ma63d(@chuckliu),博客搭建初期學習了不少他的kov-blog代碼,學習到不少東西,也讓本身的後期實現得比較順利html

項目技術細節

就是這個圖啦~
vue

博客總體架構

可是固然還有更多細節

client端admin部分

這是博客後臺,其實個人想法就是實現一個文本編輯器,有個左邊欄,而後右邊欄編輯,爲了不復雜,我只用了兩個頁,一個登陸頁,而後一個就是帶有編輯器的列表頁java

鑑權

統一使用axios來通訊,鑑權使用jwt,login頁中登陸成功存入token,而後進入編輯頁,經過vue-router的beforeEach鉤子加入node

Axios.defaults.headers.common['Authorization'] = 'Bearer ' + store.state.auth.token;複製代碼

而後服務端接收時驗證token來決定是否鑑權成功,失敗時Axios統一攔截,刪除store裏存取的token, 經過vue-router再回到登陸頁webpack

狀態管理

列表和編輯器分紅了兩個組件,用vuex統一管理狀態,經過action取/存而後mutaion修改狀態便可,可是這部分邏輯較多,具體實現就不展開描述了ios

// editor部分state
const state = {
  articleList: [],
  tagList: [],
  currentArticle: {
    id: -1,
    index: -1,
    content: '',
    title: '',
    tags: [],
    save: true,
    publish: false
  },
  allPage: 1,
  curPage: 1,
  selectTagArr: []
};複製代碼
// auth部分state
const state = {
  token: sessionStorage.getItem('token')
};複製代碼

編輯器/markdown

受@chuckliu安利一樣使用了simplemde-markdown-editor,而後解析和高亮部分使用了marked.jshighlight.jscss3

client端front部分

event bus

由於考慮到前臺須要更快的加載速度,並且邏輯也比較簡單,就放棄使用vuex,採用vue event bus來實現非父子組件間的通訊nginx

// 在main.js定義全局event bus,不使用vuex管理
var EventBus = new Vue();
Object.defineProperties(Vue.prototype, {
  $eventBus: {
    get: function() {
      return EventBus;
    }
  }
});
// 而後在組件內能夠這樣使用
this.$eventBus.$on('filterListByTag', this.filterListByTag);
this.$eventBus.$emit('filterListByTag', this.filterListByTag);
this.$eventBus.$off('filterListByTag', this.filterListByTag);複製代碼

keep-alive

排除article組件,其餘則保留組件狀態或避免從新渲染。

<transition name="fade" mode="out-in">
    <!-- keep-alive排除article -->
    <keep-alive exclude="article">
        <router-view>
         </router-view>
    </keep-alive>
</transition>複製代碼

css小坑/小技巧

原本的transition是使用到transform的,由於移動端個人側邊欄是fixed,發如今切換的過程當中側邊欄抖動,才發現是由於fixed是會跟隨transform的,能夠具體參考CSS3 transform對普通元素的N多渲染影響,因而便刪去了切換時的移動,保留opacity

在佈局上學習了下vue官網的兩欄,兩欄超出部分均可以滑動,而且不互相影響,這是怎麼實現的?其實仍是挺簡單的,不過用到一個小技巧,設置

// 這樣能夠實現一個元素100%width以及100%height
div {
 position: absolute;
 top: 0;
 right: 0;
 bottom: 0;
 left: 0;
}複製代碼

其餘就是經典兩欄佈局和加個overflow-y:auto了

還有一個經典的佈局問題,就是foot部分如何實如今頁面沒什麼東西的,固定在頁面底部,頁面出現滾動條,而後foot部分跟隨在頁面主體後面

server端

server端直接上koa2了,支持async/await, 異步部分用起來真的很舒服,如今node7+也支持了,因此小夥伴們趕忙用吧~

Json Web Token

再講講jwt鑑權吧
其實並不複雜
在驗證登陸時,後端取出數據庫已有的密碼驗證,成功則用配置好的secret生成一個signed jwt,通俗點說你將它加密了再傳給客戶端

const token = jwt.sign({
        uid: user._id,
        name: user.name,
        exp: Math.floor(Date.now() / 1000) + 24 * 60 * 60 //1 hours
      }, config.jwt.secret);複製代碼

http無狀態,因此客戶端須要存下這個token, 而後以後請求時帶上這個token服務端驗證經過後返回資源便可

try {
    tokenContent = await jwt.verify(token, config.jwt.secret);
  } catch (err) {
    if ('TokenExpiredError' === err.name) {
      ctx.throw(401, 'token expired');
    }
    ctx.throw(401, 'invalid token');
  }複製代碼

若是想更深層次地瞭解jwt是什麼,能夠自行去搜索網上的文章

RESTful API

Representational State Transfer簡單來理解就是每一個URL都是資源,經過不一樣的http method去操做這些資源就行,設計上就能夠這樣,而後統一加上prefix:'/api'

router.get('/articles', verify, $.getAllArticles) //獲取全部文章
    .post('/articles', verify, $.createArticle) //建立文章
    .patch('/articles/:id', verify, $.modifyArticle) //修改特定文章
    .get('/articles/:id', $.getArticle)  //獲取特定文章
    .delete('/articles/:id', verify, $.deleteArticle) //刪除特定文章複製代碼

webpack相關

原本打算直接使用vue-cli,這部分就很是省心了,vue-cli這個腳手架作得確實很友好,我以爲vue比較好上手開發的一部分緣由也是由於它吧,不過由於這個博客項目就是學習的過程,不想這麼輕易地逃過這部分的學習😸,直接上webpack2,而後參考vue-cli和webpack官網來寫,以爲也學習到不少東西,而且由於front和admin是分開的,因此也實現了多頁配置

開發環境

hot-reload是必備的

// ...
entry: {
    'modules/admin': [
      'webpack-hot-middleware/client?reload=true',
      CLIENT_FOLDER + '/src/modules/admin/main'
    ],
    'modules/front': [
      'webpack-hot-middleware/client?reload=true',
      CLIENT_FOLDER + '/src/modules/front/main'
    ]
  },
// ...
plugins: [
    new webpack.HotModuleReplacementPlugin()
// ..複製代碼

用CleanWebpackPlugin清空下目錄,HtmlWebpackPlugin自動生成html,而後該寫的loader

生產環境

生產環境下改動就大了,先得刪去hot-reload和devtool部分,而後提取css

//...
styl: ExtractTextPlugin.extract({
      use: [{
        loader: 'css-loader',
        options: {
          minimize: true,
          sourceMap: true
        }
      }, {
        loader: 'stylus-loader',
        options: {
          sourceMap: true
        }
      }],
      fallback: 'vue-style-loader'
    }),
//...複製代碼

UglifyJsPlugin壓縮代碼,而後提取公有代碼vendor、manifest,manifest用來防止vendor的hash在vendor部分沒有變化時不被修改

// ...
// 分別提取vendor、manifest
    new webpack.optimize.CommonsChunkPlugin({
      name: 'modules/vendor_admin',
      chunks: ['modules/admin'],
      minChunks: function(module, count) {
        return (
          module.resource &&
          /\.js$/.test(module.resource) &&
          module.resource.indexOf(
            join(__dirname, './node_modules')
          ) === 0
        )
      }
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'modules/manifest_admin',
      chunks: ['modules/vendor_admin']
    }),
//...複製代碼

而後用CopyWebpackPlugin將static拷進dist就行了

與koa配合

這部分就能夠看vue-cli是怎麼實現的了,vue-cli使用的是express經過webpack-dev-middleware和webpack-hot-middleware實現開發模式下熱重載,固然koa也能夠

const koaWebpack = require('koa-webpack');
  const webpack = require('webpack');
  const webpackConfig = require('../webpack.config');
  let compiler = webpack(webpackConfig);
  app.use(koaWebpack({
    compiler: compiler,
    dev: {
      //noInfo: true,
      stats: {
        colors: true,
        chunks: false
      },
      publicPath: webpackConfig.output.publicPath,
    }
  }));複製代碼

順便講到項目中用的是history模式,這裏也須要後端配合

app.use(convert(historyApiFallback({
  verbose: process.env.NODE_ENV == 'production' ? false : true,
  index: '/front.html',
  rewrites: [
    { from: /^\/admin$/, to: '/admin.html' },
    { from: /^\/admin\/login/, to: '/admin.html' },
    { from: /^\/$/, to: '/front.html' },
    { from: /^\/article/, to: '/front.html' }
  ]
})))複製代碼

可是這裏有個坑是要注意書寫use的順序,放在router api後面就行,這裏須要理解下koa的洋蔥結構

koa

webpack優化

我以爲這部分除了區分開發環境和生產環境之外,還要注意到對webpack打包的過程和模塊的分析,不要吝嗇webpack打包的輸出

// 顯示顏色,耗時長的都有顏色區分 --colors
// 能夠看到每一步的耗時 --profile
// 顯示模塊 --display-modules
// 而且按size大小排序 --sort-modules-by size
webpack.config.js --colors --profile --display-modules --sort-modules-by size複製代碼

這樣就能看出打包什麼耗去你大量的時間,佔據了大量空間,還有是不是重複打包

還有一個直觀而又酷炫的方式github.com/alexkuz/web…


直接看佔比就能看出哪部分須要你去優化了

我經過alias和external優化了下,效果仍是挺明顯的,最後本地生產環境打包大概14-20s,也還能夠接受,可是估計還有優化的空間,其實我也試過happypack和並行的uglify,不過發現沒什麼效果= =

//...
resolve: {
    extensions: ['.js', '.vue', '.json'],
    modules: [join(__dirname, './node_modules')],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      'vuex$': 'vuex/dist/vuex.esm.js',
      'vue-router$': 'vue-router/dist/vue-router.esm.js',
      'simplemde$': 'simplemde/dist/simplemde.min.js',
      'highlight.js$': 'highlight.js/lib/highlight.js',
      'fastclick': 'fastclick/lib/fastclick.js',
      'lib': resolve(__dirname, './client/src/lib'),
      'api': resolve(__dirname, './client/src/api'),
      'publicComponents': resolve(__dirname, './client/src/components'),
    }
  },
//...
// html template引入simplemde cdn
externals: {
    'simplemde': 'SimpleMDE'
 },
//...複製代碼

還有一些緩存優化的問題,就是js/css的hash問題,js chunkhash而後提取css使用contenthash,爲了不改動vendor的hash,commonChunk額外提取出manifest(extract the webpack bootstrap logic into a separate file),這樣當vendor沒有被修改的時候,從新運行webpack不會再生產新的hash,變更的只有manifest,具體能夠看webpack.js.org/plugins/com…

關於多頁配置

其實只須要根據模塊劃分而後多寫幾行配置就行,要作相似腳手架的話可使用glob這些工具
具體看代碼吧,會生成如下目錄

dist
 |---css
     |---modules
             |---admin.xxx.css
             |---front.xxx.css
 |---fonts
 |---modules
     |---admin.xxx.min.js
     |---front.xxx.min.js
     |---vendor_admin.xxx.min.js
     |---vendor_front.xxx.min.js
     |---manifest_admin.xxx.min.js
     |---manifest_front.xxx.min.js
 |---static
 |---admin.html
 |---front.html複製代碼

線上部署及優化

pm2

表示以前我在玩耍node服務的時候都是使用screen命令的,pm2確實很贊,監控/日誌管理這些都很完善,日誌方面我還使用了pm2-logrotate

pm2 install pm2-logrotate
pm2 set pm2-logrotate:retain 100 //控制日誌數量
pm2 set pm2-logrotate:size 1M  //控制日誌切割大小複製代碼

nginx

nginx基本是上線必備,應該也不用多說了,開啓gzip效果確實很顯著,原本前臺的vendor_front從227k直接被壓縮到84k,簡直cool!😆

最後

謝謝閱讀~
歡迎follow我哈哈github.com/BUPT-HJM
看到這裏,不star不行了😋
github.com/BUPT-HJM/vu…
歡迎繼續觀光個人博客~

歡迎關注

相關文章
相關標籤/搜索