網(wǎng)站實(shí)戰(zhàn):從拖拽上傳到自動(dòng)構(gòu)建)
前陣子受夠了“寫個(gè)HTML想發(fā)給別人看結(jié)果發(fā)了zip包對(duì)方根本不知道怎么打開”的尷尬我把一個(gè)叫spade2的純靜態(tài)項(xiàng)目丟到了Netlify上整個(gè)過(guò)程從拖拽文件夾到拿到公網(wǎng) HTTPS 鏈接前后不到十分鐘。這篇文章就把這次的部署過(guò)程、踩過(guò)的坑、以及上線之后才察覺(jué)的幾個(gè)隱藏細(xì)節(jié)全部記錄下來(lái)。如果你手里也有一堆寫好的 HTML/CSS/JS 頁(yè)面不想買服務(wù)器、不想配 Nginx、更不想學(xué) Docker那么 Netlify 這套部署方案可能是你最快把東西“變成網(wǎng)站”的路徑。文章會(huì)從部署前的目錄準(zhǔn)備開始講再到兩種上傳方式的選擇、Netlify 后臺(tái)那些容易被忽略的配置項(xiàng)、最后是上線后我實(shí)測(cè)遇到的重定向和緩存問(wèn)題盡量讓你看完之后能一次跑通少走彎路。1. 為什么是 NetlifyHTML 靜態(tài)站部署的定位與優(yōu)勢(shì)1.1 靜態(tài)站部署到底在解決什么問(wèn)題很多剛接觸前端的人會(huì)以為“部署”就是把文件傳到某個(gè)服務(wù)器上其實(shí)這里面藏著幾個(gè)容易被忽略的環(huán)節(jié)。一個(gè) HTML 文件想要被別人通過(guò)瀏覽器訪問(wèn)至少需要解決三件事文件得放在一臺(tái) 7x24 小時(shí)在線的機(jī)器上這臺(tái)機(jī)器得能通過(guò)公網(wǎng) IP 或域名被找到訪問(wèn)的時(shí)候還得走 HTTPS 協(xié)議不然瀏覽器會(huì)直接攔截。傳統(tǒng)的做法是買一臺(tái)云服務(wù)器裝 Nginx配置站點(diǎn)目錄再折騰 SSL 證書。這一套流程對(duì)只寫了幾個(gè)靜態(tài)頁(yè)面的開發(fā)者來(lái)說(shuō)成本和復(fù)雜度都高得不成比例。而 Netlify 這類靜態(tài)托管平臺(tái)做的事情本質(zhì)上就是把“服務(wù)器 Web 服務(wù)軟件 HTTPS 證書 CDN 加速”全部打包成一條流水線你只需要把本地文件夾交給它剩下的它自己搞定。1.2 和傳統(tǒng)部署方式相比Netlify 的核心體驗(yàn)差異拿 spade2 這個(gè)項(xiàng)目來(lái)說(shuō)它就是我本地寫的一個(gè)純前端項(xiàng)目沒(méi)有任何后端接口所有數(shù)據(jù)都是寫死在 JS 里的。這種項(xiàng)目如果用 Nginx 部署我得先去買域名、解析 DNS、配置 Nginx 站點(diǎn)、申請(qǐng)證書、續(xù)期證書中間任何一步出問(wèn)題都要折騰半天。Netlify 的處理方式完全不同。它支持兩種接入方式一種是直接在網(wǎng)頁(yè)上把項(xiàng)目文件夾拖進(jìn)上傳區(qū)叫 Netlify Drop另一種是把項(xiàng)目關(guān)聯(lián)到 Git 倉(cāng)庫(kù)每次 push 代碼它自動(dòng)觸發(fā)部署。兩種方式我都試過(guò)個(gè)人感受是對(duì)比維度Netlify Drop拖拽上傳關(guān)聯(lián) Git 倉(cāng)庫(kù)自動(dòng)部署上手門檻極低適合一次性發(fā)布需要基礎(chǔ)的 Git 操作更新流程每次都要手動(dòng)重新拖拽push 代碼后自動(dòng)觸發(fā)回滾能力弱只能重新上傳可以一鍵回滾到任意歷史版本適用場(chǎng)景臨時(shí)演示、快速分享正式項(xiàng)目、需要持續(xù)迭代spade2 我最終選擇了關(guān)聯(lián) Git 倉(cāng)庫(kù)的方式因?yàn)楹罄m(xù)還要持續(xù)改動(dòng)自動(dòng)部署省掉了每次手動(dòng)上傳的重復(fù)勞動(dòng)。1.3 標(biāo)題里的“Spade2”到底是什么項(xiàng)目形態(tài)這里多說(shuō)一句 spade2 這個(gè)項(xiàng)目本身。它本質(zhì)上是一個(gè)無(wú)需構(gòu)建工具的靜態(tài)原型目錄里就是 index.html、styles.css、app.js 這幾個(gè)文件外加一個(gè) assets 文件夾放圖片和字體。之所以選它做部署實(shí)驗(yàn)是因?yàn)樗慕Y(jié)構(gòu)足夠簡(jiǎn)單——沒(méi)有框架、沒(méi)有打包步驟、沒(méi)有環(huán)境變量正好可以把 Netlify 部署的底層邏輯看得清清楚楚。如果你是第一次接觸部署我強(qiáng)烈建議先找一個(gè)這樣的小項(xiàng)目練手而不是一上來(lái)就部署一個(gè) React 工程。因?yàn)楫?dāng)部署失敗的時(shí)候簡(jiǎn)單項(xiàng)目能讓你把問(wèn)題定位在“部署流程”而不是“構(gòu)建過(guò)程”排查起來(lái)清晰得多。2. 部署前的目錄準(zhǔn)備少走彎路的項(xiàng)目文件規(guī)范2.1 本地項(xiàng)目的目錄結(jié)構(gòu)應(yīng)該長(zhǎng)什么樣在拖拽上傳之前有一件事比部署本身更影響成敗項(xiàng)目目錄是否干凈。我見過(guò)很多本地項(xiàng)目跑得好好的一旦部署上線就樣式全丟、圖片全裂根因就是目錄結(jié)構(gòu)不規(guī)范——文件沒(méi)有放到根目錄、路徑寫的是絕對(duì)路徑指向了本地磁盤、甚至混進(jìn)來(lái)一堆 node_modules 垃圾文件。spade2 在提交部署前我特意把目錄整理成下面這個(gè)樣子spade2/ ├── index.html ├── styles/ │ └── main.css ├── scripts/ │ └── app.js └── assets/ ├── images/ └── fonts/部署時(shí)Netlify 的站點(diǎn)根目錄Publish Directory指的就是這個(gè)spade2文件夾本身。所有 HTML 文件必須直接放在這個(gè)根目錄下或者在子目錄里但通過(guò)相對(duì)路徑正確引用。尤其注意Netlify 的默認(rèn)首頁(yè)文件名必須是index.html如果你把首頁(yè)命名為home.html訪問(wèn)域名時(shí)不會(huì)自動(dòng)打開你的頁(yè)面會(huì)直接返回 404。2.2 路徑寫法相對(duì)路徑還是絕對(duì)路徑這可能是靜態(tài)站部署中最容易翻車的一個(gè)點(diǎn)。本地打開 HTML 文件時(shí)瀏覽器用的是file://協(xié)議這時(shí)候?qū)懡^對(duì)路徑/assets/images/logo.png也能打開因?yàn)槲募驮谀愕拇疟P上。但部署到 Netlify 之后整個(gè)站點(diǎn)的根目錄變成了域名根路徑如果你的資源路徑是從根斜杠開始寫的只要部署目錄層級(jí)不一致就會(huì)全部指向錯(cuò)誤的位置。我的建議是在本地寫代碼時(shí)就用相對(duì)路徑。比如 HTML 在根目錄圖片在assets/images/下引用就寫assets/images/logo.png不要加開頭的斜杠。如果你有子頁(yè)面在pages/about.html里想引用根目錄的圖片就寫../assets/images/logo.png。提示路徑問(wèn)題在本地不一定能暴露因?yàn)楸镜匚募到y(tǒng)對(duì)路徑的容錯(cuò)度比服務(wù)器高。部署前最好在瀏覽器開發(fā)者工具的 Console 面板里看一下有沒(méi)有資源加載失敗的報(bào)錯(cuò)Network 面板里標(biāo)紅的就是需要修的。2.3 忽略文件與臨時(shí)文件的清理另一個(gè)隱蔽的問(wèn)題來(lái)自那些“本地必需、線上沒(méi)用”的文件。比如你用 VSCode 打開項(xiàng)目后生成的.vscode/配置目錄、macOS 的.DS_Store文件、某些編輯器的自動(dòng)備份文件。這些文件雖然不影響部署成功率但會(huì)讓你的站點(diǎn)倉(cāng)庫(kù)變得很臃腫而且部分文件內(nèi)容可能包含本地路徑信息也就是所謂的“信息泄露隱患”。如果你是關(guān)聯(lián) Git 部署務(wù)必在項(xiàng)目根目錄創(chuàng)建.gitignore文件把下述內(nèi)容寫進(jìn)去.DS_Store node_modules/ dist/ .vscode/ *.log如果你是拖拽上傳那就手動(dòng)確保拖進(jìn)去的文件夾里只有需要上線的文件。簡(jiǎn)單粗暴但很有效。2.4 本地預(yù)覽的正確姿勢(shì)在部署之前最好用本地靜態(tài)服務(wù)器預(yù)覽一下項(xiàng)目而不是直接雙擊 HTML 文件。因?yàn)橹苯佑胒ile://協(xié)議打開時(shí)瀏覽器的安全策略會(huì)限制某些能力比如 fetch 請(qǐng)求本地 JSON 文件、某些瀏覽器下的模塊加載都會(huì)報(bào)跨域錯(cuò)誤。這些錯(cuò)誤在你部署到 Netlify 之后因?yàn)樽叩氖?HTTP 協(xié)議反而不會(huì)出現(xiàn)但它們會(huì)干擾你對(duì)項(xiàng)目現(xiàn)狀的判斷。如果你是 VSCode 用戶裝一個(gè) Live Server 插件就能解決?;蛘哂?Python 一行命令python3 -m http.server 8080然后在瀏覽器訪問(wèn)localhost:8080看到的頁(yè)面狀態(tài)和部署后基本一致。這一步做踏實(shí)了后面部署成功率會(huì)高非常多。3. 兩條部署路徑實(shí)測(cè)Netlify Drop 與 Git 倉(cāng)庫(kù)連接3.1 Netlify Drop 拖拽部署最快三分鐘上線Netlify Drop 是整個(gè)平臺(tái)里最“反直覺(jué)”的功能——不需要注冊(cè)賬號(hào)甚至可以直接把文件夾拖進(jìn)網(wǎng)頁(yè)Netlify 會(huì)臨時(shí)給你一個(gè)鏈接你可以用游客身份訪問(wèn)也可以順手注冊(cè)賬號(hào)把這個(gè)站點(diǎn)接管到自己的名下。實(shí)際操作步驟非常簡(jiǎn)單打開 Netlify 官網(wǎng)找到 Drop 區(qū)域。把整個(gè)spade2文件夾直接拖進(jìn)去。等待幾秒Netlify 自動(dòng)完成上傳、構(gòu)建、部署然后給你生成一個(gè)隨機(jī)子域名格式一般是隨機(jī)名稱.netlify.app。點(diǎn)擊鏈接查看線上效果。我第一次用 Drop 部署的時(shí)候整個(gè)過(guò)程快到?jīng)]反應(yīng)過(guò)來(lái)。唯一的不足是這種方式部署出來(lái)的站點(diǎn)和你本地文件是一份靜態(tài)快照后續(xù)你改了本地代碼線上不會(huì)自動(dòng)更新要重新拖一次才行。3.2 關(guān)聯(lián) Git 倉(cāng)庫(kù)從 push 到線上自動(dòng)更新對(duì) spade2 這個(gè)需要持續(xù)改動(dòng)的小項(xiàng)目我更推薦走 Git 接入這條路。流程是在代碼托管平臺(tái)創(chuàng)建倉(cāng)庫(kù)把本地項(xiàng)目 push 上去。進(jìn)入 Netlify 后臺(tái)選擇 “Add new site”然后選擇 “Import an existing project”。授權(quán) Netlify 訪問(wèn)你的代碼托管平臺(tái)然后選中 spade2 對(duì)應(yīng)的倉(cāng)庫(kù)。配置構(gòu)建選項(xiàng)。由于 spade2 是純靜態(tài)項(xiàng)目不需要執(zhí)行任何構(gòu)建命令構(gòu)建命令留空即可。發(fā)布目錄Publish Directory填.或直接保持默認(rèn)表示倉(cāng)庫(kù)根目錄就是站點(diǎn)根目錄。點(diǎn)擊 “Deploy site”等待系統(tǒng)自動(dòng)完成部署。部署完成后你每次把代碼 push 到倉(cāng)庫(kù)的指定分支默認(rèn)是 main 分支Netlify 都會(huì)自動(dòng)拉取代碼、執(zhí)行構(gòu)建如果配置了的話、發(fā)布上線。整個(gè)鏈路是自動(dòng)化的你唯一要做的就是寫好代碼并 push。3.3 站點(diǎn)管理與域名的那些事無(wú)論走哪條路徑部署成功之后進(jìn)到站點(diǎn)后臺(tái)你會(huì)看到三個(gè)核心信息站點(diǎn)名稱Site name、域名列表Domain management以及部署記錄Deploy log。站點(diǎn)名稱可以直接改改完訪問(wèn)域名會(huì)跟著變。比如 spade2 我改成了一個(gè)可讀性比較高的名字域名就變成你喜歡的名字.netlify.app。如果你自己有域名可以在 Domain management 里添加自定義域名。Netlify 會(huì)提示你到域名注冊(cè)商那邊加一條 DNS 記錄通常是一個(gè) CNAME 記錄或 A 記錄指向Netlify 提供的目標(biāo)地址。這個(gè)步驟完成后域名解析有幾分鐘到幾小時(shí)的生效延遲不是立刻就能訪問(wèn)別急著反復(fù)刷新。提示自定義域名的 DNS 設(shè)置里我碰到過(guò)一個(gè)問(wèn)題——原域名解析記錄的 TTL 太長(zhǎng)了導(dǎo)致改了之后怎么都不生效。如果你遇到類似情況把 TTL 調(diào)小到 300 秒再等會(huì)快很多。4. 上線后的隱藏配置Netlify 后臺(tái)不容忽視的選項(xiàng)4.1 站點(diǎn)的保護(hù)與訪問(wèn)策略有些時(shí)候你不想讓一個(gè)剛部署的站點(diǎn)被全網(wǎng)隨便訪問(wèn)Netlify 支持在站點(diǎn)設(shè)置里開啟訪問(wèn)保護(hù)。具體位置在 Site settings 下的 Access control你可以設(shè)置密碼保護(hù)讓訪問(wèn)者必須輸入密碼才能看到網(wǎng)頁(yè)內(nèi)容。這個(gè)功能對(duì)項(xiàng)目預(yù)演、客戶驗(yàn)收臨時(shí)環(huán)境特別有用。不過(guò)我實(shí)踐下來(lái)有個(gè)細(xì)節(jié)要提醒如果開啟了密碼保護(hù)那么某些自動(dòng)化工具比如網(wǎng)頁(yè)截圖服務(wù)、搜索引擎爬蟲可能無(wú)法正常訪問(wèn)你的站點(diǎn)。如果你想先保護(hù)起來(lái)給一兩個(gè)人確認(rèn)之后再公開記得解除策略。4.2 重定向與 404 頁(yè)面不只是“找不到頁(yè)面”那么基礎(chǔ)一個(gè) HTML 網(wǎng)站最容易忽視的就是錯(cuò)誤頁(yè)。Netlify 默認(rèn)的 404 頁(yè)面是一個(gè)極簡(jiǎn)的 “Not Found”雖然能說(shuō)明問(wèn)題但和站點(diǎn)風(fēng)格完全不搭。spade2 里我特意加了一個(gè) 404.html 頁(yè)面并在 Netlify 后臺(tái)把它設(shè)置成默認(rèn)的 404 頁(yè)面。這樣用戶訪問(wèn)不存在的鏈接時(shí)看到的還是網(wǎng)站自己的設(shè)計(jì)語(yǔ)言觀感好很多。重定向同樣重要。常見的場(chǎng)景有兩種。第一種是把http請(qǐng)求全部重定向到https這個(gè) Netlify 默認(rèn)就做了不用額外配置。第二種是路徑別名比如想讓用戶訪問(wèn)/home時(shí)實(shí)際渲染index.html可以通過(guò) Netlify 的_redirects文件來(lái)配置。這個(gè)文件放在發(fā)布目錄的根目錄下格式如下/home /index.html 200配置了之后訪問(wèn)/home不會(huì)再遇到 404而是正常渲染首頁(yè)內(nèi)容。對(duì)純靜態(tài)站點(diǎn)來(lái)說(shuō)這算是最輕量的“模擬路由”方案。4.3 部署日志與回滾出問(wèn)題時(shí)最靠得住的功能Netlify 的部署日志幾乎是調(diào)試的第一現(xiàn)場(chǎng)。你在后臺(tái)進(jìn)到 Deploys 頁(yè)面能看到每一次部署的狀態(tài)成功是綠色的 Published失敗是紅色的 Failed。點(diǎn)進(jìn)任意一條記錄展開日志能看到系統(tǒng)執(zhí)行了哪些步驟。純靜態(tài)項(xiàng)目的話日志內(nèi)容比較短主要就是上傳文件、處理重定向規(guī)則、發(fā)布這幾步。真正讓我覺(jué)得這個(gè)功能值錢的地方在于“回滾”。有一次 spade2 改版時(shí)我不小心把一整塊頁(yè)面的樣式寫崩了本地沒(méi)有及時(shí)發(fā)現(xiàn)push 之后線上直接一團(tuán)亂。這時(shí)候我進(jìn)到 Deploys找到上一個(gè)成功的版本點(diǎn)擊 “Rollback to this deploy”系統(tǒng)會(huì)在幾秒內(nèi)把線上版本恢復(fù)到上一次正常狀態(tài)。4.4 構(gòu)建與發(fā)布目錄配置的踩坑記錄如果你部署的是純靜態(tài) HTML最穩(wěn)妥的配置方式是什么配置項(xiàng)推薦值說(shuō)明Build command留空純靜態(tài)項(xiàng)目無(wú)需構(gòu)建Publish directory.或dist/取決于你的文件是否在子目錄部署分支默認(rèn)主分支即可可用生產(chǎn)分支區(qū)分使用環(huán)境我第一次部署時(shí)沒(méi)把發(fā)布目錄設(shè)置對(duì)填成了public/結(jié)果 Netlify 在那個(gè)目錄下找不到index.html直接報(bào)錯(cuò)。后來(lái)仔細(xì)看了一下本地目錄結(jié)構(gòu)才意識(shí)到文件就在根目錄里發(fā)布目錄應(yīng)該填.。這個(gè)低級(jí)錯(cuò)誤很典型本地項(xiàng)目結(jié)構(gòu)里如果多包了一層文件夾發(fā)布目錄就要填到那一層。5. 部署后的性能調(diào)優(yōu)緩存、預(yù)加載與資源優(yōu)化5.1 Netlify 自帶的 CDN 與緩存機(jī)制很多人用 Netlify 單純把它當(dāng)一個(gè)“靜態(tài)服務(wù)器”其實(shí)它的底層分發(fā)網(wǎng)絡(luò)比我預(yù)想的要復(fù)雜得多。你的站點(diǎn)文件在部署后會(huì)被同步到多個(gè)不同的網(wǎng)絡(luò)節(jié)點(diǎn)上用戶訪問(wèn)時(shí)系統(tǒng)會(huì)根據(jù)用戶的物理位置解析到最近的節(jié)點(diǎn)從而大幅縮短資源加載時(shí)間。但 CDN 也帶來(lái)了一個(gè)緩存問(wèn)題你更新了線上文件但部分用戶的瀏覽器可能還在使用舊的緩存內(nèi)容。Netlify 默認(rèn)會(huì)在響應(yīng)頭里帶上緩存有效期通常對(duì)assets/這類帶哈希的文件緩存得比較久對(duì)index.html這類入口文件緩存得短一些。5.2 手動(dòng)調(diào)整緩存策略Correct Headers 的設(shè)置方法如果想精細(xì)控制緩存可以在 Netlify 后臺(tái)的 Headers 面板配置響應(yīng)頭。比如給index.html設(shè)置較短的緩存時(shí)間讓用戶每次訪問(wèn)都能拿到最新內(nèi)容/index.html Cache-Control: public, max-age0, must-revalidate給靜態(tài)圖片、字體這類內(nèi)容設(shè)置較長(zhǎng)的緩存時(shí)間/assets/* Cache-Control: public, max-age31536000, immutable這樣設(shè)置之后每次更新頁(yè)面內(nèi)容時(shí)入口文件能快速生效而圖片字體這類很少改動(dòng)的資源又能充分利用瀏覽器緩存。實(shí)測(cè)下來(lái)spade2 在配置完緩存策略后重復(fù)訪問(wèn)時(shí)的加載速度直觀上快了不少。5.3 HTML 靜態(tài)站的輕量性能優(yōu)化手段除了緩存還有幾個(gè)不費(fèi)力的優(yōu)化手段可以順手做掉。比如給每個(gè)img標(biāo)簽加上width和height屬性能有效減少頁(yè)面布局抖動(dòng)比如把 CSS 里引用的字體文件從遠(yuǎn)程字體庫(kù)改成自托管能減少一次 DNS 查詢和并發(fā)請(qǐng)求。spade2 里我用到了幾張小圖標(biāo)原來(lái)的做法是每個(gè)圖標(biāo)單獨(dú)一個(gè)img標(biāo)簽請(qǐng)求。后來(lái)我看了眼 Network 面板發(fā)現(xiàn)這些圖標(biāo)請(qǐng)求占掉了將近一半的請(qǐng)求數(shù)量。改成了圖標(biāo)合并成一張雪碧圖之后請(qǐng)求數(shù)量從十幾條降到了三條頁(yè)面加載快了一大截。唯一要注意的是坐標(biāo)定位很繁瑣值得權(quán)衡之后再?zèng)Q定要不要上手。5.4 利用 Netlify Forms 給靜態(tài)頁(yè)面加上數(shù)據(jù)收集功能純靜態(tài)站點(diǎn)的最大痛點(diǎn)是沒(méi)有后端表單提交一般只能靠第三方服務(wù)或者寫接口。Netlify 自帶一個(gè) Forms 功能可以直接把靜態(tài)頁(yè)面的表單數(shù)據(jù)收集到后臺(tái)。使用方法很簡(jiǎn)單在 HTML 表單里加一個(gè)>form namecontact methodPOST>