Personal blog based on vue2, eggjs and MySQL (being updated)

Time:2020-9-9

Just make a blog.
Function points:
Registration, login, cookie, permission control, article list, article details, article directory, likes, comments, paging loading

front end

Creating a project using vue-cli3

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

To avoid the annoying eslint, the manual selection feature is selected
Personal blog based on vue2, eggjs and MySQL (being updated)
At the same time, linter / formatter is not selected, and the plug-ins prettier and vetur in vscode are used to format the code.
Personal blog based on vue2, eggjs and MySQL (being updated)

Encapsulating HTTP request method with Axios

Referring to the encapsulation of Axios and the management of API interface in Vue, the network request is encapsulated.
Personal blog based on vue2, eggjs and MySQL (being updated)
Network requests are placed in/src/requestMedium,config.jsIn is the basic configuration information:
Personal blog based on vue2, eggjs and MySQL (being updated)
http.jsIt encapsulates request interception, response interception and unified error handling.
api.jsIn is all the interfaces of the website. After exporting them, mount them tovue.prototype.$apiSo that it can be used globallythis.$api.xxxUse interface:
Personal blog based on vue2, eggjs and MySQL (being updated)
And then in themain.jsImport and mount:
Personal blog based on vue2, eggjs and MySQL (being updated)

cookie/session

The blog uses the cookie / session method to record the session stateapp.vueOfcreatedIn the life cycle functioncheckLogin()The specific logic is as follows:
Personal blog based on vue2, eggjs and MySQL (being updated)

Homepage paging loading

let documentEle = document.documentElement
  let needLoadMore =
    documentEle.scrollTop + documentEle.clientHeight + 50 > documentEle.scrollHeight;
  if (needLoadMore && !this.nomore) {
    this.loadingMore = true;
    //Data cannot be scrolled for the time being
    this.nomore = true;
    this.loadMoreArticle();
  }

document.documentElement.scrollTop: the distance to scroll the document
document.documentElement.clientHeight: the height of the document in visual range
document.documentElement.scrollHeight: total document height

The judgment idea is: the height of the viewport + the scrolling distance of the document > = the total height of the document. You can reserve a distance of 50 for preloading. Each time the front-end judges the bottom, it will ask the back-end for the next page of data and returnnomoreField indicating whether there are any articles that have not been loaded. Here is a detail: after each bottom touch, manually set thenomoreSet totrue, back end returnnomoreThe purpose of this is to prevent the request from being sent again before the request returns.
Reference: clarify clientheight, offsetHeight, scrollheight, offsettop, scrolltop

How to display blog posts

Before I started my blog, I thought that every blog post was set up by writing HTML tags alone. Then, through other people’s projects, I found the right way to do it
Use markdown / rich text editor to edit the article and generatehtmlFragment, used in Vuev-htmlSyntax inserts thehtmlThis is a short passagehtmlThe form of the label can be rendered. The markdown editor used in the blog is the MAVON editor. In addition, thehighlightjsHighlight the code.

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

Extract directory

    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
        });
      });
    }

In the previous section, thehtmlThe clip is rendered todivIn the container element, you can use thequerySelectorAll("h1,h2,h3,h4,h5,h6")To find all the node titles in the text,querySelectorAll()The advantage is that it looks in the order of the incoming parameters, so you don’t have to worry about out of order. After getting the directory, the directory numbers of all levels are numbered in order to generate anchor points for jump.

Judge whether the corresponding stanza appears

    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);

Judgment method: the distance from the top edge of the element to the top of the view + the height of the element itself < = the height of the view & the distance from the top of the element to the top of the view + the height of the element > 0
To get the distance between the element and the top edge of the view, use:getBoundingClientRect(), a collection of the positions of an element relative to the window. There are top, right, bottom, left attributes in the collection

rectObject = object.getBoundingClientRect();

rectObject.top  //The distance from the top edge of the element to the top edge of the window;

rectObject.right  //The distance from the right side of the element to the left side of the window;

rectObject.bottom  //The distance from the lower edge of the element to the upper edge of the window;

rectObject.left  //The distance from the left side of the element to the left side of the window;

In the DOM structure of the whole article, usequerySelectorAll("h1,h2,h3,h4,h5,h6")Search the DOM nodes of all title classes. Each time you scroll, dynamically check the distance between these header elements and the top of the viewport. If you find the first DOM element that appears in the viewport, you will return. In addition, the throttle function can optimize the performance.

Reference: how to determine whether an element is in the viewport

Implementation of comments

The data structure of comment data is as follows. Here, only secondary comments are achieved. The data structure is determined, and the specific implementation is relatively simple.

let comments = [
  {
    author: "admin",
    Content: "message 1",
    articleId: "9",
    time: "2020-04-12 10:59",
    id: "0",
    replyList: [
      {
        author: "ghm",
        Content: "reply to message 1",
        articleId: "9",
        replyTo: "admin",
        time: "2020-04-12 10:59",
        id: "0-0"
      }
    ]
  },
  {
    author: "admin",
    Content: "message 2",
    articleId: "9",
    time: "2020-04-12 10:59",
    id: "1",
    replyList: [
      {
        author: "ghm",
        Content: "reply to message 2",
        articleId: "9",
        replyTo: "admin",
        time: "2020-04-12 11:00",
        id: "1-0"
      }
    ]
  }
];

Input box for adding expression

Implementation of the first edition: in<input>Insert directly intoemojiOfunicodeThe display effect is as follows:
Personal blog based on vue2, eggjs and MySQL (being updated)
The problem with this is that the display effect is not as good as the picture, and it will show different effects in different browsers. Decisively change the form of pictures to show the expression, but<input>Yes, it can’t be inserted<img>Tag, then refer to the implementation of the Nuggets, usingcontenteditableThis CSS attribute changes the normal DOM element into an editable DOM element so that you can use theappendChild()In a way that will<img>Insert, the final display effect is also significantly better than the direct useemojiYes.

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

Personal blog based on vue2, eggjs and MySQL (being updated)

back-end

Design of database table

go online

Configure the server environment

1. A Linux server
The cheapest ECS cloud server purchased by the author is configured with a core of 2G and 1m bandwidth. It is enough for learning and using CentOS as the operating system. Then, according to the tutorial given by alicloud, deploy nodejs environment on the server and deploy mysql.
2. Purchase domain name
After purchasing a domain name on alicloud, follow the novice’s Guide to set the domain name resolution, and set the WWW and @, then the website can go through the www.xxx.com And xxx.com visit. At the same time, if you want to use the domain name to visit the website, you need to record the domain name.
3. Migrate the local database to the cloud server
The database visualization tool used by the author is Navicat. In Navicat, the SQL file of the selected database is dumped, and then a new database is created in MySQL of the cloud server, and the SQL file is run to complete the database migration.
Personal blog based on vue2, eggjs and MySQL (being updated)

Upload files

1. Download the file transfer tool xftp
2. Deploy front-end code

npm run build

After packing, use xftp todistThe folder is uploaded to the server
3. Deploy the back-end code
Put the back-end codegit cloneTo the server

npm i
npm start

Configure nginx

1. Install nginx

yum install nginx

The installed nginx will be in the/etc/nginxin
2. Use xftp to modify nginx configuration
find/etc/nginxDirectorynginx.confFile, open in Notepad, edit:

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;
    }
  }
}

These two configurations are important:

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

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

After the website is deployed, it can be accessed normally. However, if you open the secondary page and refresh it, it will be 404. The reason is that the path set by v-router is not a real route. The route jump in the project is implemented through JS, and the front-end is packageddistAfter the directory is deployed on the server, accessing the root path in the browser will default to index.html However, if you directly access other paths, there is no real path corresponding to it. (Reference: https://www.cnblogs.com/kevingrace/p/6126762.html )

After completing the above configuration, you can pass theServer IP: 80To visit the deployed website, but it is not reasonable to visit the website through IP address all the time, so you need to configure the domain name for the website.

Configure domain name

Recommended Today

How to set the prompt box automatically pop up when the computer is plugged into the headset?

Sometimes, we want to automatically pop up the prompt box (or not) after inserting the headset. In this case, we can follow the following steps to solve the problem: 1. Clickopening menu——>control panel 2. ClickDell Audio 3. Clicksenior——>Jack information——>Select on the right“Open the auto pop-up dialog box when inserting the device”Radio box. Related recommendations: How […]