
1. 这个导航页到底解决什么问题先说说我为什么要折腾这个东西。日常工作中我经常需要在不同设备、不同浏览器之间切换每次换环境都要重新翻收藏夹、重新登录各种账号时间全耗在“找入口”这件事上了。尤其是帮同事临时查个资料、给朋友演示个工具打开浏览器面对一片空白的新标签页那种感觉就像进了一个没有路标的停车场。免登录Yandex聚合入口这个思路核心就是做一个纯前端导航页——不需要后端服务器、不需要数据库、不需要用户系统打开就能用所有数据存在浏览器本地。Yandex在这里扮演的角色是一个可选的搜索与内容聚合来源它的开放接口和页面结构相对规整适合做快速索引和跳转。而AI的介入点在于我不需要手写几百行HTML和CSS把需求描述清楚让AI帮我生成基础骨架我再做针对性调整。这个导航页适合什么人用三类人最受益一是经常在多台设备间切换的开发者或运维人员需要一个随身携带的入口集合二是想学前端但不想从零啃文档的新手拿这个项目当练手素材改改样式、加加链接就能跑起来三是需要给团队做内部工具面板的人纯前端意味着可以直接扔到任意静态托管上不需要运维成本。我实测下来整个项目从零到可用状态核心开发时间不超过两小时其中AI生成初版代码占了大概四十分钟剩下的是我手动调整布局、测试兼容性、补充细节。这个投入产出比对于一个小工具来说相当划算。2. 整体设计思路与方案选型2.1 为什么坚持纯前端方案纯前端意味着所有逻辑都在浏览器里跑没有服务端参与。这个选择不是赶时髦而是基于几个很实际的考量。第一部署成本为零。你不需要买服务器、不需要配域名解析、不需要管SSL证书。把生成的HTML文件往任何静态托管服务上一放或者直接双击本地文件打开就能用。对于个人工具来说这是最轻量的方案。第二数据隐私可控。所有导航链接、分类、备注都存在浏览器的localStorage里不会上传到任何服务器。你加了什么书签、怎么分类的只有你自己知道。这一点对于经常处理内部系统入口的人来说很重要。第三离线可用。纯前端页面一旦加载完成即使断网也能打开当然跳转目标需要网络。我试过在飞机上整理导航分类落地后直接就能用体验很顺畅。当然纯前端也有明显的短板数据不能跨设备同步。你在电脑上加的链接手机上看不到。这个问题后面我会讲怎么用导出导入功能来缓解。2.2 AI在项目中扮演什么角色很多人对AI编程有误解觉得描述一句话就能生成完美产品。实际用下来AI更像是一个速度极快的初级开发者——它能帮你把重复性的结构代码写出来但架构决策、细节打磨、兼容性处理还得自己来。我这次用AI主要做了三件事生成基础HTML结构和CSS布局、写localStorage的读写逻辑、生成一批示例导航数据。提示词我写得比较具体比如“生成一个响应式导航页顶部有搜索框主体是卡片式链接网格每个卡片包含图标、标题、描述支持分类筛选数据存在localStorage”。这种颗粒度的描述AI一次就能给出可运行的代码。但AI生成的代码有几个通病需要手动修一是CSS类名混乱同一个元素在不同地方用了不同命名二是移动端适配往往只做了最基础的媒体查询实际在窄屏上会有溢出三是localStorage的异常处理基本没有比如存储满了或者被禁用的情况。这些问题不修页面能跑但不好用。2.3 技术栈的极简选择这个项目我刻意没有引入任何框架。React、Vue确实能提高开发效率但对于一个导航页来说引入构建工具链、包管理器、编译步骤反而增加了维护负担。我用的是最朴素的三件套原生HTML CSS JavaScript。图标方案上我没有用Font Awesome这类图标库而是直接用了Emoji和SVG内联。Emoji的好处是零依赖、跨平台显示一致大部分情况而且AI生成时可以直接嵌入。SVG用于几个需要精确控制的图标比如搜索按钮和分类标签。数据存储用localStorage结构就是一个JSON数组每个对象包含id、title、url、description、category、icon几个字段。读写逻辑封装成两个函数加载时解析保存时序列化。这里有个细节每次修改后不要全量写入而是先更新内存中的数组再统一持久化减少IO操作。3. 核心细节解析与实操要点3.1 页面结构怎么搭才合理导航页的结构看着简单但要做到“一眼能找到东西”并不容易。我最终采用的布局是顶部固定搜索栏 左侧分类侧边栏 右侧卡片网格。这个结构在桌面端很高效但在移动端需要折叠侧边栏。顶部搜索栏不只是装饰它承担了实时过滤的功能。输入关键词时下面的卡片会即时筛选匹配标题、描述和分类。这个交互比传统的“输入后按回车”体验好很多用户不需要额外操作就能看到结果。左侧分类侧边栏在桌面端常驻显示每个分类显示名称和该分类下的链接数量。点击分类后右侧只显示该分类的卡片。移动端则把侧边栏收进一个汉堡菜单里点击后从左侧滑出。这个响应式切换的断点我设在768px实测下来这个宽度在平板竖屏和手机横屏时都能较好适配。卡片网格用的是CSS Gridgrid-template-columns: repeat(auto-fill, minmax(240px, 1fr))。这行代码的意思是每列最小240px最大自动填充剩余空间列数根据容器宽度自动计算。这样无论屏幕多宽卡片都能均匀分布不会出现某一行特别空或者特别挤的情况。3.2 数据存储的坑与解决方案localStorage用起来简单但有几个坑我踩过之后才明白。第一个坑是存储容量限制。大多数浏览器给localStorage的限制是5MB左右纯文本链接数据远远用不到这个量但如果你把图标转成Base64存进去很快就会撑满。我的做法是图标只用Emoji或短SVG字符串不存图片文件。第二个坑是数据格式兼容。第一版我存的是纯数组后来想加一个“最后修改时间”的字段直接改结构会导致旧数据读取报错。解决方案是在读取时做一次迁移检查如果解析出来的对象没有version字段就按旧格式处理并补上新字段。这个逻辑写起来就几行但能避免用户升级后数据丢失。第三个坑是隐私模式下的异常。某些浏览器的隐私模式下localStorage是只读的写入会抛异常。我的处理方式是用try-catch包裹所有写入操作失败时给用户一个提示“当前环境不支持本地存储您的修改不会被保存”。这样至少不会让页面直接崩溃。function saveData(data) { try { localStorage.setItem(nav_data, JSON.stringify(data)); return true; } catch (e) { console.warn(存储失败:, e); showToast(当前环境不支持本地存储); return false; } }3.3 AI生成代码的调优技巧用AI生成代码提示词的质量直接决定输出质量。我总结了一个四段式提示词结构角色设定 功能描述 技术约束 输出格式。角色设定让AI知道自己在什么场景下工作比如“你是一个资深前端开发者擅长写简洁高效的 vanilla JavaScript”。功能描述要具体到交互层面不要只说“做一个导航页”而是说“做一个导航页支持添加、删除、编辑链接支持分类筛选支持搜索过滤”。技术约束明确不用框架、不用外部依赖、兼容移动端。输出格式要求AI把HTML、CSS、JS分开放在三个代码块里方便我直接复制。AI生成的代码我一般会做三轮调整。第一轮是功能验证把所有按钮点一遍看有没有报错。第二轮是样式微调AI给的配色和间距往往偏“通用”我会改成自己习惯的色板和间距系统。第三轮是边界测试比如输入超长标题、添加重复链接、快速连续点击删除按钮看有没有异常。这里有个小技巧让AI生成代码时要求它在关键逻辑处加注释。这样你后续修改时能快速理解意图不用逐行反推。注释不用多每个函数头部一行说明就够了。3.4 搜索过滤的实现细节搜索过滤看着简单但要做得跟手、不卡顿有几个细节要注意。首先是防抖处理。如果每次按键都触发过滤输入快的时候会明显卡顿。我的做法是设置一个200毫秒的延迟用户停止输入200毫秒后才执行过滤。这个延迟在体感上几乎察觉不到但能大幅减少计算次数。其次是匹配策略。我用的不是简单的includes而是把搜索词拆成单个字符要求标题或描述中包含所有字符不要求连续。这样搜“git hub”也能匹配到“GitHub”容错性更好。当然这会增加计算量所以配合防抖使用。最后是空结果提示。当过滤后没有匹配项时显示一个友好的空状态而不是一片空白。我放了一个简单的SVG插画和一行文字“没有找到匹配的链接试试其他关键词”。这个细节对用户体验的提升很明显。4. 完整实操流程与核心环节4.1 从零生成基础页面的步骤第一步打开你常用的AI对话工具输入提示词。我用的提示词大意是“生成一个纯前端导航页包含以下功能顶部搜索框实时过滤、左侧分类列表、右侧卡片网格展示链接、支持添加和删除链接、数据存储在localStorage。使用原生HTML/CSS/JS不依赖任何外部库。输出三个代码块分别对应HTML、CSS、JS。”第二步把AI生成的代码保存为三个文件index.html、style.css、app.js。如果你想让文件更少也可以把CSS和JS内联到HTML里但分开更利于后续维护。第三步在浏览器中打开index.html打开开发者工具的控制台看有没有报错。常见的问题包括AI生成的代码里引用了不存在的图标类名、localStorage的key拼写不一致、事件监听绑定在了尚未渲染的元素上。这些问题根据控制台提示逐个修复即可。第四步添加几条测试数据验证增删改查和搜索过滤是否正常。我一般会加五条数据涵盖不同分类和不同长度的标题确保布局不会因为内容长度而崩坏。4.2 分类与标签系统的设计分类系统我设计了两层主分类和标签。主分类是互斥的一个链接只能属于一个主分类比如“开发工具”“设计资源”“内部系统”。标签是多对多的一个链接可以有多个标签比如“常用”“待整理”“团队共享”。这个设计的好处是筛选维度更灵活。你可以先按主分类缩小范围再用标签做二次过滤。实现上主分类存在链接对象的category字段里标签存在tags数组里。筛选时先匹配主分类再检查标签数组是否包含选中的标签。分类的排序我用了手动拖拽的方案。每个分类有一个order字段拖拽后更新这个字段并重新渲染。拖拽库我没有引入外部依赖而是用HTML5的Drag and Drop API自己实现。代码量大概五十行比引入一个库更可控。4.3 响应式布局的断点设置响应式布局不是简单地加几个媒体查询就完事关键是要在每个断点上都保证可用性。我的断点设置是这样的大于1024px时侧边栏常驻卡片网格四列768px到1024px之间侧边栏常驻但变窄卡片网格三列小于768px时侧边栏折叠卡片网格两列小于480px时卡片网格一列搜索框占满宽度。每个断点我都实际在浏览器里拖拽窗口测试过确保没有横向滚动条、没有文字溢出、按钮可点击区域不小于44px移动端触控的最小推荐尺寸。这里有个容易忽略的点卡片的描述文字在窄屏上要限制行数否则一条长描述会把卡片撑得很高破坏网格对齐。我用-webkit-line-clamp: 2来限制显示两行超出部分省略号。4.4 数据导入导出的实现纯前端方案最大的短板是不能跨设备同步导入导出功能就是补这个短板的。实现很简单导出一个JSON文件在另一台设备上导入。导出时把localStorage里的数据取出来用Blob和URL.createObjectURL生成一个下载链接文件名带上日期比如nav-backup-2025-01-15.json。导入时用一个隐藏的input typefile接收文件读取后用JSON.parse解析校验格式后合并到现有数据里。这里有个细节导入时是覆盖还是合并。我提供了两个选项默认是合并按id去重用户也可以选择覆盖。合并的逻辑是遍历导入的数据如果id已存在就跳过不存在就追加。这样不会丢失现有数据也不会产生重复项。function importData(file, mode) { const reader new FileReader(); reader.onload (e) { try { const imported JSON.parse(e.target.result); if (mode merge) { const existingIds new Set(data.map(item item.id)); const newItems imported.filter(item !existingIds.has(item.id)); data data.concat(newItems); } else { data imported; } saveData(data); render(); } catch (err) { showToast(文件格式不正确); } }; reader.readAsText(file); }4.5 性能优化的几个实测点导航页数据量大了之后我测试过500条链接渲染性能会下降。优化手段有几个虚拟滚动是最彻底的方案但实现复杂度高。对于500条以内的数据我用的是分批渲染先渲染前50条滚动到底部时再加载下一批。这样首屏渲染时间从800毫秒降到了120毫秒左右。事件委托也很关键。如果每个卡片的删除按钮都单独绑定事件500个按钮就是500个监听器。改成在父容器上监听通过event.target判断点击的是哪个按钮内存占用和初始化时间都会大幅下降。CSS containment是一个容易被忽略的优化点。给每个卡片加上contain: layout style paint告诉浏览器这个元素的布局和绘制不影响外部浏览器可以更激进地优化渲染。实测在低端设备上滚动帧率能从40fps提升到55fps左右。5. 常见问题与排查技巧实录5.1 页面打开一片空白怎么查这是最常见的问题原因通常有三个。第一JS文件路径不对浏览器控制台会报404。检查script src...的路径是否和实际文件位置一致。第二JS代码有语法错误控制台会显示具体的行号和错误信息。第三localStorage里有脏数据导致解析失败。解决方法是在读取时加try-catch解析失败就重置为空数组。我遇到过一次比较隐蔽的情况AI生成的代码里用了const声明了一个变量但在后面的代码里又用var声明了同名变量导致严格模式下报错。这种问题看控制台提示就能定位关键是养成打开控制台的习惯。5.2 移动端点击不灵敏的排查移动端点击不灵敏通常是因为点击区域太小或者300毫秒延迟。点击区域的问题好解决给按钮加padding确保最小44x44px。300毫秒延迟是历史遗留问题现代浏览器在设置了正确的viewport meta标签后已经不会有了。viewport标签要写成meta nameviewport contentwidthdevice-width, initial-scale1.0。如果漏了initial-scale1.0某些浏览器会按980px的虚拟视口渲染导致页面缩小、点击错位。还有一个坑是CSS的:hover样式在移动端会粘住。用户点一下按钮hover样式一直保留到点其他地方。解决方案是用media (hover: hover)包裹hover样式只在支持悬停的设备上生效。5.3 数据丢失的预防与恢复数据丢失是用户最恼火的问题。预防措施有三层第一每次修改后自动保存到localStorage不需要用户手动点保存。第二提供一个“导出备份”按钮引导用户定期备份。第三在页面加载时检查数据完整性如果发现数据为空但localStorage里有备份key提示用户恢复。恢复逻辑是这样的每次保存时除了写入主key还写入一个带时间戳的备份key只保留最近三个备份。如果主key读取失败或为空就尝试从备份key恢复。这个机制我实测救过两次数据一次是浏览器崩溃导致写入中断一次是用户误操作清空了数据。5.4 常见问题速查表问题现象可能原因排查方法解决方案页面空白JS路径错误或语法错误打开控制台看报错修正路径或语法数据不保存localStorage被禁用控制台执行localStorage.setItem(t,1)提示用户或降级到内存存储移动端点击无效点击区域过小检查按钮尺寸增加padding至44px以上搜索卡顿未做防抖快速输入观察帧率加200ms防抖卡片高度不一描述文字过长检查长文本卡片用line-clamp限制行数导入失败JSON格式错误用JSON校验工具检查文件提示用户检查文件格式5.5 几个我踩过的坑第一个坑是AI生成的CSS用了不兼容的属性。比如gap在旧版Safari上需要-webkit-前缀AI有时候会漏掉。我的做法是生成后用Can I Use查一下关键属性必要时手动加前缀。第二个坑是Emoji图标在不同系统上显示差异很大。同一个Emoji在Windows上是彩色在某些Linux发行版上是黑白在旧版Android上甚至显示为方框。对于关键图标我最终换成了内联SVG虽然代码量多了点但显示一致性有保障。第三个坑是localStorage的key命名冲突。如果你在同一个域名下部署了多个纯前端应用都用data作为key就会互相覆盖。我的命名习惯是加上项目前缀比如nav_data、nav_backup_1避免冲突。第四个坑是忘记处理空状态。当用户删除了所有链接页面应该显示一个引导添加的提示而不是一片空白。这个细节很小但对新用户的体验影响很大。6. 后续扩展与个人体会这个导航页目前的功能已经能满足我的日常需求但还有几个方向可以继续折腾。一个是多设备同步纯前端做不了真正的实时同步但可以用文件同步工具比如把导出的JSON放在同步盘里来曲线实现。另一个是快捷键支持比如按/聚焦搜索框、按Esc清空搜索、按数字键快速跳转分类这些用keydown事件监听就能实现。我还试过把导航页做成PWA渐进式Web应用加上manifest和service worker后可以安装到桌面离线也能打开。这个改动大概花了半小时体验提升很明显推荐有需要的人试试。最后分享一个使用AI生成代码的心得不要指望一次生成就完美。我的习惯是让AI生成第一版然后自己跑一遍把问题列出来再让AI针对具体问题修改。这种“生成-测试-反馈-再生成”的循环比一次性写一个超长提示词效果更好。AI的优势是快人的优势是判断两者结合才能做出真正可用的东西。