日,我的一位同事向我尋求建議,她打算為自己構建一個博客。于是,我對靜態網站生成器和博客引擎進行了一番研究,發現 Hugo 是一個很不錯的選擇。但是,我的同事還有一些特殊要求,比如,她想要一個自定義的博客網址和 CSS 主題。盡管這些 Hugo 都可以實現,但我并不打算花時間來學習它。我想自己創建一個簡單的靜態網站生成器,以便我的同事在她已經準備好的 HTML 中編寫博客文章。
這個靜態網站生成器的代碼大約 100 行,非常簡潔。它提供了詳細代碼和示例博客 。眾所周知,GitLab 提供靜態頁面的免費托管服務,還帶有 CI/CD 功能,它允許你在部署之前編譯頁面。
以下教程將帶你使用 Node.js 設置自己的靜態網站生成器,Node.js 的版本需要 “>=8.11.x”。
npm init npm i --save-exact bluebird chokidar fs-extra mustache mkdir src mkdir public
首先,設置項目:
開始之前,我們需要弄清楚一個問題:為什么需要靜態網站生成器?因為某些情況并不需要靜態網站生成器。假如你的博客訪問量很小,你只需簡單地手工創建 HTML 頁面并發布它們即可。實際上,在服務器編程興起之前,在很長時間內這就是大多數 Web 的發布方式。但是,一旦頁面和內容增加,對這些頁面中的通用部分(例如頁面底部)進行更改將會變得非常重復和乏味。因此,我們開始尋找一種更加理想的方法,嘗試使用某種簡單的模板引擎來分離常見內容,然后在特定的地方插入所需的內容。
開始研究模板引擎之前,先設置我們的網站。我們需要在項目根目錄下創建 2 個文件夾 :
我們的目標是將 src 目錄的內容復制到 public 目錄中。在項目根目錄下創建 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,即可啟動該腳本。
祝賀你!此刻,你已榮升為一名后端開發人員。
接下來,我們將添加文件監視器,src 文件夾中的內容一旦發生更改就將重新生成網站。該博客總共包含 500-1000 個文件,我們可以在任何變化發生時重新生成整個網站:
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(); }); };
上面的代碼清楚地說明了為什么初始版本有一個名為 generateSite 的函數。現在執行命令 node index.js 啟動我們的靜態網站生成器,如果在 src 目錄中編輯任何文件,public 都會發生變化。此時,我們還將添加一個環境變量來區分開發和生產模式。在開發模式中,我們將關注更改情況并重新生成網站,而在生產模式中,我們只需重新生成:
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 來運行以上代碼。請注意,觀察源目錄的更改和重新編譯并不是每次都必須的,你可以跳過此步驟,只需在每次進行更改時運行腳本即可。
至此,差不多完成了!現在來說說模板。我們將使用 Mustache.js 模板,它非常簡單易用,并且我們的需求并不復雜。創建一個文件夾 src/partials,用來存放網站的公共部分。然后稍微修改我們的網站結構,保證所有頁面都存放在 src/pages 目錄中。接下來加載頁面并使用 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]); } };
想要了解最終版本,請查看 Software Dawg 項目(https://gitlab.com/wheresvic/software-dawg)。它與本教程有一些細微差別:
此外,你還可以安裝 browser-sync 軟件包,然后通過命令 npm run live-reload 運行它,如此一來,只要有任何更改發生瀏覽器就會自動刷新。請注意,由于任何更改都將重新生成整個網站,因此并不適用于 Windows。
GitLab 提供靜態網站免費托管,只需一個 .gitlab-ci.yml 配置文件即可。真正令人難以置信之處在于,你可以自定義構建過程,這意味著在該例中,我們可以在部署之前生成網站!有關此功能的詳細信息,請參見https://about.gitlab.com/features/pages/。
本教程到此結束,我的同事對此非常滿意,該方案非常靈活,它允許她根據自己的喜好進行自定義,也希望對你有所助益!
原文:https://smalldata.tech/blog/2018/08/16/building-a-simple-static-site-generator-using-node-js
作者簡介:Victor Parmar,是一位全棧工程師,熱愛旅行,熱愛 DIY。
譯者:安翔,責編:屠敏
信息加速發展的互聯網時代,越來越多的科技公司為了專注核心競爭力業務以及降低軟件項目成本,開始將項目中的部分業務模塊分發給第三方外包公司來完成。而這樣是否就意味著大幅度地降低成本了?
事實告訴我們,并沒有。
本文作者作為一名外包商,以自身的經歷告訴我們本可以在3天之內完成了的一個報價僅為 1500 美元的靜態 HTML 頁面,是如何被大型企業硬是拖成了一個為期 7 周且需要耗費 18000 美元項目的。
作者 | Ibrahim Diallo
譯者 | 王艷妮,責編 | 屠敏
出品 | CSDN(ID:CSDNnews)
不久前,我作為承包商工作,經常從一個項目跳到另一個項目。有些是短期的,工作一周左右,可很快提交我的工作成果。也有的項目會持續幾個月,這期間我會攢一些錢用以休息一段時間。我更喜歡短期工作,因為這樣的工作使我可以在單位時間內收取更高的費用。這樣不僅我感覺是在為自己打工,而且我覺得我不需要太努力工作就能過上還算體面的生活了。我的最高費率仍然在合理的范圍之內,而且我總是提供高質量的服務。這就是我和一家大公司定下這個項目之前我的工作狀態。
這家公司聯系我的時候顯得很著急,經理告訴我他們現在就需要一個人來搞定這件事。需要一個不怎么需要公司培訓就能馬上上手,而且能交付最大性能的人。不管怎么說,這剛好是我的座右銘。這個項目正是我喜歡的工作類型。它內容簡短,很快就能做好,而且報酬很高。
在談判確定好合適的費率后,我收到了一封包含說明的電子郵件。他們給了我更多關于這個項目的背景。他們的開發人員在沒有事先告知的情況下就離開了,并且從未跟任何其他人匯報過項目的進展。
我們需要您毫不分心地完成此項目。在合同期限內,您將只與我們合作,并及時交付成果。我們會對給您造成的麻煩進行補償。
任務說明很簡單:閱讀這些需求然后估計完成這個項目需要多長時間。這是我職業生涯中遇到的一個那類比較容易的項目之一。這是一個HTML頁面,包含一些簡單的動畫和幾個嵌入的視頻。我花了一個晚上研究需求并在腦中模擬實施。這些年來,我已經學會了在能確定收到報酬之前不為客戶寫任何代碼。
我確定了這個項目充其量也就是一天的活兒。但為了保持謹慎,我上報了20個小時,總計1500美元。畢竟這只是一個HTML頁面而已,我也只能收取這么多費用。他們讓我到25英里外的衛星辦公室去。在為他們工作的那三天里我必須天天開車去那兒。
第二天,我到了衛星辦公室。在一個購物中心,然后通過一扇秘密的門進入了一個秘密的世界,一些工作人員在他們的小隔間里安靜地工作著。接待員給我看了一個我將用它來工作的全新MacBook Pro,我必須從零開始設置環境。我的確更偏向于使用公司的筆記本電腦,因為他們經常要求承包商安裝一些可疑的軟件。(我可不想裝到自己電腦上。)
我花了一天時間下載我的工具包,設置電子郵件、ssh密鑰和請求服務的授權。換句話說,我什么都沒做。這就是為什么我上報了20個小時,還沒開始寫代碼呢,光前期設置就耗費了8個小時。
第二天,我準備開始真正地干活了。有了MacBook Pro,我用它發了一封電子郵件給經理。我告訴他我已經準備好工作了,正在等待上述的資源。那天,我在我柔和燈光下的工位上待著,玩著手指,直到太陽落山。
我再次計算了一下。根據我的估計,我還只剩4個小時的時間來完成這項工作,這對單個HTML頁面來說也不是不可能。但不用說,第二天,我把這剩下的4個小時花在了吃公司贊助的午餐上,伙食很不錯,而且我與其他員工玩得很開心。
當預計的20小時到期時,我確保向經理發送了另一封電子郵件,讓他知道我確實人一直在公司,但我沒有收到我需要的資源。當然,那封電子郵件被無視了。
接下來的星期一,我猶豫地開過了這25英里。令我驚訝的是,經理已經來到衛星辦公室,并熱情地問候了我。他是個三十來歲,很隨和很不錯的人。我很不解,他并不像當初要雇我的那時候那么著急了。我們進行了友好的交談,沒有提到任何工作。后來,我們去吃午餐,他付了錢。這是美好的一天。完全沒工作。
好吧你可以說我很容易形成習慣,但如果你供我吃喝并每天呵護我,我會習慣這一切。這變成了一個例程。我來上班,花一些時間在網上閱讀以及看視頻。我每天發一封電子郵件,所以他們知道我確實去了公司。然后,我會去吃午飯并和碰見的有趣的人一起玩耍。在一天結束時,我站起來,伸個懶腰,打一個當之無愧的哈欠,然后開車回家。
我習慣了。事實上,我在期待這些。當我終于收到一封帶有指向我需要的資源的鏈接的電子郵件時,我反而有點失望。我重新開始腳踏實地,變回自己工作時的嚴肅臉。但是,在花了幾分鐘查看zip文件后,我才注意到它缺少了我需要的大部分內容。設計師給我發了一些Adobe Illustrator文件,我無法在MacBook上打開它。
我回復了電子郵件來解釋我的疑慮,而且一并問了一些其他問題以節省時間。那時,我當初上報的20個小時時間早都已經過了。我現在真的想要完成這項工作了。點擊發送后不久,我收到了一封電子郵件。只有一句:“轉發給Alex”,然后Alex得到了這封電子郵件的抄送。Alex回答說他轉發給了Steve。Steve回答說Michelle是設計師,她會了解得更多一些。 Michelle的自動回復稱她正在度假,所有詢問都應該直接告訴她的經理。她的經理回復說“誰是Ibrahim?(我的名字)”我的經理回復說他很抱歉還沒有向大家介紹我。
作為承包商,在人們注意到我在那里工作之前,我通常就已經完成我的工作并離開那家公司了。但這次,我收到了大量歡迎的電子郵件。這樣的郵件持續了一段時間,而我被迫回復那些友好地過了頭的郵件。有些人很想跟我本人見面。當我說我在加利福尼亞州,離得遠著呢,他們有點失望。以及羨慕,他們說他們羨慕加州美好的天氣。
他們很有禮貌地無視我的電子郵件。他們用抄送來轉移我的問題。他們把我問過的任何事情歸為垃圾郵件。我花了很多時間,像一位考古學家在深深的電子郵件之溝內挖掘,希望找到我問題的答案。你可以想象每當我想起我唯一的任務是構建一個靜態HTML頁面時,我感覺到的冒名頂替綜合癥(心虛,懷疑自己的回報不是理所應得的)的程度之深。原本虛報了的20個小時的項目變成了為期7周的冒險,期間我享受免費午餐,每天開車50英里,并翻看電子郵件。
當我最終完成項目時,我在GitHub上將它發送給了團隊。所有偉大的冒險都必須有個盡頭。但不久之后,我收到了邀請,整個團隊會用Google Hangout開視頻會議對我的代碼進行code review。我花了一個多月的時間來寫一個靜態HTML頁面,而現在整個團隊都要評價我的工作?那個什么,我要為自己說句話,這個頁面也包含一些JavaScript交互,是響應式的,還包括CSS動畫......好吧我真的覺得自己像個來冒名頂替的。
當然,視頻會議的時間又重新安排了幾次。當它終于發生時,我和我的工作已經不是會議的主題了。他們都坐在紐約某個地方的同一個房間里,像一個緊密團結的團體一樣聊了一會兒。事實上,他們所說的關于我做的項目的所有內容只有:
那天晚上回家的時候,我意識到自己正面臨另一個挑戰。我在這家公司工作了7個星期,而我的原始報價為1,500美元。這相當于每年11,100美元或每周214美元。或者直接說,每小時5.35美元。
這幾乎還不夠我付油錢的。所以,我給他們發了一張發票,我按照原來的每小時費率給他們報了7個星期。總額達18,000美元。我當然感到羞恥,但我還能怎么辦呢?
就像我預期的那樣,我沒有收到回復。如果所有大公司都有什么相同之處,那就是他們并不急于按時支付賬單。這么簡單的工作要價這么多,我覺得自己像一個騙子,但話又說回來了,我又不是來做慈善的。我每天開車50英里來做這項工作,如果工作沒有完成,那不是因為我不想。這是因為他們回復太緩慢了。
接下來的一周我得到了回復。這是一封來自經理的冷郵件,他把我每天的工作日分成不同的時間段。然后他把我工作的那部分時間高亮了,每天標記一個小時的午休時間。最后他用我們商定的小時費率做了一些計算。
顯然,我算錯了。我錯誤估算了總數。調整后,他們欠我的總金額是21,000美元。
請確認重新調整后的小時數,以便財務可以給您寫個支票。
我很快回復了確認。
原文:https://idiallo.com/blog/18000-dollars-static-web-page
作者簡介:Ibrahim Diallo,具有多年開發經驗的軟件工程師。
本文為 CSDN 翻譯,轉載請注明來源出處。
之前我介紹了在spring boot中使用thymeleaf模板,這次我會給大家介紹在spring boot中使用freemarker模板技術,同時利用freemarker生成靜態html頁面。生成靜態html頁面就能實現網站的靜態化進而提高網站的訪問速度以及提高SEO能力。
首先在pom.xml中添加依賴
添加依賴
<dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.23</version> </dependency>
application配置
在application.properties中添加freemarker的配置參數
##freemarker spring.freemarker.cache=false spring.freemarker.charset=UTF-8 spring.freemarker.check-template-location=true spring.freemarker.content-type=text/html spring.freemarker.enabled=true spring.freemarker.suffix=.ftl spring.freemarker.template-loader-path=classpath:/templates
Controller和ftl模板
下一步我們就建一個基礎Controller類和配套的ftl模板
Controller類
package com.hw.myp2c.common.controller; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateException; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import javax.annotation.Resource; import java.io.*; import java.net.URISyntaxException; import java.util.HashMap; import java.util.Map; @Controller @RequestMapping("") public class MainController { @GetMapping public String main(Model model){ String w="Welcome FreeMarker!"; Map root=new HashMap(); root.put("w",w); model.addAttribute("w","Welcome FreeMarker!"); return "test"; } }
可以看到很簡單,跟之前的thymelefa和jsp的沒有區別。
freemarker模板
<html> <head> <title>Welcome!</title> <link rel="stylesheet" href="/bootstrap.min.css"> <script src="/lib/jquery.min.js"></script> </head> <body> <h1>Hello ${w}!</h1> </body> </html>
這樣之后我們就能完成了基礎freemarker的使用,更多的使用參見freemarker官方網站,這里不做過多的描述。
這里我們已經完成了標準的freemarker集成,下面我們將介紹如何利用freemarker生成靜態html頁面,直接上代碼,作為演示我們還是在Controller中完成,在實際應用中我們可以按照自己的實際需要進行封裝。
package com.hw.myp2c.common.controller; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateException; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import javax.annotation.Resource; import java.io.*; import java.net.URISyntaxException; import java.util.HashMap; import java.util.Map; @Controller @RequestMapping("") public class MainController { @Resource Configuration cfg; @GetMapping public String main(Model model){ String w="Welcome FreeMarker!"; Map root=new HashMap(); root.put("w",w); freeMarkerContent(root); model.addAttribute("w","Welcome FreeMarker!"); return "test"; } private void freeMarkerContent(Map<String,Object> root){ try { Template temp=cfg.getTemplate("test.ftl"); //以classpath下面的static目錄作為靜態頁面的存儲目錄,同時命名生成的靜態html文件名稱 String path=this.getClass().getResource("/").toURI().getPath()+"static/test.html"; Writer file=new FileWriter(new File(path.substring(path.indexOf("/")))); temp.process(root, file); file.flush(); file.close(); } catch (IOException e) { e.printStackTrace(); } catch (TemplateException e) { e.printStackTrace(); } catch (URISyntaxException e) { e.printStackTrace(); } } }
利用freemarker生成靜態頁面我理解的流程是這樣的
1.利用Configuration讀取想生成靜態頁面的模板,這里是test.ftl
2.解析模板文件,并將模板中的${}!包含的參數替換成真實的數據
3.最終將讀取了真實數據的模板生成相應的html文件,并寫入指定目錄
這樣我們就完成了spring boot中使用freemarker模板,并且利用freemarker生成靜態html文件
*請認真填寫需求信息,我們會在24小時內與您取得聯系。