前言:最近在寫一些新聞資訊詳情的頁面,header組件、footer組件、目錄組件都是固定的,只有新聞的內容是變化的。爲了避免去寫重複的代碼(有句話怎麼說來着:戰略上偷懶,戰術上勤奮
,就是這個意思),準備採用md文件渲染爲html文件這個思路,若是後續追加新的新聞資訊頁面,只需新增對應得md文件便可。html
總體思路:把每一個新聞資訊頁面共有的的部分固定寫好做爲模板,變化的內容部分寫在md文件裏面,利用NodeJS讀取md文件,轉換成html以後,替換模板中變化的內容。react
相關技術棧:React
Next.js
NodeJS
markdown-it
webpack
其餘解決方案:這個問題是好久以前就在摸索的了,試了不少方法。有些能夠讀能夠渲染,可是不是最終想要的,不過仍是順便總結一下。git
react-markdown
處理使用參考:github
思路:這個以前嘗試過一次,確實能讀取md文件的內容,也能把內容做爲組件的屬性,可是這樣作並不符合個人思路,只是把變化的內容放到了md文件裏,再讀出來放到了組件屬性裏面操做。不過能夠經過這個方法,傳遞到高階組件裏面處理。web
loader
處理markdown-loader
React Markdown
都是利用webpack在打包的時候去解析md文件,這個我沒有采用,由於個人項目是基於Next.js
的,配置文件不知道如何去寫比較好。npm
const fs = require('fs')
複製代碼
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) {
//是文件夾將如何操做
}
}
}
複製代碼
fs.readFile(path[, options], callback)
異步讀取markdown
fs.readFileSync(path[, options])
同步讀取
fs.writeFile(file, data[, options], callback)
異步寫入
fs.writeFileSync(file, data[, options])
同步寫入
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);
複製代碼
安裝
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
裏面同樣能行。