使用markdow-it渲染md文件爲html頁面

前言:最近在寫一些新聞資訊詳情的頁面,header組件、footer組件、目錄組件都是固定的,只有新聞的內容是變化的。爲了避免去寫重複的代碼(有句話怎麼說來着:戰略上偷懶,戰術上勤奮,就是這個意思),準備採用md文件渲染爲html文件這個思路,若是後續追加新的新聞資訊頁面,只需新增對應得md文件便可。html

總體思路:把每一個新聞資訊頁面共有的的部分固定寫好做爲模板,變化的內容部分寫在md文件裏面,利用NodeJS讀取md文件,轉換成html以後,替換模板中變化的內容。react

相關技術棧React Next.js NodeJS markdown-itwebpack

其餘解決方案:這個問題是好久以前就在摸索的了,試了不少方法。有些能夠讀能夠渲染,可是不是最終想要的,不過仍是順便總結一下。git

1.使用react-markdown處理

介紹:Renders Markdown as pure React components. —— 將Markdown渲染爲純React組件。

使用參考github

思路:這個以前嘗試過一次,確實能讀取md文件的內容,也能把內容做爲組件的屬性,可是這樣作並不符合個人思路,只是把變化的內容放到了md文件裏,再讀出來放到了組件屬性裏面操做。不過能夠經過這個方法,傳遞到高階組件裏面處理。web

2.使用loader處理

  • markdown-loader
  • React Markdown
  • ...

都是利用webpack在打包的時候去解析md文件,這個我沒有采用,由於個人項目是基於Next.js的,配置文件不知道如何去寫比較好。npm


NodeJS文件處理操做

fs文件模塊

1.使用fs模塊需手動導入

const fs = require('fs')
複製代碼

2.查看文件狀態

fs.stat(path[, options], callback) 異步方法segmentfault

fs.statSync(path[, options]) 同步方法bash

  • callback的函數裏的err,在文件路徑出錯的時候會打印錯誤,文件路徑不出錯則err打印null;
  • stats打印當前文件的一些狀態。
    • birthtime: 文件的建立時間
    • mtime: 文件中內容發生變化,文件的修改時間
  • fs.stat()的path傳遞__filename時可用.isFile()判斷當前路徑對應的是不是一個文件。
  • fs.stat()的path傳遞__dirname時可用.isDirectory()判斷當前路徑對應的是不是一個文件夾。
fs.stat(filedir, function (eror, stats) {
    if (eror) {
        console.warn('獲取文件stats失敗')
    } else {
        var isFile = stats.isFile() //是文件
        var isDir = stats.isDirectory() //是文件夾
        if (isFile) {
            //是文件將如何操做
        }
        if (isDir) {
            //是文件夾將如何操做
        }
    } 
}
複製代碼

3.文件讀取與寫入

fs.readFile(path[, options], callback) 異步讀取markdown

fs.readFileSync(path[, options]) 同步讀取

fs.writeFile(file, data[, options], callback) 異步寫入

fs.writeFileSync(file, data[, options]) 同步寫入

path路徑模塊

path.join([...paths]):用於拼接路徑

若是參數中沒有添加/,那麼該方法會自動添加

若是參數中有..,那麼會自動根據前面的參數生成的路徑,去到上一級路徑

let str = path.join("/a/b", "c"); // /a/b/c
let str = path.join("/a/b", "/c"); // /a/b/c (不添加/會自動添加)
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);
複製代碼

path.resolve([...paths]):用於解析路徑

若是後面的參數是絕對路徑,那麼前面的參數就會被忽略

let res = path.resolve('/foo/bar', './baz'); // /foo/bar/baz
let res = path.resolve('/foo/bar', '../baz'); // /foo/baz(上一級)
let res = path.resolve('/foo/bar', '/baz'); // /baz(絕對路徑)
console.log(res);
複製代碼

循環文件夾

1.根據文件路徑讀取文件,返回文件列表

2.遍歷讀取到的文件列表

3.獲取當前文件的絕對路徑

4.根據文件路徑獲取文件信息,返回一個fs.Stats對象

5.根據fs.Stats對象判斷當前文件是文件仍是文件夾

6.若是是文件夾,就繼續遍歷該文件夾下面的文件

let mdPath = path.join(__dirname, '/src/md')
fileDisplay(mdPath)
function fileDisplay(mdPath) {
  //根據文件路徑讀取文件,返回文件列表
  fs.readdir(mdPath, function (err, files) {
    if (err) {
      console.warn(err)
    } else {
      //遍歷讀取到的文件列表
      files.forEach(function (filename) {
        //獲取當前文件的絕對路徑
        var filedir = path.join(mdPath, filename)
        //根據文件路徑獲取文件信息,返回一個fs.Stats對象
        fs.stat(filedir, function (eror, stats) {
          if (eror) {
            console.warn('獲取文件stats失敗')
          } else {
            var isFile = stats.isFile() //是文件
            var isDir = stats.isDirectory() //是文件夾
            if (isFile) {
              //對文件的相關操做
            }
            if (isDir) {
              fileDisplay(filedir) //遞歸,若是是文件夾,就繼續遍歷該文件夾下面的文件
            }
          }
        })
      })
    }
  })
}
複製代碼

拷貝文件的幾種方式

readFile & writeFile

fs.readFile(filedir, 'utf8', (err, data) => {
  if (err) console.log(err)
  else {
    // 讀取以後的其餘操做
    fs.writeFile(writePath, tplData, 'utf8', (err) => {
      // 寫入操做
    })
  }
})
複製代碼

createReadStream&createWriteStream

// 1.生成讀取和寫入的路徑
let readPath = path.join(__dirname, "test.mp4");
let writePath = path.join(__dirname, "abc.mp4");
// 2.建立一個讀取流
let readStream = fs.createReadStream(readPath);
// 3.建立一個寫入流
let writeStream = fs.createWriteStream(writePath);
// 4.監聽讀取流事件
readStream.on("open", function () {
  console.log("表示數據流和文件創建關係成功");
});
readStream.on("error", function () {
  console.log("表示數據流和文件創建關係失敗");
});
readStream.on("data", function (data) {
  // console.log("表示經過讀取流從文件中讀取到了數據", data);
  writeStream.write(data);
});
readStream.on("close", function () {
  console.log("表示數據流斷開了和文件的關係, 而且數據已經讀取完畢了");
  writeStream.end();
});
// 5.監聽寫入流的事件
writeStream.on("open", function () {
  console.log("表示數據流和文件創建關係成功");
});
writeStream.on("error", function () {
  console.log("表示數據流和文件創建關係失敗");
});
writeStream.on("close", function () {
  console.log("表示數據流斷開了和文件的關係");
});
複製代碼

pipe()讀取流的管道方法

// 1.生成讀取和寫入的路徑
let readPath = path.join(__dirname, "test.mp4");
let writePath = path.join(__dirname, "abc.mp4");
// 2.建立一個讀取流
let readStream = fs.createReadStream(readPath);
// 3.建立一個寫入流
let writeStream = fs.createWriteStream(writePath);
// 利用讀取流的管道方法來快速的實現文件拷貝
readStream.pipe(writeStream);
複製代碼

markdown-it處理文件

markdown-it中文文檔

安裝

npm install markdown-it --save
複製代碼

用法

//導入
const md = require('markdown-it')

//使用
let result = md.render('# markdown-it rulezz!');
複製代碼

具體使用

1.循環讀md文件夾下的全部md文件

2.利用markdown-it處理讀到的內容

3.讀取模板頁面的內容

4.處理讀取到的文件名稱、組件名稱轉換等(短線轉駝峯)

5.替換模板頁面中變化的部分爲,處理以後的md內容

// md文件路徑
let mdPath = path.join(__dirname, '/src/md')
// 模板路徑
let tplPath = path.join(__dirname, '/src/template.txt')
// filedir實際上是循環讀取的,這裏僅作參考一下
let filedir = path.join(mdPath, filename)

// 讀取md文件
fs.readFile(filedir, 'utf8', (err, data) => {
  if (err) console.log(err)
  else {
    let result = md.render(data)
    // console.log('md文件讀取的結果' + result)
    
    // 讀取模板頁面
    fs.readFile(tplPath, 'utf-8', (err, tplData) => {
      if (err) console.log(err)
      else {
      
        // 處理md文件名,去掉前面的路徑和文件類型後綴
        let filename = filedir.replace(__dirname, '')
        let startIndex = filename.lastIndexOf('\\')
        let endIndex = filename.indexOf('.')
        filename = filename.substring(startIndex + 1, endIndex)
        // console.log(filename)

        // 處理組件名稱首字母大寫
        // A-coder-beauty應爲ACoderBeauty
        
        // 模板數據替換
        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)
        })
      }
    })
  }
})
複製代碼

template.txt文件參考

import React from 'react'
import { observer } from 'mobx-react'

// 組件導入
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%>
複製代碼

小結

以上全部代碼都是在配置文件next.config.js裏面寫的,我我的對Next.js的使用停留在基礎層面,不過webpack.config.js能作的,寫在next.config.js裏面同樣能行。

相關文章
相關標籤/搜索