
簡介瀏覽器收藏夾一多就亂傳統書簽工具又受限于同步和隱私。自托管導航頁成為個人與團隊整理高頻入口的輕量方案。基于 onenav 這類 PHPSQLite 程序無需復雜環境即可快速部署數據文件備份即遷移。主題系統支持模板與樣式分離借助 CSS Grid 卡片布局、CSS 變量和暗色模式能輕松改出符合日常操作習慣的視覺風格。同時可整合搜索框、圖標回退、鏈接失效監控等工程化細節讓導航頁真正替代瀏覽器首頁成為統一入口。本文從部署環境、主題文件結構到性能優化梳理了定制一套專屬導航主題的完整路徑。 有導航需求的人大多經歷過同一個階段瀏覽器收藏夾里堆了幾百條鏈接想著“等有空整理一下”結果越攢越亂最后干脆靠搜索硬找。我折騰 onenav 導航主題就是從這種狀態開始的。這套基于 onenav 4.0510 版本定制出來的導航主題不只是把鏈接換了個地方放而是直接替代了瀏覽器首頁把常用工具、資料、內部系統入口全部統一到一個頁面里。今天這篇就聊聊我為什么選 onenav、怎么部署、怎么從默認主題改出自己的一套導航主題以及上線之后踩過的那些坑。如果你正準備搭個人導航站、團隊內部導航頁或者只是想給瀏覽器換個更順手的起始頁這篇文章應該能幫你少走不少彎路。我不寫那種浮在表面的大道理全部是實際操作過的步驟、代碼和排查思路。1. 為什么我把收藏夾扔了改用 onenav 自己搭導航頁1.1 瀏覽器收藏夾的“失控曲線”先坦白我之前的狀況書簽欄放了十幾個文件夾每個文件夾里又套子文件夾最深的層級能點四五層才能到目標網站。這還不算最麻煩的——真正的問題是收藏夾的搜索能力極弱分類只能靠手工拖拽而且換一臺電腦、換一個瀏覽器書簽同步還得看賬號和網絡狀態。后來也試過在線書簽工具但要么是功能太重要么是把數據存在別人的服務器上隱私和訪問速度總有一個讓我不舒服。再加上我自己的使用場景里有大量“只對內部開放”的系統地址這些東西放在第三方平臺上本身就是隱患。于是我把目光轉向了自托管導航頁自己買一個小服務器或者直接部署在已有的 VPS、NAS 上通過網頁的方式把所有鏈接統一管理。好處是數據完全自主可控換設備只需要開個網頁登錄分類、排序、圖標全都可以自定義。1.2 為什么是 onenav 而不是其他導航程序市面上類似的工具我至少試過五六個包括純靜態的 HTML 導航頁、Flarum 之類的論壇式書簽、還有一些用 TypeScript 寫的新項目。對比下來onenav 有幾個優勢非常突出部署簡單它是個 PHP 程序環境要求不高一臺內存 512MB 的小機器就能跑得動。數據存儲輕量默認用 SQLite不需要單獨裝 MySQL備份就是一個文件。自帶后臺管理分類、鏈接、主題、站點設置都能在網頁后臺完成不需要改代碼。主題系統清晰模板、樣式、腳本分離改起來比那些“一鍵生成導航頁”的工具靈活得多。中文生態友好文檔和社區討論大多是中文遇到問題搜一下就能解決。至于這套導航主題為什么叫 4.0510其實就是我這邊的內部版本號。我一開始用的是 onenav 官方默認主題但用了一周就發現默認主題的布局太“工具化”首頁直接平鋪一堆鏈接視覺上沒什么層次。4.0510 是我從默認主題的模板結構出發重做卡片樣式、間距、圖標體系和響應式布局之后定下來的版本號后面所有的改動都以這套主題為基礎。1.3 這套主題適合誰個人用戶想給瀏覽器一個清爽的起始頁把高頻站點、常用工具、待辦入口集中起來。小團隊/小組把項目文檔、代碼倉庫、監控面板、會議鏈接統一放一個導航頁省得每天口頭傳遞網址。服務器愛好者有一臺閑置 VPS/NAS想讓它發揮一點實際作用。如果你只想“放幾個鏈接完事”那也確實沒必要上這套東西但如果你像我一樣有幾十上百個入口需要組織并且希望頁面既好看又能快速到達那這套方案是劃算的。2. 部署 onenav 4.x環境選擇、安裝步驟與首登配置2.1 環境需求別再犯裝不上程序的低級錯誤onenav 對運行環境的要求其實很低但恰恰因為“低”很多人反而不看文檔直接裝最后因為缺擴展卡在半路。我這邊梳理一份可以照抄的清單項目建議配置備注PHP 版本PHP 7.4 及以上建議 PHP 8.0/8.1PHP 8.2 也可能沒問題但有些第三方擴展會兼容性翻車必裝擴展PDO、SQLite3、curl、mbstring、openssl沒有 SQLite3 的話安裝階段就會直接報錯Web 服務器Nginx 或 Apache 均可個人更推薦 Nginx偽靜態配置更順手系統Linux 優先Windows/macOS 也能跑生產環境建議 Linux最低內存512MB 以上1GB 會更寬裕我在部署時用的是寶塔面板加 Nginx 的組合PHP 版本選的 8.0。安裝過程中遇到過最典型的兩個問題一是 PHP 沒裝 SQLite 擴展安裝界面直接白屏二是打開安裝向導時目錄權限不對導致無法寫入配置文件。這兩個問題在下面的安裝步驟里我會特別標注。2.2 從下載到跑起來三種安裝方式方式一直接下載源碼部署到 onenav 的發布頁面下載最新版本的壓縮包解壓到站點根目錄比如/www/wwwroot/nav.example.com。然后設置運行目錄把站點運行目錄指向public這一步很關鍵不然訪問首頁會直接暴露目錄結構。接著在站點配置里加一條偽靜態規則。我的 Nginx 配置如下Apache 用戶可以直接用程序自帶的.htaccesslocation / { if (!-e $request_filename) { rewrite ^(.*)$ /index.php?s$1 last; } }配置好之后在瀏覽器訪問你的域名會進入安裝向導。填入站點名稱、管理員賬號密碼、數據庫類型保持 SQLite 即可下一步就會生成配置文件。如果安裝頁面提示權限問題記得給data目錄和根目錄寫入權限chmod -R 755 /www/wwwroot/nav.example.com chmod -R 777 /www/wwwroot/nav.example.com/data方式二Docker 部署如果你不想在宿主機上折騰 PHP 環境Docker 是最省事的方式。我測試過用下面這個組合跑 onenav 4.x穩定運行了大半年version: 3 services: onenav: image: helloz/onenav:latest container_name: onenav ports: - 8080:80 volumes: - ./data:/data/wwwroot/default/data restart: unless-stopped啟動之后瀏覽器訪問http://服務器IP:8080同樣會進入安裝向導。需要注意 Docker 版本的數據目錄必須掛載出來否則容器刪掉后數據就全沒了。方式三內網 NAS 部署如果你的 NAS 支持 Docker也可以直接跑。我的實際經驗是內網部署時不需要做端口映射只需要在路由器上保留內網訪問即可。當然如果想在外網訪問那就需要用到反向代理不建議直接把端口裸奔到公網。2.3 登錄后臺后第一件事改什么onenav 裝好后默認的頁面和功能都比較“原始”你需要花幾分鐘把下面幾項設置改好后面再折騰主題時才能順心設置項我的建議說明基礎設置里的站點名稱改成你的實際名稱會顯示在瀏覽器標題和首頁頭部別留默認的鏈接打開方式新窗口打開導航頁的跳出率很低保持新窗口打開才不會讓用戶迷路登錄密碼和 Token一定要改默認值后臺默認賬號密碼登錄后立即更新是否開啟注冊單人或團隊內部用就徹底關掉避免陌生人進來亂改分類站點統計按需接入不接也沒關系但接了能看到哪些鏈接被點得最多還有一點容易被忽略如果是在 HTTP 環境里跑建議盡早給站點加上 SSL 證書不然瀏覽器會一直提示“不安全”不僅難看還會影響搜索框的自動填充等功能。用 Let‘s Encrypt 或寶塔面板的一鍵 SSL 都行。3. 主題文件結構解析改主題前必須知道的三件事3.1 主題目錄里到底哪些文件說了算onenav 的主題文件都在app/view/目錄下默認主題是default。我自己做的 4.0510 主題實際上是在這個目錄下新建了一個mytheme文件夾再從默認主題拷貝出核心模板文件來改。這樣做的最大好處是程序升級時不會覆蓋你的自定義文件即使覆蓋了也只是覆蓋了default你的mytheme依然保持完整。主題文件結構大致如下app/view/mytheme/ ├── admin.php # 后臺管理模板一般不用動 ├── footer.php # 頁腳模板 ├── header.php # 頁頭模板包含 HTML head、CSS、JS 引入 ├── index.php # 首頁主模板導航分類和鏈接的核心渲染邏輯 ├── login.php # 登錄頁模板 ├── assets/ │ ├── css/ # 樣式文件 │ └── js/ # 交互腳本如果你只想做局部美化核心看兩個文件header.php和index.php。前者控制整個頁面的頭部和公共樣式引入后者決定導航內容的排列方式。3.2 通過模板變量理解數據渲染邏輯第一次打開index.php的時候你會發現里面夾雜著很多?php echo $xxx; ?這種寫法。這是 onenav 自研模板引擎的輸出方式不是亂碼。你只需要掌握最常用的幾個變量$webconf[site_name] // 站點名稱 $webconf[site_desc] // 站點描述 $categorys // 全部分類數據循環輸出 $links // 當前分類下的鏈接集合 $current_cate // 當前選中的分類最簡單的理解方式是$categorys是一個二層結構外層是分類內層是該分類下的鏈接數組。首頁模板的循環邏輯大概長這樣?php foreach ($categorys as $cate) : ? div classnav-category h2 classcategory-title?php echo $cate[name]; ?/h2 div classcategory-links ?php foreach ($cate[links] as $link) : ? a classnav-item href?php echo $link[url]; ? target_blank img src?php echo $link[icon]; ? alt span classlink-title?php echo $link[title]; ?/span /a ?php endforeach; ? /div /div ?php endforeach; ?把這個結構看懂之后主題改造就變成了一件事——修改這些 HTML 標簽和 CSS 類名把它從“平鋪列表”變成你想要的“卡片網格”。3.3 我為什么把導航鏈接設計成“卡片圖標優先”默認主題里每個鏈接都是一個純文字列表一眼掃過去分不清主次。4.0510 主題里我把每個鏈接改成了卡片式按鈕左側或上方是網站圖標下面跟著標題。這樣做的原因有兩個一是視覺上有“按鈕感”鼠標點起來更自然二是圖標能幫助眼睛快速定位目標特別是當你的導航頁里有十幾個同類站點時一張臉比一段文字更容易認。實現方式不復雜核心就是 CSS Grid 布局加上圖標處理。后面我會詳細展開。4. 視覺重構實操從布局、配色到字體的一步步改造4.1 用 CSS 變量統一主題色調改配色不再全站亂找很多人在改導航主題時容易掉進“這里改一下、那里改一下”的坑最后頁面紅不紅藍不藍看起來像拼貼畫。我的做法是先定義一組 CSS 變量把顏色、圓角、間距全部抽象出來后續只需要改變量全站風格就統一了。在assets/css/main.css頂部加這么一段:root { --bg-primary: #f5f7fb; /* 頁面背景 */ --bg-card: #ffffff; /* 卡片背景 */ --text-primary: #1f2937; /* 主文字 */ --text-secondary: #6b7280; /* 次級文字 */ --accent: #4f6ef7; /* 強調色 */ --radius: 12px; /* 卡片圓角 */ --shadow: 0 2px 8px rgba(0, 0, 0, 0.06); --max-width: 1200px; /* 內容最大寬度 */ }之后在.nav-item、.category-title等樣式中直接用這些變量。如果你想做暗色模式只需要在后半夜時間段切換body上的一個類名然后把變量重新賦值即可。4.2 卡片網格的寫法和斷點設計4.0510 主題的布局核心是網格而不是傳統的浮動或 Flex 換行。用 Grid 的好處是卡片寬度自動適配而且不用寫一堆 margin hack。.nav-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 16px; } .nav-item { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 20px 12px; background: var(--bg-card); border-radius: var(--radius); box-shadow: var(--shadow); transition: transform 0.2s ease, box-shadow 0.2s ease; text-decoration: none; } .nav-item:hover { transform: translateY(-4px); box-shadow: 0 8px 20px rgba(0, 0, 0, 0.12); } .nav-item img { width: 40px; height: 40px; border-radius: 10px; margin-bottom: 10px; object-fit: contain; } .link-title { font-size: 14px; color: var(--text-primary); text-align: center; line-height: 1.4; }這個布局有一個很實用的特性minmax(160px, 1fr)表示每個卡片最小 160px 寬如果屏幕夠寬網格會自動把額外的空間分給每一列。手機上訪問時卡片自動從一行五六個變成一行兩三個不需要額外寫媒體查詢。不過也要提一個容易踩的小坑如果你在卡片里使用了box-shadow在低性能手機上列表很長時可能會有輕微掉幀。解決辦法是把 hover 的陰影過渡改成opacity過渡或者干脆不要 transform 位移我實測去掉 transform 后滾動流暢度明顯提升。4.3 首頁首屏LOGO、搜索框和快捷入口導航頁的“首屏”很重要因為用戶的注意力首先落在那里。我的 4.0510 主題首頁首屏從上到下依次是頂部站點 LOGO 和名稱做成居中的低調樣式一個巨大的搜索框支持直接跳轉百度、必應、GitHub 等搜索源下面緊接著就是導航分類和鏈接卡片區。搜索框的實現其實不復雜我用了 onenav 自帶的搜索功能只需要在header.php里加入一個簡單的表單即可。如果你想讓搜索框支持多引擎切換可以做成一個下拉選擇框把action指向不同搜索引擎的請求地址。form classsearch-box actionhttps://www.bing.com/search methodget target_blank select nameengine option valuebing必應/option option valuebaidu百度/option option valuegithubGitHub/option /select input typetext nameq placeholder搜索一下... autocompleteoff button typesubmit搜索/button /form如果你用的是其他搜索引擎形式也差不多反正都是 GET 請求把q參數帶過去就行。這一步做完導航頁的“工具屬性”就立起來了不再是單純的一堆鏈接。4.4 暗色模式的實現思路暗色模式是很多人一上來就想要的功能但實現得不好會適得其反。我用的方案是給body加一個.dark類然后重新定義 CSS 變量body.dark { --bg-primary: #111827; --bg-card: #1f2937; --text-primary: #f9fafb; --text-secondary: #9ca3af; --accent: #8196ff; --shadow: 0 2px 8px rgba(0, 0, 0, 0.4); }切換邏輯放在assets/js/darkmode.js里根據本地存儲記錄用戶偏好const toggleDark () { document.body.classList.toggle(dark); localStorage.setItem(theme, document.body.classList.contains(dark) ? dark : light); }; if (localStorage.getItem(theme) dark) { document.body.classList.add(dark); }暗色模式不只影響背景色卡片里的圖標、文字、搜索框邊框都要跟著變。所以一定要用 CSS 變量而不是寫死顏色。5. 分類、圖標與排序把導航頁做成本地“總入口”5.1 分類邏輯別按“網站類型”分要按“使用場景”分很多人給導航分類時下意識按“技術”“生活”“娛樂”這種方式分組但實際使用中你會發現一個問題一個網站可能同時屬于多個場景。比如“GitHub”既是開發工具又是學習資料還是日常逛的社區。硬分類就會糾結。我的做法是按“使用場景”分組而不是“網站類型”場景分組包含示例為什么這樣分每日必開郵箱、日歷、待辦、新標簽頁上班第一件事就點它開發工具箱GitHub、代碼片段、JSON 格式化、API 文檔寫代碼過程中頻繁切換內網系統路由后臺、NAS、監控面板不需要記 IP入口統一資訊與社區技術論壇、新聞、博客閱讀場景集中分類名稱可以短一點一兩個字最好導航頁不是文章目錄不需要解釋得很清楚。后臺“分類管理”里可以直接拖動排序把最常用的放在前面。5.2 圖標方案從 favicon 到自定義圖標的完整鏈路導航鏈接的圖標是決定頁面美觀度的關鍵之一。onenav 本身有“自動拉取網站 favicon”的功能但實際體驗并不理想有些網站的 favicon 加載慢有些干脆沒有還有的低分辨率圖標放大后糊成一片。我在 4.0510 主題里采用的圖標策略是三級回退優先使用后臺手動上傳的自定義圖標。沒有自定義圖標時使用https://favicon.im/{域名}這類公開 favicon 服務。如果 favicon 獲取失敗顯示一個首字母占位圖標。后臺添加鏈接時圖標地址可以填本地路徑或遠程 URL。如果你有一堆站點要批量加可以寫個簡單的 Python 腳本把站點域名批量替換成圖標地址https://favicon.im/example.com另外圖標是有緩存的。如果你改了某個鏈接的圖標但首頁還是舊圖多半是瀏覽器緩存或 onenav 的圖標緩存目錄里存了舊文件。清理一下data/下的緩存目錄再強制刷新瀏覽器一般就能解決。5.3 鏈接排序的隱藏規則前臺導航分類里的鏈接排序后臺有個“排序”數值字段數字越小越靠前。但這里有個容易誤解的點不是所有鏈接都按全局排序同一個分類下的鏈接之間才是按這個值排序。所以如果你想微調某個分類內部順序直接在后臺改對應鏈接的排序值即可。我也見過有人在鏈接標題里加數字前綴來手動排序比如“01-郵箱”“02-日歷”這種做法的缺點是一旦要插入新鏈接后面所有前綴都要改。后來我寫了一個小 SQL 腳本批量調整排序省了不少事建議你直接在后臺拖動排序就好不用搞花活。6. 主題上線后的性能優化與常見坑排查6.1 慢頁面排查PHP-FPM、SQLite 與緩存onenav 本身很輕量但如果你在一個性能很差的虛擬主機上跑還是可能出現打開首頁要兩三秒的情況。第一步先確認瓶頸出在哪。我一般是這樣排查的看 Nginx 訪問日志確認是動態請求慢還是靜態資源慢。靜態資源慢就開 CDN 或優化圖片動態慢就往下查。打開 PHP-FPM 的慢日志例如寶塔面板里設置request_slowlog_timeout 5s然后看踩點日志。如果慢日志里定位到index.php和 SQLite 查詢大概率是數據量太大導致全表掃描。鏈接數量達到幾千條時SQLite 的查詢性能會明顯下降。對于絕大多數個人導航站鏈接數量不會超過幾百條這種規模下 SQLite 完全夠用。真到了幾千條的規模建議考慮往 MySQL 遷移onenav 后臺可以直接切換數據庫類型數據導入導出也比較成熟。另外onenav 自帶頁面緩存功能在“設置”里開啟后頁面會生成靜態 HTML 文件后續請求直接命中緩存速度和純靜態頁差不多。代價是修改鏈接、分類后需要手動更新緩存否則前臺看不到變化。我平時是“開發時關緩存上線后開緩存”這個節奏比較舒服。6.2 升級程序時我的主題會不會被覆蓋這是我最擔心也最常被問到的問題。onenav 升級時官方包里的app/view/default會被覆蓋但只要你按照前面提到的做法把主題放在一個新的文件夾里比如mytheme然后到后臺主題設置里選擇這個主題理論上就不會被升級流程動到。但也有兩個意外情況值得提醒后臺的“主題設置”里如果勾選了自動更新相關選項可能會自動去下載主題更新覆蓋你的自定義文件。我建議關掉自動更新手動做備份。升級前先備份data目錄和app/view/mytheme目錄。備份方式很簡單直接打包整個站點目錄或使用后臺的備份功能。我升級 onenav 4.x 到新版本的常規流程是先打包舊站點目錄再上傳新版本源碼保留data目錄和自定義主題目錄然后執行升級腳本。目前跑了幾次都沒出問題。6.3 鏈接失效與站點可用性監控導航頁的最大痛苦是“死鏈”別人點了一個鏈接半天打不開體驗很糟。onenav 本身沒有內置鏈接監控功能我的做法是寫了一個簡單的爬蟲腳本每個月跑一次把所有鏈接抓一遍返回非 200 狀態碼的標記出來。腳本邏輯不復雜import sqlite3 import requests conn sqlite3.connect(data/onenav.db) cursor conn.cursor() cursor.execute(SELECT id, url FROM links) rows cursor.fetchall() for link_id, url in rows: try: resp requests.head(url, timeout5, allow_redirectsTrue) if resp.status_code ! 200: print(f鏈接失效: {url} - {resp.status_code}) except Exception as e: print(f請求異常: {url} - {e})這個腳本可以放到服務器的 crontab 里定時執行結果輸出到日志文件有需要就抽空清理失效鏈接。6.4 移動端的適配與 PWA 離線緩存很多人以為導航頁只會在電腦上開但實際上手機瀏覽器打開導航頁的頻率也非常高。4.0510 主題在移動端的適配比默認主題更細致搜索框和 LOGO 在手機上縮小間距避免首屏全是空白卡片網格從 5 列自動變成 2 列或 3 列導航分類的標題在手機上固定定位滾動時保持可見方便快速切換。如果想更進一步可以給導航頁加一個簡單的 PWA 離線緩存讓它在弱網狀態下也能打開。做法是在header.php里加 manifest 鏈接和 Service Worker 注冊代碼然后寫一個簡單的sw.js文件緩存首頁和核心資源。不過 PWA 對 HTTPS 有硬性要求如果你只是內網 http 部署這部分可以跳過。7. 4.0510 主題的后續擴展思路導航主題做到這一步功能和視覺都已經比較完整了。如果你還想要更多我根據自己實際試過的方向列幾個可行的擴展第一個是“快捷方式分類”加二級菜單。onenav 后臺默認分類是扁平結構但我的內網系統里有很多子頁面比如路由器后臺下面還有交換機管理頁面。我通過在鏈接標題里加入#分隔符再在前臺模板里做一個簡單的分組渲染實現了類似二級菜單的效果。這個方法不需要改數據庫只是利用標題規則和 CSS 技巧適合鏈接數量不多的時候用。第二個是“今日推薦”模塊。在主題首頁加一個獨立的區塊顯示最近添加的鏈接或者手動置頂的站點。實現方式很簡單在主模板里額外加一個數據查詢調用 onenav 的鏈接模型按創建時間倒序取最近 5 條數據。這個小改動能讓導航頁看起來更“活”而不是死板的一堆鏈接。第三個是“團隊共享”場景的權限細分。如果你是把導航頁給整個小組用可能希望不同人看到不同分類。onenav 的用戶體系默認比較簡單但可以通過外部認證或分組插件擴展。我目前是直接把導航頁設為公開所有成員都能看能點但只有管理員能改鏈接和分類這樣能兼顧開放和穩定。第四個是接入瀏覽器的“新標簽頁”功能。Chrome 和 Edge 都支持自定義新標簽頁擴展原理是讓新標簽頁直接 iframe 內嵌你的導航頁地址。這樣瀏覽器一打開新標簽頁就是你自己的導航站體驗和設置主頁一樣順滑。如果你用的是 Firefox也可以通過插件實現類似效果。8. 最后分享兩個小技巧一是主題文件改完后“強制刷新”不一定能立即看到效果。onenav 在某些版本會啟用服務端頁面緩存直接刷新瀏覽器可能還是舊頁面。需要在后臺的“緩存管理”里清一下緩存再刷新一次。這個坑我踩過好幾次后來干脆養成“改完主題先清緩存再看效果”的習慣。二是如果你也想用這套“卡片式導航”的思路去改其他 PHP 導航程序上面的 CSS Grid 布局、圖標三級回退、搜索框整合方案基本都是通用的改改模板變量名就能復用。我每次給別人推薦導航頁方案時都會說一句話工具很多能按自己工作流定制的才叫好用。上面的部署、主題改造和運維經驗基本覆蓋了從零到上線再到日常維護的完整鏈路。你可以照著步驟先搭一個 demo再根據自己的實際習慣去調整分類和配色慢慢就會找到最適合自己使用節奏的那套導航方案。本文還有配套的精品資源點擊獲取