vue + koa2 + mongodb 搭建的個人部落格

dawkey發表於2018-09-13

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

部落格地址:
dawkey.top

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

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

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

1. 技術棧:

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

基本上就是搭建部落格常用的一些庫.

2. 部落格頁面:

我要開始甩圖片了?

主頁:

vue + koa2 + mongodb 搭建的個人部落格

分類:

vue + koa2 + mongodb 搭建的個人部落格

歸檔:

vue + koa2 + mongodb 搭建的個人部落格

更新日誌:

vue + koa2 + mongodb 搭建的個人部落格

部落格文章:

vue + koa2 + mongodb 搭建的個人部落格

3. 部落格的後臺管理:

後臺管理介面也是整合在前端部分的,後端部分只負責處理資料就行了.

想要通過瀏覽器看一下後臺介面的朋友,可以通過login介面的visit按鈕(不需要賬號密碼),直接進入後臺管理哦,不過沒有相關許可權就是了?(移動端沒有login選項)

管理介面:

vue + koa2 + mongodb 搭建的個人部落格

新增部落格到草稿箱:

vue + koa2 + mongodb 搭建的個人部落格

修改和釋出部落格:

vue + koa2 + mongodb 搭建的個人部落格

刪除草稿和部落格:

vue + koa2 + mongodb 搭建的個人部落格

新增和刪除分類:

vue + koa2 + mongodb 搭建的個人部落格

新增和刪除更新日誌:

vue + koa2 + mongodb 搭建的個人部落格

4. 部落格搭建相關的

發了這麼多演示圖,還沒有些什麼實質性的東西.

主要記錄一下部落格搭建中的遇到的一些問題:

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等鍵位能有對應的作用,更好輸入體驗;
  • 把舊部落格的日記功能也加上.

相關文章