[{"data":1,"prerenderedAt":230},["ShallowReactive",2],{"blog:page:\u002Fvi\u002Fblog\u002Fstatic-deployment":3,"blog:articles:vi":171,"blog:translations:\u002Fvi\u002Fblog\u002Fstatic-deployment":227},{"id":4,"title":5,"author":6,"body":7,"cover":156,"coverAlt":157,"date":158,"description":159,"extension":160,"featured":161,"meta":162,"navigation":163,"path":164,"seo":165,"stem":166,"tags":167,"__hash__":170},"blog\u002Fvi\u002Fblog\u002F4.static-deployment\u002Findex.md","Deploy một blog như static assets","John Martin",{"type":8,"value":9,"toc":151},"minimark",[10,14,18,23,77,80,114,118,125,129,132,135,147],[11,12,5],"h1",{"id":13},"deploy-một-blog-như-static-assets",[15,16,17],"p",{},"Deployment của blog này không chứa code nào. Cấu hình Worker khai báo một\nthư mục file, một trang 404, và không gì khác.",[19,20,22],"h2",{"id":21},"config-nói-gì","Config nói gì",[24,25,30],"pre",{"className":26,"code":27,"language":28,"meta":29,"style":29},"language-jsonc shiki shiki-themes github-light github-dark","{\n  \"assets\": {\n    \"directory\": \".output\u002Fpublic\",\n    \"not_found_handling\": \"404-page\",\n    \"html_handling\": \"drop-trailing-slash\",\n  },\n}\n","jsonc","",[31,32,33,41,47,53,59,65,71],"code",{"__ignoreMap":29},[34,35,38],"span",{"class":36,"line":37},"line",1,[34,39,40],{},"{\n",[34,42,44],{"class":36,"line":43},2,[34,45,46],{},"  \"assets\": {\n",[34,48,50],{"class":36,"line":49},3,[34,51,52],{},"    \"directory\": \".output\u002Fpublic\",\n",[34,54,56],{"class":36,"line":55},4,[34,57,58],{},"    \"not_found_handling\": \"404-page\",\n",[34,60,62],{"class":36,"line":61},5,[34,63,64],{},"    \"html_handling\": \"drop-trailing-slash\",\n",[34,66,68],{"class":36,"line":67},6,[34,69,70],{},"  },\n",[34,72,74],{"class":36,"line":73},7,[34,75,76],{},"}\n",[15,78,79],{},"Ba quyết định đáng giải thích:",[81,82,83,94,102],"ul",{},[84,85,86,93],"li",{},[87,88,89,90],"strong",{},"Không ",[31,91,92],{},"main"," - không có Worker script thì không có gì thực thi theo từng\nrequest. Nền tảng phục vụ file trực tiếp, vừa là chế độ phục vụ rẻ nhất vừa\nlà chế độ đáng tin nhất.",[84,95,96,101],{},[87,97,98],{},[31,99,100],{},"404-page"," - một URL lạ nhận 404 status thật kèm error page thật. Fallback\nkiểu SPA trả 200 cho mọi thứ, âm thầm biến typo thành \"trang\" có thể index.",[84,103,104,109,110,113],{},[87,105,106],{},[31,107,108],{},"drop-trailing-slash"," - URL canonical ở đây không có trailing slash.\nChế độ mặc định ",[31,111,112],{},"auto-trailing-slash"," sẽ redirect địa chỉ canonical sang một\nđịa chỉ không canonical - vĩnh viễn.",[19,115,117],{"id":116},"_404-là-một-phần-của-sản-phẩm","404 là một phần của sản phẩm",[15,119,120,121,124],{},"Một blog mà trả 200 mềm cho mọi slug lạ sẽ đào tạo reader mất niềm tin vào link\ncủa nó. Phục vụ 404 thật cho ",[31,122,123],{},"\u002Fvi\u002Fblog\u002Fnot-an-article"," là deployment đang làm\nđúng việc của nó.",[19,126,128],{"id":127},"có-identity-mà-không-cần-script","Có identity mà không cần script",[15,130,131],{},"Deployment vẫn mang một Worker identity cho nền tảng - preview và định tuyến\nmôi trường khoá vào đó - nhưng identity không phải một file thực thi. Nó là\nmột cái tên cho một thư mục file, và đó chính xác là những gì bề mặt này là.",[133,134],"hr",{},[15,136,137],{},[138,139,140,141,146],"em",{},"Đường đọc trước, deployment sau: ",[142,143,145],"a",{"href":144},"\u002Fen\u002Fblog\u002Fbuild-time-content","content được resolve ở build\ntime"," giải thích vì sao không có runtime database\nnào để bind.",[148,149,150],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":29,"searchDepth":43,"depth":43,"links":152},[153,154,155],{"id":21,"depth":43,"text":22},{"id":116,"depth":43,"text":117},{"id":127,"depth":43,"text":128},"\u002Fassets\u002Fcover-static-deployment.svg","Minh họa bìa: thư mục các tệp HTML và SVG chảy ra mạng lưới edge CDN","2026-09-23","Không Worker script, không database binding - toàn bộ deployment là một thư mục file và một trang 404.","md",false,{},true,"\u002Fvi\u002Fblog\u002Fstatic-deployment",{"title":5,"description":159},"vi\u002Fblog\u002F4.static-deployment\u002Findex",[168,169],"deployment","cloudflare","B9wS6pU2NHSTlvHn5jHi1cRJCY4BO1GIU4iiOtRZyoc",[172,180,192,195,207,216],{"path":173,"stem":174,"title":175,"description":176,"date":177,"author":6,"tags":178,"featured":161},"\u002Fvi\u002Fblog\u002Fdemo-content-note","vi\u002Fblog\u002F6.demo-content-note\u002Findex","Blog này là gì (và không phải gì)","Đây là một vertical slice demo - layout thật, pipeline thật, content editorial một cách có chủ ý.","2026-10-07",[179],"meta",{"path":181,"stem":182,"title":183,"description":184,"date":185,"author":6,"tags":186,"featured":161,"cover":189},"\u002Fvi\u002Fblog\u002Freading-first-design","vi\u002Fblog\u002F5.reading-first-design\u002Findex","Trang đọc bài không phải trang tài liệu","Vì sao layout article của blog cố tình khác layout docs - phân cấp thông tin, bề rộng cột, và sự tiết chế.","2026-09-30",[187,188],"design","typography",{"src":190,"alt":191},"\u002Fassets\u002Fcover-reading-first.svg","Minh họa bìa: bố cục một cột hẹp được chọn thay vì bố cục nhiều cột rối rắm",{"path":164,"stem":166,"title":5,"description":159,"date":158,"author":6,"tags":193,"featured":161,"cover":194},[168,169],{"src":156,"alt":157},{"path":196,"stem":197,"title":198,"description":199,"date":200,"author":6,"tags":201,"featured":161,"cover":204},"\u002Fvi\u002Fblog\u002Flocale-switching","vi\u002Fblog\u002F3.locale-switching\u002Findex","Locale switching không bao giờ nói dối","Language switcher chỉ được offer thứ tồn tại. Đây là cách danh sách link được suy ra từ content thật.","2026-09-16",[202,203],"i18n","ux",{"src":205,"alt":206},"\u002Fassets\u002Ftwo-locales-one-page.svg","Minh họa bìa: hai đường dẫn ngôn ngữ hội tụ về cùng một bố cục trang",{"path":208,"stem":209,"title":210,"description":211,"date":212,"author":6,"tags":213,"featured":161},"\u002Fvi\u002Fblog\u002Fbuild-time-content","vi\u002Fblog\u002F2.build-time-content\u002Findex","Content được resolve ở build time","Vì sao blog này ship dưới dạng file prerender thay vì một content API - và điều đó mua được gì.","2026-09-09",[214,215],"architecture","static-sites",{"path":217,"stem":218,"title":219,"description":220,"date":221,"author":6,"tags":222,"featured":163,"cover":224},"\u002Fvi\u002Fblog\u002Furl-model","vi\u002Fblog\u002F1.url-model\u002Findex","Một mô hình URL cho mọi trang public","\u002Flocale\u002Fmount\u002Fpath giữ cho mọi trang public đoán được - và vì sao ta ràng buộc nó thay vì hy vọng nó được tuân thủ.","2026-09-02",[214,223],"urls",{"src":225,"alt":226},"\u002Fassets\u002Fcover-url-model.svg","Minh họa bìa: một thanh địa chỉ tách thành ba segment locale, mount và path",[228,229],"en","vi",1791809694186]