基于vue2,eggjs,mysql的个人博客(正在更新)

2020/4/25 5:22:43

本文主要是介绍基于vue2,eggjs,mysql的个人博客(正在更新),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

简单做个博客。
功能点:
注册、登录、cookie、权限控制、文章列表、文章详情、文章目录、点赞、评论、分页加载

前端

使用vue-cli3创建项目

npm install -g @vue/cli
vue create hello-world

为了避开烦人的 eslint,选择了手动选择特性:
image.png
同时没有选择 Linter/Formatter,使用 vscode 中的插件 prettier 和 vetur 配合格式化代码。
image.png

使用axios封装http请求方法

参考了vue中Axios的封装和API接口的管理,对网络请求进行了发封装。
image.png
网络请求统一放在/src/request中,config.js中是基本配置信息:
image.png
http.js中封装请求拦截、响应拦截、错误统一处理。
api.js中是网站所有接口,将其导出后,挂载到 vue.prototype.$api 上,这样全局可以使用 this.$api.xxx 使用接口:
image.png
然后在main.js引入并挂载:
image.png

cookie/session

博客采用cookie/session的方式来记录会话状态,在app.vuecreated生命周期函数中通过checkLogin()接口查询用户登录状态,具体的逻辑为:
当当网流程图3.png

首页分页加载

  let documentEle = document.documentElement
  let needLoadMore =
    documentEle.scrollTop + documentEle.clientHeight + 50 > documentEle.scrollHeight;
  if (needLoadMore && !this.nomore) {
    this.loadingMore = true;
    //暂时不能再滚动加载数据
    this.nomore = true;
    this.loadMoreArticle();
  }

document.documentElement.scrollTop:文档滚动的距离
document.documentElement.clientHeight:文档在可视范围内的高度
document.documentElement.scrollHeight:文档总高度

判断思路是:视口的高度 + 文档的滚动距离 >= 文档总高度,可以预留50的距离做预加载。前端每一次判断触底后,会向后端请求下一页数据,并返回 nomore 字段,表示是否还有未加载文章。这里有个细节是:每一次触底后手动将 nomore 设为 true,后端返回 nomore 后再修改其值,这样做的目的是防止在请求返回前再次发送请求。
参考:搞清clientHeight、offsetHeight、scrollHeight、offsetTop、scrollTop

如何展示博文

没有实际做博客之前,以为每一篇博文都是通过单独写html标签的方式排版的...然后通过别人的项目,发现了正确的做法是:
使用markdown/富文本编辑器编辑文章,生成html片段,在vue中使用v-html语法插入该html片段,文章便以html标签的形式渲染出来可。博客中使用的markdown编辑器是mavonEditor,另外使用highlightjs高亮代码。

<div v-html="articleInfo.contentHtml" class="article-container" ref="content"></div>

提取目录

    extractCatalog() {
      let contentElementRef = Array.from(
        this.$refs.content.querySelectorAll("h1,h2,h3,h4,h5,h6")
      );
      contentElementRef.forEach((item, index) => {
        item.id = item.localName + "-" + index;
        this.catalogList.push({
          tagName: item.localName,
          href: `#${item.localName}-${index}`,
          text: item.innerText
        });
      });
    }

上一小节中,将文章的html片段渲染到了div容器元素中,接下来可以使用querySelectorAll("h1,h2,h3,h4,h5,h6")函数来找出文中所有的标题Dom节点,querySelectorAll()的好处在于它会按照传入参数的顺序进行查找,所以不用担心乱序。获取到目录后对各级目录编号,以便生成锚点进行跳转。

判断对应节是否出现

    watchPageScrollFunc() {
      let contentElementRef = Array.from(
        this.$refs.content.querySelectorAll("h1,h2,h3,h4,h5,h6")
      );
      for (let index = 0; index < contentElementRef.length; index++) {
        const viewPortHeight =
          window.innerHeight ||
          document.documentElement.clientHeight ||
          document.body.clientHeight;
        const elementHeight = contentElementRef[index].clientHeight;
        const el = contentElementRef[index];
        const top =
          el.getBoundingClientRect() &&
          el.getBoundingClientRect().top + elementHeight;
        if (top <= viewPortHeight && top > 0) {
          this.firstVisibleElemetHref = this.catalogList[index].href;
          break;
        }
      }
    }
    
    window.addEventListener("scroll", this.watchPageScrollFunc);

判断方法:元素上边到视口上边的距离 + 元素自身高度 <= 视口高度 && 元素上边到视口上边的距离 + 元素自身高度 > 0
其中,获取元素到视口上边的距离用到:getBoundingClientRect(),某个元素相对于视窗的位置集合。集合中有top, right, bottom, left等属性:

rectObject = object.getBoundingClientRect();

rectObject.top // 元素上边到视窗上边的距离;

rectObject.right // 元素右边到视窗左边的距离;

rectObject.bottom // 元素下边到视窗上边的距离;

rectObject.left // 元素左边到视窗左边的距离;

在整个文章的Dom结构中,使用querySelectorAll("h1,h2,h3,h4,h5,h6")去搜索所有标题类的Dom节点,每一次滚动都去依次动态检查这些标题元素距离视口上方的距离,找到第一个出现在视口中的Dom元素就返回。另外加上节流函数可优化性能。

参考:如何判断元素是否在可视区域ViewPort

评论的实现

评论数据采用的数据结构如下,这里只做到了二级评论,数据结构定下来,具体的实现还是比较简单的。

let comments = [
  {
    author: "admin",
    content: "留言1",
    articleId: "9",
    time: "2020-04-12 10:59",
    id: "0",
    replyList: [
      {
        author: "ghm",
        content: "回复留言1",
        articleId: "9",
        replyTo: "admin",
        time: "2020-04-12 10:59",
        id: "0-0"
      }
    ]
  },
  {
    author: "admin",
    content: "留言2",
    articleId: "9",
    time: "2020-04-12 10:59",
    id: "1",
    replyList: [
      {
        author: "ghm",
        content: "回复留言2",
        articleId: "9",
        replyTo: "admin",
        time: "2020-04-12 11:00",
        id: "1-0"
      }
    ]
  }
];

可添加表情的输入框

第一版的实现方式:在<input>中直接插入emojiunicode,展示效果如下所示:
image.png
这样做的问题在于:展示效果没有图片好,并且在不同的浏览器中会展现出不同的效果。果断换用图片形式展示表情,但是<input>是不能插入<img>标签的,于是参考了掘金的实现方式,使用contenteditable这个css属性将普通Dom元素变为可编辑的Dom元素,这样就可以使用appendChild()的方式将<img>插入,最终的显示效果也是明显优于直接使用emoji的。

  <div
    class="textarea"
    ref="inputContent"
    contenteditable="true"
    autocomplete="off"
    :placeholder="placeholder"
    draggable="false"
    spellcheck="false"
  ></div>

image.png

后端

数据库表的设计

上线

配置服务器环境

1.一台linux服务器
作者买的阿里云最便宜的ECS云服务器,配置为1核2G,1M带宽,作为学习使用完全够了,操作系统选择的CentOS。然后参照阿里云给出的教程在服务器上部署NodeJs环境,部署mysql。
2.购买域名
在阿里云上购买域名后,按照新手引导设置域名解析,同时设置 www 和 @,网站便可通过 www.xxx.com 和 xxx.com 访问。同时,想要使用域名访问网站,还需要对域名进行备案。
3.本地数据库迁移到云服务器
作者使用的数据库可视化工具是navicat,在navicat中对选中的数据库做转储SQL文件处理,再在云服务器的mysql中新建数据库,运行此SQL文件,便完成了数据库的迁移。
image.png

上传文件

1.下载文件传输工具 Xftp
2.部署前端代码

npm run build

打包好后,使用 Xftp 将 dist 文件夹上传到服务器中
3.部署后端代码
将后端代码 git clone 到服务器中

npm i
npm start

配置nginx

1.安装nginx

yum install nginx

安装好的 nginx 会在 /etc/nginx
2.使用 Xftp 修改 nginx 配置
找到/etc/nginx目录下的 nginx.conf文件,使用记事本打开编辑:

user  root;
worker_processes  1;

events {
    worker_connections  1024;
}

http {
  include       mime.types;
  default_type  application/octet-stream;

  sendfile        on;
  keepalive_timeout  65;

  server {
    listen       80;
    server_name  localhost;
    root  /root/project/dt_blog/frontend/dist/;
    index  index.html;
    add_header Access-Control-Allow-Origin *;
      
    location /api {
      proxy_pass  http://127.0.0.1:7001;
      proxy_set_header  HOST $host;
    }
        
    location / {
      try_files $uri $uri/ @router;
      index index.html;
    }

    location @router {
      rewrite ^.*$ /index.html last;
    }
  }
}

其中这两个配置很重要:

location / {
  try_files $uri $uri/ @router;
  index index.html;
}

location @router {
  rewrite ^.*$ /index.html last;
}

网站部署好后,可以正常访问,但是打开二级页面后再刷新,就会404,原因是:v-router设置的路径并不是真实存在的路由,工程中的路由跳转都是通过 js 实现的,而将前端打包好的 dist 目录部署在服务器后,在浏览器中访问根路径会默认打到 index.html 中,但是直接访问其他路径,就没有一个真实的路径与其对应。(参考:https://www.cnblogs.com/kevingrace/p/6126762.html)

做好以上配置后,就可以通过 服务器ip:80 来访问部署好的网站了,但是一直通过 ip 地址访问网站不太合理,那么就需要给网站配置域名。

配置域名



这篇关于基于vue2,eggjs,mysql的个人博客(正在更新)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程