Tại sao `serve` lại hiển thị một trang web xấu xí trong khi `publishSite` lại hoàn hảo
Đăng ngày 22 April 2026
Bạn có biết khoảnh khắc mà trang web cục bộ của bạn trông chẳng ra sao, nhưng sau khi triển khai trực tuyến thì mọi thứ đều ổn không? Đó là điều đã xảy ra với plugin Gradle JBake của tôi. Tác vụ`serve`hiển thị những nút vuông, chữ tối trên nền đen, và CSS vắng mặt một cách kỳ lạ. Tuy nhiên`publishSite`tạo ra cùng một trang web, còn nó thì hoàn hảo. Thủ phạm không phải là CSS, cũng không phải trình duyệt, cũng không phải bộ nhớ đệm. Đó là một dòng mã Kotlin — và một thói quen xấu với các đối số dòng lệnh.
- cốc
-
Không có văn bản nguồn nào được cung cấp để dịch. Vui lòng gửi đoạn văn bản tiếng Pháp cần dịch.
Cảnh: bốn ảnh chụp màn hình, hai bản render
Đó là sau khi hệ thống bị sập. Tôi đã chụp bốn ảnh chụp màn hình để so sánh cách hiển thị cục bộ (./gradlew serve`trên`localhost:8820) với kết xuất trực tuyến (`publishSite`trên GitHub Pages). Hai ảnh chụp màn hình mỗi trang: phần trên và phần dưới.
Kết xuất cục bộ
Lên đầu trang— Các nút Project và Template lànhỏ, hình chữ nhật, với màu sắc tiêu chuẩn (xanh dương và xanh lá). Thẻ Start Writing có mộtnền đen trơn, không có khung.
Chân trang— Tiêu đề phụ "Bài viết và tài nguyên mới nhất" làgần như không đọc được, quá tối trên nền đen. Ngày tháng bên dưới các hình ảnh blog ("17 October 2013", v.v.) làvắng mặt.
Kết xuất trực tuyến
Đầu trang— Các nút giống nhau làlớn, hình bầu dục, xanh neon sặc sỡ, văn bản in hoa. Thẻ có mộtkhung trắng bo tròn với bóng đổ.
Chân trang— Phụ đề làrõ ràng dễ đọc. Các ngày làcó thể nhìn thấy. Các thẻ bài viết có nhữnggóc bo trònvà các tiêu đề làin đậm.
Phản xạ đầu tiên: chủ đề. Tôi sử dụng hệ thống chủ đề động (sáng, tối, tương phản cao) dựa trên`localStorage`. Có lẽ local store đã kích hoạt một chủ đề tương phản cao? Không. Tôi đã viếthttps://cheroliv.com/blog/2025/0098_preloading_css_variable_post.html[một bài viết đầy đủ về việc tải trước các chủ đề], tôi biết nó hoạt động thế nào. Và hơn nữa, ngay cả trong chế độ tương phản cao, cấu trúc CSS (nút hình bầu dục, bóng đổ) lẽ ra phải có. Nó đã không có.
Điều thứ hai nghĩ đến: bộ nhớ đệm trình duyệt. Không, tôi đã thử với các profile sạch. Diff vẫn còn.
Phản xạ thứ ba:`serve`không phục vụ cùng những tệp như`publishSite`.
Chẩn đoán: serve không trỏ đến đúng thư mục
Plugin Gradle của tôi`bakery`có hai nhiệm vụ chính:
-
bake: tạo trang web tĩnh trong`build/bake/` -
serve: khởi chạy một máy chủ web cục bộ -
publishSite: đẩy`build/bake/`đến GitHub Pages
Sự khác biệt chỉ có thể đến từ`serve`. Si publishSite`đã đăng nội dung chính xác, là vì`build/bake/`là tốt. Nếu`serve`hiển thị thứ khác, đó là vì nó không được sử dụng`build/bake/.
Hãy xem mã. Trong`SiteManager.kt`, nhiệm vụ`serve`được định nghĩa như sau:
tasks.register("serve", JavaExec::class.java) { task ->
task.apply {
mainClass.set("org.jbake.launcher.Main")
classpath = jbakeRuntime
environment("GEM_PATH", jbakeRuntime.asPath)
jvmArgs(/* ... */)
args = listOf(
"-b", file(site.bake.srcPath).absolutePath,
"-s", layout.buildDirectory.get()
.asFile.resolve(site.bake.destDirPath)
.absolutePath
)
}
}
org.jbake.launcher.Main`là điểm vào của CLI JBake 2.7.0. Ý tưởng: truyền-b`cho thư mục nguồn và`-s`cho thư mục đích, sau đó khởi chạy chế độ máy chủ. Ngoại trừ việc…
Nguyên nhân gốc: JBake CLI không hoạt động như vậy
JBake 2.7.0 mong đợi nhữngđối số vị trícho nguồn và đích, sau đó là các cờ tùy chọn. Chữ ký là:
jbake <source> <destination> [options]
Tuy nhiên, trong code của tôi, tôi đã viết:
args = listOf("-b", "/path/to/site", "-s", "/path/to/build/bake")
JBake đã hiểu đó là:
-
-b→ phân tích cờ "bake" (boolean), đã tiêu thụ -
/path/to/site→ trở thành đối số vị trí 1 (nguồn) -
-s→ phân tích cờ "serve" (boolean), máy chủ đã khởi chạyngay lập tức -
/path/to/build/bake→ trở thành một đối số mồ côi, bị bỏ qua hoặc bị hiểu sai
Kết quả: JBake mất`/path/to/site`như nguồn,không tính đến điểm đếnđược cung cấp (build/bake), và sử dụng thư mục mặc định của nó (thường`./output`hoặc một bản sao tạm thời). Tệp`css/styles.css`của bản build do đó đã bị ghi đè hoặc bị bỏ qua, và đó là một CSS mặc định cũ (không có biến tùy chỉnh, không có bo góc, không có bóng đổ) được trả về.
Để so sánh,bake et publishSite`sử dụngplugin Gradle JBake chính thức(`jbake-gradle-plugin) người viết chữ đẹp trong`build/bake/`qua API Gradle. Chúng không đi qua CLI.
Giải pháp: đối số vị trí, không phải cờ
Việc sửa lỗi là đơn giản — một khi đã biết. Chỉ cần truyền source và destination làm đối số vị trí, rồi`-s`cuối cùng:
args = listOf(
file(site.bake.srcPath).absolutePath,
layout.buildDirectory.get()
.asFile.resolve(site.bake.destDirPath)
.absolutePath,
"-s"
)
Chỉ vậy thôi. Ba dòng đã thay đổi, lỗi đã được sửa.
Sau khi xuất bản cục bộ plugin (publishToMavenLocal), un `./gradlew serve`khởi chạy JBake với cú pháp chính xác:
jbake /home/user/project/site /home/user/project/build/bake -s
Và lần này, máy chủ Jetty tích hợp trong JBake thực sự phục vụ nội dung của`build/bake/`— giống hệt với những gì được đăng trực tuyến.
@startuml
!define RECTANGLE class
package "AVANT (lỗi)" {
RECTANGLE JBakeCLI1 {
+ args = ["-b", "site", "-s", "build/bake"]
}
RECTANGLE Source1 {
+ site/
}
RECTANGLE Dest1 {
+ ??? (dossier temporaire)
}
RECTANGLE Jetty1 {
+ sert mauvais fichiers
}
JBakeCLI1 --> Source1 : giường
JBakeCLI1 -[#red]x Dest1 : destination ignorée
Jetty1 --> Dest1 : phục vụ
}
package "APRÈS (sửa)" {
RECTANGLE JBakeCLI2 {
+ args = ["site", "build/bake", "-s"]
}
RECTANGLE Source2 {
+ site/
}
RECTANGLE Dest2 {
+ build/bake/
}
RECTANGLE Jetty2 {
+ sert les bons fichiers
}
JBakeCLI2 --> Source2 : giường
JBakeCLI2 --> Dest2 : được viết trong
Jetty2 --> Dest2 : phục vụ OK
}
@enduml
Kiểm tra nhanh với curl
Để đảm bảo rằng máy chủ phục vụ đúng nội dung, một … đơn giản`curl`xác nhận rằng`index.html`chứa`data-bs-theme="light"`và rằng`build/bake/css/styles.css`có kích thước đúng 31 402 byte — giống hệt tệp được tạo bởi`bake`.
$ ./gradlew serve
# Dans un autre terminal :
$ curl -s http://localhost:8820/ | grep data-bs-theme
<html ... data-bs-theme="light" ...>
$ curl -I http://localhost:8820/css/styles.css
HTTP/1.1 200 OK
Content-Length: 31402
Content-Type: text/css
Kết xuất cục bộ giờ đây làgiống hệt từng pixelđến bản kết xuất đã triển khai.
Tại sao lỗi này lại hiểm hóc
Bạn có thấy tại sao khó bắt được nó không?
-
Không có lỗi rõ ràng: JBake không bị crash. Nó "hoạt động", chỉ đơn giản là với một thư mục sai.
-
Bản build đã hoạt động :
./gradlew bake`đã tạo các tệp một cách chính xác trong`build/bake/. -
Việc triển khai đã hoạt độngNo text was provided to translate. Please send the French text you’d like translated into Vietnamese.`publishSite`đang đẩy nội dung phù hợp lên mạng.
-
Chỉ có
servebị hỏng: nhiệm vụ phát triển, nhiệm vụ mà người ta sử dụng liên tục để lặp lại. -
Thói quen của
-key value: với tư cách là lập trình viên, chúng ta bị định hình bởi nhiều năm sử dụng CLI Unix (-o output`,-i input). JBake CLI là một ngoại lệ — nguồn và đích là các đối số vị trí.
Kết luận
Nếu bạn duy trì một plugin Gradle bao bọc JBake (hoặc bất kỳ công cụ CLI nào),hãy đọc tài liệu về các đối sốngay cả khi bạn nghĩ rằng mình biết chúng. Một giả định ngầm (-s= "set destination") có thể khiến bạn tốn hàng giờ để gỡ lỗi trực quan.
Ở đây, cách khắc phục thực chất chỉ là thay đổi:
args = listOf("-b", src, "-s", dest) // ❌ BUG
en :
args = listOf(src, dest, "-s") // ✅ FIX
Ba token được di chuyển, và trang web cục bộ của tôi lại đẹp như trang web trên môi trường production.
Bài học: khi kết xuất khác nhau giữa local và prod mà không có lý do rõ ràng, hãy nghi ngờ pipeline trước tiên — không phải CSS, không phải trình duyệt, và càng không phải framework. Thường chính bước ngay trước khi kết xuất mới là thứ gian lận.