Preface: Recently in writing some news information details of the page, header components, footer components, directory components are fixed, only the content of the news is changed. In order not to write duplicate code, we are going to use the idea of rendering md files to HTML files. If we add new news pages later, we just need to add the md files that are due.

General idea: Write the common part of each news page as a template, write the changed content part in the MD file, use NodeJS to read the MD file, convert it into HTML, replace the changed content in the template.

React Next-js NodeJS markdown-it

Other solutions: This problem has been explored for a long time, and many approaches have been tried. Some of them can be read and rendered, but not in the end, but just to summarize.

1. Usereact-markdownTo deal with

introduce

Usage reference:

  • Use react-Markdown
  • Github reference link

We can read the contents of the MD file and use the contents as properties of the component, but this is not the way we want to do it. We can read the contents of the MD file and use them as properties of the component. But you can do this by passing it to higher-order components.

2. UseloaderTo deal with

  • markdown-loader
  • React Markdown
  • .

I used Webpack to parse the MD file during packaging, which I didn’t use, because my project was based on Next. Js, and I didn’t know how to write the configuration file.


NodeJS file processing operations

Fs file module

1. Import the FS module manually

const fs = require('fs')
Copy the code

2. Check the file status

Fs.stat (path[, options], callback) asynchronous method

Fs.statsync (path[, options]) Synchronization method

  • callbackIf the file path is wrong, err will print an error. If the file path is not wrong, err will print null.
  • statsPrints some state of the current file.
    • birthtime: File creation time
    • mtime: Indicates the time when the file content is changed
  • tofs.stat()The path of transfer__filenameWhen available.isFile()Check whether the current path corresponds to a file.
  • tofs.stat()The path of transfer__dirnameWhen available.isDirectory()Check whether the current path corresponds to a folder.
fs.stat(filedir, function (eror, stats) {
    if (eror) {
        console.warn('Obtaining file stats failed')}else{var isFile = stats.isfile () var isDir = stats.isdirectory () // Is a folderif(isFile) {// How the file will operate}if(isDir) {// Is how the folder will operate}}}Copy the code

3. Read and write files

Fs. readFile(path[, options], callback) reads asynchronously

Fs. readFileSync(path[, options]) read synchronously

Fs. writeFile(file, data[, options], callback) write asynchronously

Fs. writeFileSync(file, data[, options]) synchronous write

Path Path module

Path. join([…paths]) : Used to join paths

If a/is not added to the argument, the method adds it automatically

If the parameter has.. , the system automatically goes to the upper path based on the path generated by the previous parameter

let str = path.join("/a/b"."c"); // /a/b/c
let str = path.join("/a/b"."/c"); // /a/b/c (not added/automatically added)let str = path.join("/a/b"."/c".".. /"); // /a/b/c --> /a/b
let str = path.join("/a/b"."/c".".. /.. /"); // /a/b/c --> /a
console.log(str);
Copy the code

Path.resolve ([…paths]) : Used to resolve paths

If the following argument is an absolute path, the preceding argument is ignored

let res = path.resolve('/foo/bar'.'./baz'); // /foo/bar/baz
let res = path.resolve('/foo/bar'.'.. /baz'); // foo/baz(upper level)let res = path.resolve('/foo/bar'.'/baz'); // /baz(absolute path) console.log(res);Copy the code

Circular folder

1. Read the file based on the file path and return the file list

2. Traverse the list of read files

3. Obtain the absolute path of the current file

4. Obtain file information based on the file path and return an Fs. Stats object

5. Determine whether the current file is a file or folder according to the fs.Stats object

6. If it is a folder, continue to traverse the files under the folder

let mdPath = path.join(__dirname, '/src/md')
fileDisplay(mdPath)
functionFileDisplay (mdPath) {// Read the file according to the file path, return the file list fs.readdir(mdPath,function (err, files) {
    if (err) {
      console.warn(err)
    } else{// Iterate over the list of files read files.foreach (functionVar filedir = path.join(mdPath, filename) var filedir = path.join(mdPath, filename) var filedir = path.join(mdPath, filename)function (eror, stats) {
          if (eror) {
            console.warn('Obtaining file stats failed')}else{var isFile = stats.isfile () var isDir = stats.isdirectory () // Is a folderif(isFile) {// Operations on files}if(isDir) {fileDisplay(filedir) // recursively, if it is a folder, continue to traverse the files under that folder}}})})})})})})}Copy the code

Several ways to copy files

readFile & writeFile

fs.readFile(filedir, 'utf8', (err, data) => {
  if (err) console.log(err)
  elseFs. writeFile(writePath, tplData,'utf8', (err) => {// write operation})}})Copy the code

createReadStream&createWriteStream

// 1. Generate read and write pathslet readPath = path.join(__dirname, "test.mp4");
let writePath = path.join(__dirname, "abc.mp4"); // create a read streamlet readStream = fs.createReadStream(readPath); // create a write streamletwriteStream = fs.createWriteStream(writePath); // 4. Listen for read stream eventsreadStream.on("open".function () {
  console.log("Indicates a successful relationship between data flow and file.");
});
readStream.on("error".function () {
  console.log("Failed to establish relationship between data stream and file");
});
readStream.on("data".function (data) {
  // console.log("Indicates that data has been read from a file through the read stream.", data);
  writeStream.write(data);
});
readStream.on("close".function () {
  console.log("Indicates that the data stream is disconnected from the file and that the data has been read."); writeStream.end(); }); // 5. Listen to the event writestream.on ("open".function () {
  console.log("Indicates a successful relationship between data flow and file.");
});
writeStream.on("error".function () {
  console.log("Failed to establish relationship between data stream and file");
});
writeStream.on("close".function () {
  console.log("Indicates that the data stream is disconnected from the file.");
});
Copy the code

Pipe () Reads the pipe method of the stream

// 1. Generate read and write pathslet readPath = path.join(__dirname, "test.mp4");
let writePath = path.join(__dirname, "abc.mp4"); // create a read streamlet readStream = fs.createReadStream(readPath); // create a write streamletwriteStream = fs.createWriteStream(writePath); // Use read stream pipe method to quickly implement file copyreadStream.pipe(writeStream);
Copy the code

Markdown-it processes files

Markdown-it Chinese document

The installation

npm install markdown-it --save
Copy the code

usage

// import const md = require('markdown-it'/ / uselet result = md.render('# markdown-it rulezz! ');
Copy the code

The specific use

1. Read all the MD files in the MD folder

2. Use Markdown-it to process what you read

3. Read the template information

4. Process the read file name, component name conversion, etc. (short line to hump)

5. Replace the changed part in the template page with the processed MD content

// md file pathlet mdPath = path.join(__dirname, '/src/md') // Template pathlet tplPath = path.join(__dirname, '/src/template.txt') // Filedir is read in a loop, for reference onlyletFiledir = path.join(mdPath, filename) // Read md file fs.readfile (filedir,'utf8', (err, data) => {
  if (err) console.log(err)
  else {
    let result = md.render(data)
    // console.log('MD file read result'+ result) fs.readfile (tplPath,'utf-8', (err, tplData) => {
      if (err) console.log(err)
      else{// Process the md file name, removing the preceding path and file type suffixlet filename = filedir.replace(__dirname, ' ')
        let startIndex = filename.lastIndexOf('\ \')
        let endIndex = filename.indexOf('. ') filename = filename.substring(startIndex + 1, EndIndex) // console.log(filename) // Processing component name uppercase // a-coder -beauty should be ACoderBeauty // template data to replace tplData = tpldata.replace ('<%componentName%>', componentName)
          .replace('<%template%>', result)
          .replace('<%componentName%>', componentName)
        let writePath = path.join(
          __dirname,
          '/src/pages',
          filename + '.js'Fs.writefile (writePath, tplData,'utf8', (err) => {
          // console.log(err)
        })
      }
    })
  }
})
Copy the code

Template. TXT file for reference

import React from 'react'
import { observer } from 'mobx-react'// Component import import Head from'.. /components/head'
import Header from '.. /components/header'
import Footer from '.. /components/footer'

import '.. /styles/common.less'@observer class <%componentName%> extends React.Component {render() {
    return (
      <div className='article-container'>
        <Head></Head>
        <Header></Header>
        <div className='article-body-container'>
          <div className='article-detail-container'>
            <%template%>
          </div>
        </div>
        <Footer></Footer>
      </div>
    )
  }
}
export default <%componentName%>
Copy the code

There is a problem

Markdown-it does not escape the tag correctly when processing images in the MD file. There is no solution to this problem in the documentation, and it should be due to the rendering mechanism.

Solution 1: Download the source code, do some processing, in the configuration file to introduce the modified source file. Add a judgment to the return result of the processing tag. If it is an tag, enter the judgment above.

const md = require('./src/utils/markdown-it') ()Copy the code

Solution 2: Enable the HTML tag in the source code at the time of introduction, which is a bit awkward.

summary

All of the above file handling code is written in the configuration file next-config.js. Personal use of Next-.js is at the basic level, but what webpack.config.js can do, it can be written in next-config.js.