Learn React process to implement a personal blog, no complex implementation and operation, suitable for getting started ~

Original address: github.com/axuebin/rea…


In fact, the function of this project is very simple, is the common home page, blog, demo, about and other functions.

The page style is self-written, black and white style, maybe a little ugly. However, it is still the lowest level CSS, ready to be reconstructed

If there is a better way, or is my idea deviation, welcome to exchange correction

Welcome to axuebin.com/react-blog

Github:github.com/axuebin/rea…

preview

Home page

Blog page

Article content page

The Demo page

The key technology

  • ES6: ES6 syntax is used in the project, try to use it in the writing process, there may be some unexpected places
  • React
  • React-router: indicates the front-end route
  • React-redux: state management
  • Webpack: packaging
  • Marked: Markdown
  • Highlight.js: code highlighting
  • Fetch: Asynchronously requests data
  • Eslint: Code checking
  • Antd: : Some components are too lazy to write themselves.

The preparatory work

Since it’s not a React scaffolding project, everything is configured manually…

Module packer

Packed with Webpack 2.6.1, ready to pit Webpack 3.

Official documentation: webpack.js.org/

英 文 原 文 : doc.webpack-china.org/

I’m not familiar with webpack configuration, so I have a simple configuration for the project to start:

var webpack = require('webpack');
var path = require('path');

module.exports = {
  context: __dirname + '/src'.entry: "./js/index.js".module: {
    loaders: [{test: /\.js? $/.exclude: /(node_modules)/.loader: 'babel-loader'.query: {
          presets: ['react'.'es2015']}}, {test: /\.css$/.loader: 'style-loader! css-loader'
      }, {
        test: /\.js$/.exclude: /(node_modules)/.loader: 'eslint-loader'
      }, {
        test: /\.json$/.loader: 'json-loader'}},output: {
    path: __dirname + "/src/".filename: "bundle.js"}}Copy the code

Webpack has several important attributes: Entry, Module, Output, and plugins. I haven’t used plug-ins yet, so I haven’t configured plugins.

Loaders in module:

  • Babel-loader: Converts code to ES5 code
  • Css-loader: handles problems such as path reference in the CSS
  • Style-loader: Dynamically writes styles to CSS
  • Eslin-loader: uses esLint

Package management

Package management is still using NPM.

Official documentation: docs.npmjs.com/

  1. npm init
  2. npm install
  3. npm uninstall

NPM dependencies and devDependencies

  • Dependencies: Modules that need to be used once the project is running
  • DevDependencies: Module required during development, but not once the project is running

Code review

The project uses the now popular ESLint as a code inspection tool and uses Airbnb’s inspection rules.

ESLint:github.com/eslint/esli…

Eslint-config-airbnb:www.npmjs.com/package/esl…

As you can see in package.json, the package for ESLint is placed under devDependencies because it is only used at development time.

use

  • inwebpackLoading in configurationeslint-loader:
module: {
  loaders: [{test: /\.js$/.exclude: /(node_modules)/.loader: 'eslint-loader'}}]Copy the code
  • create.elintrcFile:
{
  "extends": "airbnb"."env": {"browser": true
  },
  "rules":{}
}Copy the code

When you run Webpack, you can check the code and see a bunch of warnings and errors

Here’s the common ESLint rule: eslint.cn/docs/rules/

The data source

In order to practice React, I decided to set up a static page for the time being. As more and more people prefer to use Github Issues to write blogs and provide better comments, I decided to try using Github Issues as the data source for my blog.

API in this: developer.github.com/v3/issues/

I also looked at how to get the Issues list without looking at the entire API.

https://api.github.com/repos/axuebin/react-blog/issues?creator=axuebin&labels=blogCopy the code

By controlling the parameters Creator and Labels, you can screen out the Issues for display. It returns an array with an issue format object. Each issue has many attributes, we may not need so many, first understand the following:

// For convenience, I wrote the comments in JSON.
[{
  "url":,Url / / issue
  "id":,// Issue ID, which is a randomly generated non-repeating number string
  "number":,// Issue number, which accumulates from 1 in the order in which the issues are created
  "title":,// Issue title
  "labels": [].// All labels of the issue, which is an array
  "created_at":,// When the issue was created
  "updated_at":,// Modify the time of the issue last
  "body":,// Contents of the issue
}]Copy the code

Asynchronous request data

The method used in the project asynchronously requests data when fetch.

About the fetch: segmentfault.com/a/119000000…

It’s easy to use:

fetch(url).then(response= > response.json())
      .then(json= > console.log(json))
      .catch(e= > console.log(e));Copy the code

Markdown rendering

See Github for more information on how to render markdown in React. There are two libraries:

  • React-markdown:github.com/rexxars/rea…
  • marked:github.com/chjj/marked

It’s all very simple to use.

If it’s react-markdown, just do this:

import ReactMarkdown from 'react-markdown';

const input = '# This is a header\n\nAnd this is a paragraph';
ReactDOM.render(
    <ReactMarkdown source={input} />,
    document.getElementById('container')
);Copy the code

If marked, do this:

import marked from 'marked';

const input = '# This is a header\n\nAnd this is a paragraph';
const output = marked(input);Copy the code

Here’s a little bit different. We got a string output, notice it’s a string, so we have to insert it into the DOM. In React, we can do this:

<div dangerouslySetInnerHTML={{ __html: output}} / >Copy the code

Since our project is based on React, we thought it would be better to use React markdown. Besides, React does not recommend directly inserting strings into dom due to security issues. However, we found that React Markdown was not friendly to table support, so we had to abandon it. To switch to marked.

Code highlighting

The code is highlighted with highlight.js: github.com/isagalaev/h…

It can be seamlessly connected with marked

Just do this:

import hljs from 'highlight.js';

marked.setOptions({
  highlight: code= > hljs.highlightAuto(code).value,
});Copy the code

Highlight.js supports multiple code color schemes and can be toggled in a CSS file:

@import '~highlight.js/styles/atom-one-dark.css';Copy the code

See highlights and color schemes for each language here: highlightjs.org/

React

What are state and props

Check out the previous article: github.com/axuebin/rea…

The life cycle of the React component

Check out the previous article: github.com/axuebin/rea…

The front-end routing

React-Router V4 was used for the front-end routing in the project.

Official documentation: reacttraining.com/react-route…

English documentation: reactTraining.cn /

The basic use

<Link to="/blog">Blog</Link>Copy the code
<Router>
  <Route exact path="/" component={Home} />
  <Route path="/blog" component={Blog} />
  <Route path="/demo" component={Demo} />
</Router>Copy the code

Note: Be sure to declare exact in the Route of the root directory, otherwise clicking any link will not be able to jump to it.

Level 2 Directory jump

Like I’m going to jump on the blog page by clicking on, this url is localhost: 8080 / blog, need to become localhost: 8080 / article/blog, can do it:

<Route path={`The ${this.props.match.url}/article/:number`} component={Article} />Copy the code

So you can jump to localhost: 8080 / blog/article, but also passed a number of parameters, in the article can through this. Props. Params. Number.

HashRouter

When I hosted the project on Github Page, I had this problem.

The Cannot GET/message is displayed, indicating that the route does not take effect.

Through understanding, we know that the reason is like this and can solve it:

  • This occurs because after the refresh, a request is sent to the server based on the URL rather than processing the routeCannot GET /Error.
  • By modifying the<Router><HashRouter>
  • <HashRouter>Routing is implemented using hashes on urls. You can switch pages without the need for a full screen refresh.

Routes do not automatically return to the top after jumping

After a page scrolls to a certain area, click “Jump”, the page will jump, but it will stay in the scrolling area, and will not automatically return to the top of the page.

This can be solved by:

componentDidMount() {
    this.node.scrollIntoView();
}

render() {
  return (
    <div ref={node= > this.node = node} ></div>
  );
}Copy the code

State management

The project needs to request data from Github Issues several times, because Redux is known to exist before, although it is a bit of overuse, in order to learn to use it for project state management, only need to request data once.

Official document: redux.js.org/

Chinese version: cn.redux.js.org/

In simple terms, every time the state is changed, the action needs to be triggered. However, I haven’t changed the data in the project yet.

About the state management of this piece, because I do not know too much, I will not mislead people ~

The major components

React is built on components, so at the beginning of the build page, we need to think about what components we need, how they are related to each other, which components can be reused, etc.

Home page

As you can see, I’ve divided the home page into four main sections:

  • Header: Site title, subtitle, navigation bar
  • Banner: About me ~, ready to change the background with my own photo, but there is no suitable photo yet
  • Card area: Indicates three cards temporarily
    • Blog card: a number of recent blog posts
    • Demo card: Several small demo categories
    • Me Card: Kind of a place where I can fly
  • Footer: Copyright information, record information, page views

Blog page

Blog page is a very regular page, this part is the most code in the whole project, including the following parts:

  • Article list component
  • Page components
  • Archive button component
  • Category component
  • The label component

The article lists

A list of articles is a list of items:

<div class="archive-list">
  <div class="blog-article-item">Article 1</div>
  <div class="blog-article-item">Article 2</div>
<div>Copy the code

For each item, it looks like this:

An article item component might need to include:

  • The article title
  • The date, category, tag, etc
  • The article based on
  • .

If described in DOM, it would look something like this:

<div class="blog-article-item">
  <div class="blog-article-item-title">The article title</div>
  <div class="blog-article-item-time">time</div>
  <div class="blog-article-item-label">category</div>
  <div class="blog-article-item-label">The label</div>
  <div class="blog-article-item-desc">Abstract</div>
</div>Copy the code

So, we can have many components:

  • Article list component<ArticleList />
  • Article Item component<ArticleItem />
  • Category label component<ArticleLabel />

They could be a relationship like this:

<ArticleList>
  <ArticleItem>
    <ArticleTitle />
    <ArticleTime />
    <ArticleLabel />
    <ArticleDesc />
  </ArticleItem>
  <ArticleItem></ArticleItem>
  <ArticleItem></ArticleItem>
</ArticleList>Copy the code

paging

For paging, the traditional approach is to complete the paging at the back end and then batch it back to the front end. For example, it might return a piece of data like this:

{
  total:500.page:1.data:[]
}Copy the code

That is, the back end returns page-divided data, with total representing the total amount of data, page representing the current number of pages, and data belonging to that page.

However, this page is only a static page and the data is obtained through API on Github Issues. (Github Issues pages can’t be customized…) , so can not directly return the sorted data, so can only be forced in front of the page ~

I missed the pagination part… Using ANTD’s Pagination component .

Ant.design/Components /…

The documentation is clear and extremely simple to use.

The logic of front-end rendering is (somewhat silly) : store the data in an array, calculate the index value of the display based on the current number of pages and the number of pages displayed per page, and fetch the corresponding data.

Page turning component:

constructor() {
  super(a);this.onChangePage = this.onChangePage.bind(this);
}

onChangePage(pageNumber) {
  this.props.handlePageChange(pageNumber);
}

render() {
  return (
    <div className="blog-article-paging">
      <Pagination onChange={this.onChangePage} defaultPageSize={this.props.defaultPageSize} total={this.props.total} />
    </div>
  );
}Copy the code

When the number of pages changes, the method handlePageChange passed from the parent component to
is triggered, passing the number of pages to the parent component and then to
.

Parent component:

handlePageChange(pageNumber) {
  this.setState({ currentPage: pageNumber });
}

render() {
  return (
    <div className="archive-list-area">
      <ArticleList issues={this.props.issues} defaultPageSize={this.state.defaultPageSize} pageNumber={this.state.currentPage} />
      <ArticlePaging handlePageChange={this.handlePageChange} total={this.props.issues.length} defaultPageSize={this.state.defaultPageSize} />
    </div>
  );
}Copy the code

The list:

render() {
  const articlelist = [];
  const issues = this.props.issues;
  const currentPage = this.props.pageNumber;
  const defaultPageSize = this.props.defaultPageSize;
  const start = currentPage === 1 ? 0 : (currentPage - 1) * defaultPageSize;
  const end = start + defaultPageSize < issues.length ? start + defaultPageSize : issues.length;
  for (let i = start; i < end; i += 1) {
    const item = issues[i];
    articlelist.push(<ArticleItem />); }}Copy the code

label

In Github Issues, you can add many labels to an issue. I have divided these labels that are useful for blog content into three categories, which are represented in different colors.

To clarify, an ID will be randomly generated after the label is created. Although the ID is not repeated, the category and label of the article will keep increasing. When a new label is added, corresponding modifications may also be made in the program, which may not be appropriate as the standard to distinguish the label. So I use color to distinguish them.

  • This is an article blog: onlyblogissueIn order to display on the page, filterbughelp
  • Category of article: Used to indicate the category of article, such as “front-end”, “photography”, etc
  • Article tag: The tag used to represent the article, such as “JavaScript”, “React”, etc

Even if there is a new label, it is good to distinguish which category it belongs to by color.

category

The main idea here is to traverse all issues, then traverse the labels of each issue, find the labels belonging to the category, and count.

const categoryList = [];
const categoryHash = {};
for (let i = 0; i < issues.length; i += 1) {
  const labels = issues[i].labels;
  for (let j = 0; j < labels.length; j += 1) {
    if (labels[j].color === COLOR_LABEL_CATEGORY) {
      const category = labels[j].name;
      if (categoryHash[category] === undefined) {
        categoryHash[category] = true;
        const categoryTemp = { category, sum: 1 };
        categoryList.push(categoryTemp);
      } else {
        for (let k = 0; k < categoryList.length; k += 1) {
          if (categoryList[k].category === category) {
            categoryList[k].sum += 1;
          }
        }
      }
    }
  }
}Copy the code

This will take three cycles to implement, which is a bit complicated and feels a bit silly, but needs to be improved, if there is a better way, please advise ~

The label

The idea here is basically the same as the idea of categories, but in a different way.

Originally, I wanted to reflect the weight of each label by font size, but later I thought that for me, only those labels would be frequent for the time being, while other labels might be rare. Therefore, it is meaningless to distinguish by font size, so I should change to sorting.

The article page

The article page is divided into two parts:

  • Article content area: Displays article content, displayed in the main area of the page
  • Section table of contents: The section table of contents of an article, displayed in the area to the right of the article

The article content

There are two ways to get specific content:

  • Walk through an array of previously requested articles to find the desired article content
  • throughissue numberSend a new request to get the content directly

I chose the latter.

The article is written in Markdown syntax, so it should be converted to HTML and inserted into the page, using a property that is deprecated by React: dangerouslySetInnerHTML.

In addition to rendering the Markdown, we also had to highlight the code in the article and customize the styles of the different tags in the article.

Chapter directory

First of all, here is an issue, I hope you can give some suggestions ~

React does not recommend using document.getelementByID to get DOM elements, so you have to use string matching to get section titles.

Since I am not familiar with regular expressions and have consulted sf, I used one of the answers:

const issues = content;
const menu = [];
const patt = /(#+)\s+? (.+)/g;
let result = null;
while ((result = patt.exec(issues))) {
  menu.push({ level: result[1].length, title: result[2]}); }Copy the code

Result [1]. Length indicates how many # strings there are. Title indicates the content of the title.

you can jump to , but now the rendered HTML does not have a unique identifier for each title…

Archive page

Filing by year:

Filing by category:

File by label:

The problem

The basic functions have been basically realized, but there are still the following problems, which is also a TodoList

  • Comment function. Using aGithub Issues APIImplement reviews, have to implementGithubAuthorized to log in
  • Go back to the top. Using aantdComponent, howeverstatevisibilityHas always been afalse
  • Home page rendering. Now the js file packed is still too large, resulting in the homepage rendering is too slow. This is the focus of the next work, and I have also learned about optimization in this aspect:
    • webpackLoad on demand. This is probably the most convenient way at present
    • Server rendering. This is troublesome, but the benefits are also more, not only to solve the rendering problem, but also conducive to SEO, so it is alsotodoOne of the
  • The code is confused and the logic is wrong. This is my problem. I need to work on it.

Original address: github.com/axuebin/rea…