国产成人精品无码青草_亚洲国产美女精品久久久久∴_欧美人与鲁交大毛片免费_国产果冻豆传媒麻婆精东

15158846557 在線咨詢 在線咨詢
15158846557 在線咨詢
所在位置: 首頁 > 營(yíng)銷資訊 > 網(wǎng)站運(yùn)營(yíng) > 100行node.js搭建一個(gè)簡(jiǎn)單的靜態(tài)站點(diǎn)生成器

100行node.js搭建一個(gè)簡(jiǎn)單的靜態(tài)站點(diǎn)生成器

時(shí)間:2023-07-25 14:18:02 | 來源:網(wǎng)站運(yùn)營(yíng)

時(shí)間:2023-07-25 14:18:02 來源:網(wǎng)站運(yùn)營(yíng)

100行node.js搭建一個(gè)簡(jiǎn)單的靜態(tài)站點(diǎn)生成器:最近我(作者 Victor Parmar)的一個(gè)同事想開一個(gè)博客,問我有沒有什么建議。在研究了一些靜態(tài)站點(diǎn)生成器和博客引擎后,我覺得 Hugo 是個(gè)很不錯(cuò)的選擇。然而,我同事還有一些其它需求,比如想讓博客都有自定義 URL 以及自定義 CSS 主題。雖然用 Hugo 也能實(shí)現(xiàn)這些要求,但是我還是決定跳過學(xué)習(xí)使用 Hugo 這部分,看看如果我同事已經(jīng)有隨時(shí)可用的 HTML 而且在 HTML 中寫博客沒有問題,能否創(chuàng)建一個(gè)很簡(jiǎn)單的靜態(tài)站點(diǎn)生成器。




接下來就教你使用 node.js >= 8.11.x 創(chuàng)建自己的靜態(tài)站點(diǎn)生成器。我們首先設(shè)置項(xiàng)目:

npm initnpm i --save-exact bluebird chokidar fs-extra mustachemkdir srcmkdir public


首先我們問一個(gè)問題——為什么需要靜態(tài)站點(diǎn)生成器?答案是實(shí)際上你并不是很需要它。如果你的博客流量很小,只需要手工編寫 HTML 頁面然后發(fā)布就行了。實(shí)際上,在服務(wù)器端編程興起之前,大部分 web 發(fā)布都是通過這種方式完成的。然而,如果你已經(jīng)有了一些頁面和內(nèi)容,修改所有頁面的共有部分(比如頁腳)會(huì)很麻煩。因此,如果我們能有某種簡(jiǎn)單的模板引擎,可以分離出共有內(nèi)容并將其插入需要的位置,那就再好不過了。




在講解模板引擎前,首先設(shè)置我們的網(wǎng)站。我們會(huì)在項(xiàng)目根 src(當(dāng)前網(wǎng)站所在的位置)和 public(包含我們生成的網(wǎng)站)下創(chuàng)建 2 個(gè)文件夾。將 src 的內(nèi)容復(fù)制到 public,在你的項(xiàng)目根下創(chuàng)建如下 index.js:




const Promise = require("bluebird");const fse = require("fs-extra");Promise.resolve().then(async () => { await main();});const main = async() => { await generateSite();};const generateSite = async() => { await copyAssets();};const copyAssets = async() => { await fse.emptyDir("public"); await fse.copy("src", "public");};


通過 node index.js 運(yùn)行該腳本,然后坐等勝利的喜悅就完事兒了。




圖:恭喜,你現(xiàn)在是一名后端開發(fā)了!



在第二步,我們添加一個(gè)文件監(jiān)視器,這樣 src 文件夾內(nèi)出現(xiàn)任何變動(dòng)都會(huì)重新生成該網(wǎng)站。由于這會(huì)是個(gè)總共有 500-1000 個(gè)文件的博客(假設(shè)有 100 個(gè)博客條目),我們可以在任何變化時(shí)重新生成整個(gè)網(wǎng)站:




const chokidar = require("chokidar");const main = async() => { await generateSite(); watchFiles();};const watchFiles = () => { const watcher = chokidar.watch( [ "src" ], { ignored: /(^|[////])/../, // chokidar will watch folders recursively ignoreInitial: false, persistent: true } ); watcher.on("change", async path => { console.log("changed " + path + ", recompiling"); await generateSite(); }); // catch ctrl+c event and exit normally process.on("SIGINT", function() { watcher.close(); });};


上面的代碼清楚的顯示了為何最初的版本中有個(gè)叫 generateSite 的函數(shù)。我們現(xiàn)在可以通過 node index.js 啟動(dòng)我們的靜態(tài)站點(diǎn)生成器,如果我們現(xiàn)在編輯 src 中的任何文件,產(chǎn)生的更改會(huì)在 public 中反映出來。在這里我們還會(huì)添加一個(gè)環(huán)境變量,用以區(qū)分開發(fā)和生產(chǎn)模式。在開發(fā)模式下,我們將監(jiān)視產(chǎn)生的更改并重新生成網(wǎng)站,而在生產(chǎn)模式下,我們只重新生成:




const env = process.env.NODE_ENV || "dev";const main = async () => { console.log("Running app in " + env); await generateSite(); if (env === "dev") { watchFiles(); }};


我們可以通過 export NODE_ENV=prod || set NODE_ENV=prod && node index.js 運(yùn)行以上代碼。注意監(jiān)視更改的源目錄,并不完全需要重新編譯,可以跳過這一步,只需在每次進(jìn)行更改時(shí)運(yùn)行腳本,但編程就是要避免做重復(fù)工作。




我們快要完成了!現(xiàn)在我們回到創(chuàng)建靜態(tài)站點(diǎn)生成器的初步階段:建模。我們會(huì)使用 Mustache.js 用于創(chuàng)建模板,因?yàn)樗亲詈?jiǎn)單的方法,我們的需求也不是很復(fù)雜。我們創(chuàng)建一個(gè)文件夾 src/patials,它會(huì)保存我們的共有部分。然后我們略微修改網(wǎng)站結(jié)構(gòu),這樣全部頁面現(xiàn)在就出現(xiàn)在 src/pages中了。剩下的工作就是加載所有的 partials 文件、加載頁面并用 Mustache 渲染它們:

const fs = require("fs");const generateSite = async () => { await copyAssets(); await buildContent();};const buildContent = async () => { const pages = await compilePages(); await writePages(pages);};const compilePages = async () => { const partials = await loadPartials(); const result = {}; const pagesDir = path.join("src", "pages"); const fileNames = await fs.readdirAsync(pagesDir); for (const fileName of fileNames) { const name = path.parse(fileName).name; const fileContent = await fs.readFileAsync(path.join(pagesDir, fileName)); result[name] = Mustache.render(fileContent.toString(), {}, partials); } return result;};const loadPartials = async () => { const result = {}; const partialsDir = path.join("src", "partials"); const fileNames = await fs.readdirAsync(partialsDir); for (const fileName of fileNames) { const name = path.parse(fileName).name; const content = await fs.readFileAsync(path.join(partialsDir, fileName)); result[name] = content.toString(); } return result;};const writePages = async pages => { for (const page of Object.keys(pages)) { await fs.writeFileAsync(path.join("public", page + ".html"), pages[page]); }};


最終的代碼版本參見 GitLab 上的 Software Dawg 項(xiàng)目(https://gitlab.com/wheresvic/software-dawg),和教程中的內(nèi)容有幾處細(xì)微的不同:

腳本本身在 src 文件夾中。

略微超過了 100 行,主要是由于干凈的代碼實(shí)踐,即常量而不是文件夾路徑的字符串等。

沒有復(fù)制整個(gè) src 文件夾,腳本只復(fù)制了必需的文件,比如 CSS,圖像等。

項(xiàng)目還用了 node-sass 來編譯模板 CSS,不過該環(huán)境依賴不是必需的。




另外,你還可以全局安裝 browser-sync 包,通過提供的命令行 npm run live-reload 運(yùn)行,這樣你的瀏覽器會(huì)自動(dòng)刷新所有頁面。注意,由于我們是根據(jù)更改重新生成整個(gè)站點(diǎn),所以在 Windows 上效果不太好。




通過本文的方法,幾乎完美地滿足了我同事的需求,不僅非常靈活,而且還能讓她根據(jù)自己的喜好進(jìn)行自定義。




參考資料:
https://smalldata.tech/blog/2018/08/16/building-a-simple-static-site-generator-using-node-js



關(guān)鍵詞:靜態(tài),成器,簡(jiǎn)單

74
73
25
news

版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。

為了最佳展示效果,本站不支持IE9及以下版本的瀏覽器,建議您使用谷歌Chrome瀏覽器。 點(diǎn)擊下載Chrome瀏覽器
關(guān)閉