Trình tạo trang web tĩnh bằng "Make" (2022)
(karl.berlin)- Trang web tĩnh dễ lưu trữ, nhanh và ít cần bảo trì, nên với một trang đơn giản thì chỉ cần vài dòng Makefile mà không phải học thêm trình dựng riêng
- Cấu trúc cốt lõi là giữ nguyên cách sắp xếp tệp trong
source, nối thêmheader.htmlvào các tệp HTML, còn các tệp khác thì sao chép nguyên trạng sangbuild make builddùngfind source -type fvàpatsubstđể ánh xạ tệp đầu vào/đầu ra, rồi áp dụng các quy tắc make khác nhau cho.htmlvà các tệp còn lại- Có thể thêm các target bổ sung khi cần cho việc làm nổi bật trang hiện tại, chuyển đổi Markdown, máy chủ cục bộ, tự động build lại khi có thay đổi và triển khai GitHub Pages
- Ít phụ thuộc và chỗ cần chỉnh sửa rất rõ ràng, nên với các trang có yêu cầu nhỏ thì luồng build tự làm có thể nhanh hơn trình tạo tổng quát
Trang web tĩnh cơ bản chỉ với Makefile
- Trình tạo trang web tĩnh cho ra kết quả dễ host, nhanh và gánh nặng bảo trì rất nhỏ
- Với một trang đơn giản, tự viết script có thể nhanh hơn và thỏa mãn hơn so với học rồi tùy biến một trình dựng trang có sẵn
- Nếu là một trang web thông thường không có timestamp tự cập nhật hay RSS feed, thì cấu hình còn có thể đơn giản hơn cả script dành cho blog
-
Yêu cầu cơ bản
- Mọi tệp đầu vào đều đặt trong thư mục
source, và đầu ra cũng giữ nguyên cấu trúc thư mục đó - Thêm
header.htmlvào đầu mọi tệp HTML - Các tệp không phải HTML được sao chép nguyên vẹn vào thư mục
build
- Mọi tệp đầu vào đều đặt trong thư mục
-
Quy tắc Makefile
- Target
buildcó các phụ thuộc ánh xạ mọi tệp bên dướisourcesang tệp đầu ra tương ứng bên dướibuild - Bản thân nó không làm việc gì, nhưng đóng vai trò điểm vào để các quy tắc phù hợp với từng tệp được áp dụng
- Quy tắc
build/%.htmlphụ thuộc vàosource/%.html,header.html, vàMakefile - Dùng
mkdir -p $(dir $@)để tạo thư mục đầu ra, rồicat header.html $< > $@để ghép phần header và HTML đầu vào thành đầu ra - Quy tắc
build/%sao chép các tệp không phải HTML bằngcp $< $@nguyên trạng - Chỉ với
header.htmlvà các quy tắc này, chạymake buildsẽ tạo ra thư mụcbuildcó thể mở cục bộ hoặc đưa lên máy chủ web
- Target
Ví dụ mở rộng và các target phụ trợ
-
Hiển thị trang hiện tại
- Nếu làm nổi bật trang hiện tại trong điều hướng, người truy cập có thể nhìn thấy ngay vị trí của mình trong trang web
- Trong ví dụ, liên kết điều hướng được tìm ra và thêm class
current sed -E 's|(href="$(subst source,,$<))|class="current" \\1|' header.html | cat - $< > $@- Cách thay thế thực tế cần thay đổi theo cấu trúc markup đang dùng
-
Tạo HTML từ Markdown
- Nếu không muốn viết HTML trực tiếp hoặc nội dung sẵn có ở dạng Markdown, có thể nối thêm một bộ chuyển đổi Markdown-to-HTML bằng pipe
- Ví dụ dùng smu
smu $< | cat header.html - > $@- Quy tắc cơ bản vẫn giả định
build/foo.htmlđược tạo từsource/foo.html - Với tệp Markdown, cần либо giữ hậu tố
.html, либо sửa quy tắc để tìm tệp.mdlàm đầu vào
-
Xem trước cục bộ
- Một số trang khó xem trước chính xác nếu mở trực tiếp tệp cục bộ trong trình duyệt, lý do phổ biến là dùng liên kết tuyệt đối thay vì liên kết tương đối
- Python đã được cài sẵn trên nhiều hệ thống và có kèm máy chủ web phù hợp cho mục đích này
- Target
serve:python -m http.server -d build
-
Tự động build lại khi có thay đổi
- Trong lúc làm trang, việc mỗi lần đều phải build lại thủ công là khá phiền
- Dùng
entrthì có thể tự động chạymake buildkhisource,header.html, hoặcMakefilethay đổi - Target
watch:find source header.html Makefile | entr make build - Nếu muốn tránh thêm phụ thuộc, cũng có thể dùng
inotifywait
-
Triển khai GitHub Pages
- Nếu kho mã nằm trên GitHub, thì host HTML kết quả bằng GitHub Pages là một lựa chọn tự nhiên
- Việc căn chỉnh lệnh triển khai để không phải bận tâm đến các chi tiết của git hơi rắc rối một chút, nhưng có thể xử lý bằng
git worktree - Cách làm dựa trên bài viết của Sangsoo Nam
- Thêm nhánh
gh-pageslàm worktreepublic_html - Sao chép
build/*vàopublic_html - Chạy
git add --all,git commit -m "Deploy to github pages", vàgit push origin gh-pages - Cuối cùng xóa worktree bằng
git worktree remove public_html
-
Ví dụ thực tế
- Có thể xem một trang được làm theo cách này tại karlb/astridbartel.de
- Một trình tạo trang web tĩnh hoàn chỉnh có thể bắt đầu chỉ với sáu dòng Makefile, rồi nhanh chóng điều chỉnh theo nhu cầu mà không có phụ thuộc lạ hay gánh nặng bảo trì
1 bình luận
Ý kiến trên Hacker News
Sau đó tôi thêm các tính năng như tin tức và RSS feed, danh sách tự động các bài nghiên cứu và tài liệu bài giảng, danh sách sách có thể lọc theo thẻ. Hiện giờ vẫn là Makefile, nhưng bản thân Makefile thậm chí đã đơn giản hơn một chút; thay vào đó nó gọi các script Bash dùng những tiện ích tuyệt vời xml2 và 2xml để xử lý HTML theo từng dòng. Chủ yếu tận dụng các tiện ích cốt lõi như
grepvàsedTôi cũng gắn thêm vài git hook để tự động gọi
makekhi cần, đặc biệt trên máy chủ từ xa nơi website được host, mỗi khi push vào repository thì phiên bản công khai sẽ được build lạiNó đã chạy rất tốt suốt nhiều năm, và lịch sử git thì quay ngược đến tận năm 2009. Nhìn các commit đầu tiên thì có
beccad7 (FIRST_VERSION) Initial commit,d1cc6d7 adding link to Google Reader shared items,6ccfd0c fix typo,d337959 adding link to Identi.ca account; đúng là đã 15 năm trôi quaxml2và2xmltrông hơi giống một dự án bị bỏ bêhttps://github.com/cryptorick/xml2
https://manpages.debian.org/unstable/xml2/2xml.1.en.html
source/thì nó không bị xóa trongbuild/Trong các dự án của tôi, build lại toàn bộ site vẫn đủ nhanh, nên tôi chọn cách xóa toàn bộ thư mục build trước khi build lại
https://github.com/jez/jez.github.io/blob/source/Makefile#L1...
Làm vậy vô hiệu hóa phần lớn incremental build, vốn là một lý do lớn để dùng build system, nhưng nếu biết trang nào muốn tạo lại thì vẫn có thể trực tiếp
makeriêng file đóTôi muốn biết liệu trong Makefile có cách workaround phổ biến nào cho mẫu này không
Dùng hàm
shellcủa GNU Make để liệt kê file, rồi dùng hàmfilter-outđể lọc bỏ các đầu ra “dự kiến”. Đây là một hack xấu xí, nhưng đảm bảo lệnh được chạy mỗi lần bằng cách khiến nó chạy như một phần của việc mở rộng biến thông qua hàmshellLink Makefile: https://github.com/jaredkrinke/make-blog/blob/main/Makefile
Ngoài lựa chọn hạt nhân như
make clean, có thể tạo các target Make riêng cho việc xóa và đổi tên. Ví dụ, đểmake rm sourcefilehoặcmake mv sourcefile newsourcefilexử lý đồng thời việc xóa/đổi tên cả nguồn lẫn các target đã sinh raThực tế, ngay cả với các blog hay dự án online khá lớn, chu trình
make clean/make allthường vẫn đủ nhanh, và hay cần khi sửa template hoặc các yếu tố thiết kế site. Nếu quy mô lớn đến mức phải lo thời gian rebuild, có lẽ nên dùng một CMS thực sự, quản lý nguồn trong database và sinh động khi client truy cậpmake cleanlà được mà?rm/%.html:@rm -f source/%.html build/%.htmlChạy bằng
$ make rm/page.htmlpruneđúng cho mục đích nàyhttp://neilmitchell.blogspot.com/2015/04/cleaning-stale-file...
Nhưng tôi nghĩ giải pháp tốt nhất vẫn dùng được với
makelà tạo targetmake distđể tạo archive.tar.gzcuối cùng của output. Nếu viết rule đúng, các file cũ sẽ không bị đưa vào. Với dự án lớn có thể chậm, nhưng trong lúc phát triển dù sao cũng không cần dùng, chỉ dùng khi release. Incremental build vẫn khả dụng, chỉ có file.tar.gzcuối cùng là được tạo lại từ đầublog.shcủa Karl được nhắc trong bài nàyTôi lấy nó về chỉnh sửa và tạo ra
barf, trình tạo site tĩnh tối giản của mình. Nó đã không tồn tại nếu Karl không công bố công việc tuyệt vời đó[0]: https://github.com/karlb/karl.berlin/blob/master/blog.sh
[1]: https://barf.bt.ht
shite, và tôi dùng nó để tạo site của mình. Tên gọi ám chỉ chất lượng phần mềm :)Điều tôi thích nhất là đến giờ chưa phải nâng cấp gì cả, và có vẻ sau này cũng sẽ không cần. Điều hay tiếp theo là có hot reload mà không cần JavaScript
[1] https://github.com/adityaathalye/shite
[2] https://evalapply.org
m4thì vẫn giữ được cùng cách tiếp cận chỉ có khung xương đó, đồng thời có thêm chút linh hoạtTôi từng duy trì một website nhỏ được làm theo kiểu đó khoảng 20 năm trước. Nhưng ngoài website cá nhân ra, tôi không chắc mô hình này ngày nay còn phù hợp hay không. Vì cách này về bản chất buộc mọi thứ quay về vai trò của Web 1.0. Mọi người dùng đóng góp đều phải thành thạo HTML, hoặc ai đó phải gánh lấy việc lặt vặt gọi là “webmaster”
[1] https://en.wikipedia.org/wiki/M4_(computer_language)
m4” đâuBạn bắt đầu một dự án sạch sẽ và tự hứa lần này sẽ không đụng đến
m4. Rồi để giảm mã lặp, bạn thêm một lời gọim4nhỏMột năm sau, bạn lần qua 5 lớp mở rộng macro để tìm lý do vì sao từ “cat” âm thầm biến mất khỏi website, và cuối cùng phát hiện một lập trình viên junior đã tự triển khai vòng lặp for thay vì chép từ manual, rồi làm hỏng dấu nháy
Sau khi giải quyết vấn đề trước mắt, bạn kết luận rằng debug DSL quá khó, nên lôi vào tệp macro M4 vẫn được chép từ dự án này sang dự án khác. Sau đó bạn mất cả ngày thay toàn bộ chỗ dùng
definebằng các macro tạo macro, để thêm chú thích vào output nhằm làm cho script tạo stack trace hoạt độngỞ dự án tiếp theo, bạn sẽ đặt ra luật tuyệt đối. Cấm
m4! Tất nhiên có thể ngoại lệ ở đúng một chỗ đóm4hayperl, tôi khuyên nên dùng SGML, nền tảng và siêu tập chung của HTML và XMLSGML cung cấp sẵn macro văn bản có kiểm tra kiểu đơn giản, tức mở rộng entity, và cũng có thể mở rộng macro có tham số hóa biết kiểu. Ở đây kiểu là kiểu nội dung tổng quát của phần tử markup, tức các phần tử con được phép và thứ tự của chúng, đồng thời cũng tính đến việc mở rộng hoặc escape theo các ngữ cảnh như thuộc tính, CDATA, RCDATA
Việc mở rộng và escape đúng cách các bình luận người dùng có khả năng độc hại, đồng thời áp dụng các quy tắc do người dùng đặt ra, chẳng hạn cho phép markup cấp inline nhưng cấm phần tử
script, là việc chỉ SGML mới có thể mở rộng đúng. Cũng có thể mở rộng Markdown hoặc cú pháp wiki thành HTML, đưa nội dung HTML bên ngoài/syndication vào, tạo RSS và outline cho điều hướng, v.v. Nó rất hợp với các tác vụ chuẩn bị site tĩnh khá phức tạp trên dòng lệnh[1]: https://sgmljs.net/docs/producing-html-tutorial/producing-ht...
[2]: https://sgmljs.net/docs/sgmlproc-manual.html
envsubstthay vì tìm-thay thế bằngm4haysedĐây là chương trình thay thế các tham chiếu biến kiểu Bash, ví dụ
$TITLE, bằng giá trị biến môi trườngexport CURRENT="..."cat page.html | envsubtm4” ư, làm ơn tuyệt đối đừngm4cũng chẳng hay ho gì ngay cả với tư cách một ngôn ngữ lập trình khó hiểuChỉ riêng việc nó từng chạy trong sendmail không đủ để biện minh cho bất cứ thứ gì
Mỗi khi đọc được một bài thú vị ở đây, tôi lại đăng ký feed đó. Dù là site Wordpress, Bear Blog, Micro.blog, blog Havenweb hay feed từ site tự làm, tôi đều thêm vào module
Really Social Sitescủa Hey HomepageCuối cùng tôi muốn công khai danh sách blog này, giống như Kagi đang làm với sáng kiến Small Web. Tuy nhiên, để tăng chất lượng thì curate là then chốt, và khi nghĩ đến curate, việc bắt đầu một kiểu tạp chí online nào đó trông có vẻ tự nhiên
Mọi người lấy đâu ra cảm giác “có quyền” theo nghĩa tích cực rằng có thể chia sẻ với người khác? Tôi tò mò vì sao họ giả định người khác sẽ quan tâm đến thứ mình đang làm. Đôi khi nó giống như một cuộc cạnh tranh. Kiểu như “để có like hoặc lượt xem trên blog, mình phải làm thứ ngầu nhất có thể”
Hợp tác dĩ nhiên là tốt, và chỉ khả thi khi mọi thứ được công khai. Nhưng tôi không rõ ranh giới giữa “làm vì cái này trông hay” và “cố chia sẻ với người khác để nhận phản hồi” nằm ở đâu
Anh ấy nói chỉ cần thay đổi một tệp test, bằng một lệnh duy nhất có thể tạo lại toàn bộ bài báo, bao gồm chạy test và tạo biểu đồ
https://pages.github.com/
Cái của tôi hơi over-engineered vì muốn hot reload không dùng JavaScript, và đó là một màn yak shaving thú vị
Ý tưởng cơ bản thì giống nhau. Dùng heredoc cho template, dùng compiler để biến văn bản thuần thành HTML, trường hợp của tôi là
pandoc. Tôi dùng CSV trung gian để tạo chỉ mục, và cũng có một kỹ thuậtsedhữu ích để trích front matter. Cổ điển mà hay[1] https://github.com/karlb/karl.berlin/blob/master/blog.sh
[2] https://github.com/adityaathalye/shite
[3] Cách tôi làm: https://github.com/adityaathalye/shite/blob/master/bin/templ...
Tuy nhiên có một vài hạn chế. Tôi sắp xếp bài viết theo namespace và đưa ngày vào URL, nhưng
makekhông xử lý trực tiếp việc đó tốt lắmmakelà khi có thay đổi nhỏ trong một chương trình lớn được build bằng trình biên dịch chậm, nó giúp build lại tăng dần nhanh hơn rất nhiềuMột tác vụ build lại toàn bộ mất 40 phút có thể hoàn tất trong khoảng 3 giây
Nếu một site tĩnh chỉ cần
catheader chung và vài trăm file HTML là có thể được tạo từ đầu trong chưa đến 1 giây, thì không có lợi ích gì khi dùngmakethay cho script. Chỉ làm phát sinh nguy cơ thực hiện một bản build không hoàn chỉnh do lỗi phụ thuộc.phonyDù vậy vẫn có thể có các phân tách như
make buildvàmake pushỞ các dự án nhỏ khác, tôi đã dùng một shell script rất nhỏ như một bundler tạm thời. Cách làm là chèn CSS và JS vào trong HTML, với mục tiêu là vẫn có thể phục vụ các file chưa build ở local