从收藏夹到自托管导航页:onenav 4.0510 主题定制完全指南

📅 2026/8/26 11:29:15
从收藏夹到自托管导航页:onenav 4.0510 主题定制完全指南
简介浏览器收藏夹一多就乱传统书签工具又受限于同步和隐私。自托管导航页成为个人与团队整理高频入口的轻量方案。基于 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再根据自己的实际习惯去调整分类和配色慢慢就会找到最适合自己使用节奏的那套导航方案。本文还有配套的精品资源点击获取