vue + koa2 + mongodb 搭建的我的博客

在接觸vue以後,就一直想用vue把原來老舊的博客(基於jqueryphp)從新搭一遍,在摸了幾個月後,總算摸出來了😂.前端部分只涉及到vue,關於koa2mongodb請移步到後端部分.php

博客地址:
dawkey.topcss

推薦在PC端訪問,移動端作了相關兼容,不過在PC端上效果要更好.html

github地址:
github.com/Dawkey/dkyS…前端

後端部分:
github.com/wwk321/dkyS…vue

1. 技術棧:

  • vue,vuex,vue-router(vue的全家桶走一下流程);
  • axios發送ajax請求(這個也不用多說);
  • markedmarkdown轉爲html(比本身瞎寫的md-html好用多了);
  • highlight代碼語法高亮;

基本上就是搭建博客經常使用的一些庫.jquery

2. 博客頁面:

我要開始甩圖片了😂ios

主頁:

分類:

歸檔:

更新日誌:

博客文章:

3. 博客的後臺管理:

後臺管理界面也是集成在前端部分的,後端部分只負責處理數據就好了.css3

想要經過瀏覽器看一下後臺界面的朋友,能夠經過login界面的visit按鈕(不須要帳號密碼),直接進入後臺管理哦,不過沒有相關權限就是了😁(移動端沒有login選項)git

管理界面:

添加博客到草稿箱:

修改和發佈博客:

刪除草稿和博客:

新增和刪除分類:

新增和刪除更新日誌:

4. 博客搭建相關的

發了這麼多演示圖,尚未些什麼實質性的東西.github

主要記錄一下博客搭建中的遇到的一些問題:

4.1 搭建的思路:

博客作的是單頁面的,因此咱們因此的數據獲取都是經過ajax的.

個人思路是在頁面加載時,就請求一個main的關鍵數據,它是一個數組,其中包含着每篇博客的標題,標籤,分類,時間,描述,以及關鍵的id號(這裏id號在請求博客文章數據時會用到).

對這一個數組數據用js進行加工,咱們就能獲得了home(首頁),tag(標籤),classify(分類),archive(歸檔)這四個頁面(也是通常博客最基礎的)所須要的數據,把這些數據存入到vuex中.

這樣,單頁面較多頁面的優點就體現出來了,咱們只請求了一次數據,以後,咱們再訪問上面提到的四個頁面,就不再會請求任何數據,甚至斷網也能正常訪問(除非刷新頁面)

當咱們想要查看一篇博客文章的具體內容時,點擊以後,根據前面提到的對應的id號,就會經過ajax請求對應的文章數據,最終顯示瀏覽器上就完事了.

至於還有一個update(更新日誌),這個在訪問它時,單獨請求數據就好了.

原理上能夠說是很是簡單😆

4.2 頁面間的切換:

由於作的是單頁面,頁面間的切換是不可避免的,在寫頁面切換時,我嘗試了不少種動畫切換(本人是極端的外觀黨😖),最終還選擇瞭如今這種比較傳統的方式.

具體的實現方式是,使用vue-router中的導航守衛中的beforeEach(具體查看vue-router的文檔),在每次切換路由時,都先顯示一段時間的加載動畫,以後才顯示出頁面,傳統,可是實在沒想出或者說實現出什麼炫酷的切換方式.

4.3 markedhighlight實現的文本編輯:

這兩哥們應該是搭建博客系統時,文本編輯的標配:

關於這二者的使用網上也是有不少了,這裏我主要記錄一下我在使用這二者時,遇到的一些,我的的一些理解,方便一樣想要搭建本身博客系統的朋友使用.

4.3.1 markedhighlight組合達到語法高亮:

這個問題應該是首要的,博客文章代碼不高亮,乾巴巴一片,就太

其實就是要用到markedrenderer,直接看代碼:(終於要上代碼了😆)

import marked,{Renderer} from "marked";
import hljs from "highlight.js";
const renderer = new Renderer();

renderer.code = (code,language) => {
  if(!language){
    language = "code";
  }
  let lang_is_valid = (language !== "code" && hljs.getLanguage(language));
  let highlighted = lang_is_valid ? hljs.highlight(language, code).value : code;

  return `<pre><div class="language">${language}</div><code class="hljs ${language}">${highlighted}</code></pre>`;
};

marked.setOptions({renderer});
複製代碼

咱們先把工做分配明確,在實現語法高亮時,highlight負責把代碼字符串轉換爲具備語法高亮結構html字符串,marked只負責告訴highlight這串代碼用的什麼編程語言.

好了,接着看上面的代碼,markedrenderer適用於咱們來DIY它最終生成的html代碼,代碼中的renderer.code天然指的是最終生成的代碼相關的html.

它是一個函數,這裏咱們能夠理解爲要重寫這個函數,這個函數最終調用時,會傳入兩個參數,第一個code是代碼字符串,第二個language是代碼的編程語言.

代碼中的hljs.highlight(language, code).value就是最終根據marked提供的兩個參數值,所生成的具備代碼高亮結構的html字符串.

再來看最終的return值,咱們能夠注意到code標籤裏面class值是"hljs空格+編程語言",這個class格式是必須的,以告訴highlight最終怎麼高亮.

以上工做作完以後,咱們marked()返回的就是具備高亮代碼格式html字符串,固然前提是咱們有引入highlight提供的css,最終咱們才能看到高亮的代碼.

marked是怎麼知道咱們的代碼是什麼編程語言😐,好吧,是我當時孤陋寡聞了😂,使用柵欄代碼塊來寫代碼,讓咱們來告訴marked咱們的語言.

4.3.2marked生成的連接能跳轉到新的標籤:

默認狀況先,marked生成的a標籤是在當前頁跳轉的,一樣用renderer咱們能夠適當修改一下就行了:

renderer.link = (href,title,text) => {
  return `<a href="${href}" target="_blank">${text}</a>`;
}
複製代碼

4.4 Mayuri開口說話:

Mayuri指的就是博客左上角的那個動漫頭像,在最開始搭建博客時,我就已經想好要作這個了,當時還準備作幾個表情,截了相關的圖,不過由於暑假摸了過久,致使目前博客上線時,還只有這個初始的表情.

未來也許可能會新增幾個表情吧😶

4.4.1 嘴部動畫

若是你不仔細看,可能不會發如今出現消息文字時,Mayuri的嘴部是在動的.

其實就是三張圖片之間在互相切換,由於本人沒有一點動漫製做的相關知識,所寫這個css的動畫徹底是憑感受(瞎)寫的,最終的效果還行吧,不過仍是有一點小瑕疵的😑.

由於只涉及到一點css3的知識,這裏就不貼代碼了.

4.4.2 文字動畫

打字動畫的實現,網上講述的也很多了,可是,我仍是想結合個人項目寫一寫,對這個不感興趣的朋友能夠直接跳過.

打字動畫用js實現效果肯是要比css要好的,本質上就是,經過不斷的更換一個元素的innerHTML來達到打字的效果.

這裏我用到了Promise鏈,當時剛剛看了promise,就用了,不知道有沒有把這個問題複雜化😕.

貼代碼:

export default function type(el,word){
  let word_array = word.split("");
  let promise = Promise.resolve();
  word_array.reduce((meno,value,index)=>{
    let str = meno + value;
    if(index === 1){
      promise = type_timer(el,meno);
    }
    promise = promise.then(()=>{
      return type_timer(el,str);
    });
    return str;
  });
  return promise;
}

//生成新的promise,串成promise鏈
function type_timer(el,str){
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      el.innerHTML = str;
      resolve();
    },115);
  });
}
複製代碼

這裏咱們export出了type方法,type方法,第一個參數是輸出文字元素對象,第二個參數是輸出的文字,執行type(el,word),就能實現打字效果了.

具體看代碼,type_timer生成一個含有setTimeoutPromise,咱們對輸出的文字進行分割,獲得word_array數組,再用數組的reduce方法串出一條Promise鏈;

使用Promise鏈的好處就是,咱們能夠經過then很好的知道何時打字動畫結束了.

對話之間的衝突😕

有時候,咱們一條消息對話尚未打完,下一條消息就產生了,這時,確定會產生兩條Promise鏈做用於同一個元素,這就發生了衝突.

這時,咱們有兩種選擇:

  • 等前一條Promise鏈執行完,再執行下一條;
  • reject當前Promise鏈,執行下一條.

我選擇了第二種作法,於是須要在上面的代碼上稍做修改:

export default function type(el,word,$store){
  let word_array = word.split("");
  let promise = Promise.resolve();
  word_array.reduce((meno,value,index)=>{
    let str = meno + value;
    if(index === 1){
      promise = type_timer(el,meno,$store);
    }
    promise = promise.then(()=>{
      return type_timer(el,str,$store);
    });
    return str;
  });
  return promise;
}

//生成新的promise,串成promise鏈
function type_timer(el,str,$store){
  return new Promise((resolve,reject)=>{
    //若是talk_flag === false,則reject,以防止生成多條promise鏈產生衝突.
    if($store.getters["talk_flag"] === false){
      reject("talk_is_break");
    }
    setTimeout(()=>{
      el.innerHTML = str;
      resolve();
    },115);
  });
}
複製代碼

用了vuex😬,在vuex中存儲一個變量talk_flag,在執行type方法時,傳入第三個參數,vuex$store對象.

每次咱們建立新的消息對話時,先把vuex中的talk_flag設爲false,保證,先前的Promise鏈必定會斷掉,而在Promise鏈斷後,調用的type方法就會catchreject,在catch中,咱們再從新把talk_flag設爲true,保證新的Promise鏈能順利執行.

好吧,寫到這裏我忽然意識到,根本不須要用到vuex,一個普通的對象就好了,當時編寫時,可能以爲vuex對象更厲害吧😃.

固然,若是有的朋友有更好的實現手段能夠和我交流(應該沒人有耐心看完這段碎碎唸吧~)

5. 兼容性

  • Chrome上效果很好,Firefox上效果通常,ie上效果未知(並不想測試~)
  • 移動端佈局作了相關自適應,不過效果不是太滿意,後面可能會考慮更好的適配一下移動端吧.

6. 寫在最後

不知不覺竟然寫了這麼多,算是這幾個月的成果的一個總結,無論怎樣,接着努力吧,固然更重要的是但願能對和我同樣,想要親手搭建一個屬於本身博客的朋友有所幫助吧.

若是你有耐心看到這裏,不防👉這裏點個star⭐吧,也算是對個人一點小小的鼓勵😂

7. TODO

不存在的😆

  • 移動端更好的視覺效果;
  • 文章新增錨點列表;
  • 文本編輯時,tab等鍵位能有對應的做用,更好輸入體驗;
  • 把舊博客的日記功能也加上.
相關文章
相關標籤/搜索