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等键位能有对应的做用,更好输入体验;
  • 把旧博客的日记功能也加上.
相关文章
相关标签/搜索