2026-01-05 // web · 移动端 · sibuchen
ZHDR
ZHDR - 知乎日报(移动端 Web 应用)
1. 项目概述
知乎日报是一款面向移动端的新闻资讯阅读应用,基于知乎日报公开 API,实现了新闻浏览、详情阅读、用户登录、收藏、点赞、评论等完整功能。项目采用 Vue 3 全家桶开发,注重移动端适配体验与交互细节。
项目类型: 移动端 SPA(Single Page Application)
项目规模: 17 个源文件,7 个页面视图,3 个状态管理模块
2. 技术栈
| 分类 | 技术选型 |
|---|---|
| 前端框架 | Vue 3(Composition API + <script setup>) |
| 构建工具 | Vite |
| 状态管理 | Pinia |
| 路由管理 | Vue Router 4 |
| UI 组件库 | Vant 4(移动端) |
| HTTP 请求 | Axios(封装拦截器) |
| 样式方案 | SCSS + CSS 变量(主题系统) |
| 移动端适配 | amfe-flexible + postcss-pxtorem(px 自动转 rem) |
3. 项目结构
src/
├── main.js # 应用入口
├── App.vue # 根组件
├── router/
│ └── index.js # 路由配置(7 条路由,全部懒加载)
├── request/
│ └── index.js # Axios 实例封装(拦截器 + Loading 管理)
├── stores/
│ ├── user.js # 用户状态(登录态、收藏、点赞、评论)
│ ├── theme.js # 主题状态(亮色/暗黑模式)
│ └── counter.js # 计数器状态
├── views/
│ ├── Home/ # 首页(轮播 + 新闻列表 + 无限滚动)
│ │ ├── Home.vue
│ │ ├── Head.vue # 粘性导航栏(滚动毛玻璃效果)
│ │ └── NewsItem.vue # 新闻卡片组件
│ ├── Detail/ # 新闻详情页(核心页面)
│ │ └── Detail.vue
│ ├── Login/ # 登录页(SVG Logo + 动效)
│ │ └── Login.vue
│ ├── Personal/ # 个人中心
│ │ └── Personal.vue
│ ├── Favorite/ # 我的收藏
│ │ └── Favorite.vue
│ ├── Likes/ # 我的点赞
│ │ └── Likes.vue
│ └── Comments/ # 我的评论
│ └── Comments.vue
└── assets/
├── images/
└── styles/
└── theme.css # 暗黑模式全局样式
4. 架构设计
4.1 路由与页面导航
采用 Vue Router 4 的 createWebHistory 模式,所有路由组件均使用 动态 import(懒加载),减少首屏加载体积:
/ → Home(首页新闻列表)
/detail/:id? → Detail(新闻详情)
/personal → Personal(个人中心)
/login → Login(登录页)
/favorite → Favorite(我的收藏)
/likes → Likes(我的点赞)
/comments → Comments(我的评论)
页面导航关系:首页通过新闻卡片进入详情页,个人中心可跳转至收藏/点赞/评论子页面,详情页内可跳转至登录页。
4.2 状态管理(Pinia)
使用 Pinia 的 Composition API 风格(defineStore + setup 函数),划分为三个独立 Store:
user.js — 用户核心状态
userInfo:用户信息(手机号、头像、登录时间)favorites:收藏的新闻 ID 列表likes:点赞的新闻 ID 列表likeCounts:点赞计数(使用reactive({})解决动态 key 响应式问题)myComments:用户评论列表(含新闻 ID、标题、内容、时间戳)- 所有状态变更后即时同步写入
localStorage,实现持久化
theme.js — 主题状态
- 优先读取
localStorage缓存,无缓存时检测系统偏好prefers-color-scheme - 通过
document.documentElement.setAttribute('data-theme', ...)切换 CSS 变量
4.3 网络请求层
对 Axios 进行统一封装(request/index.js):
- 请求计数器模式管理 Loading:维护
requestCount计数器,多个并发请求时只显示一次 Loading Toast,所有请求结束后才关闭,避免 Loading 闪烁 - 支持
skipLoading配置:通过config.skipLoading跳过特定请求的 Loading 显示 - 请求拦截器:自动展示全局 Loading
- 响应拦截器:自动关闭 Loading、解包
response.data、处理 401 未授权状态 - 超时控制:6 秒超时
- API 代理:Vite 开发服务器将
/api代理至https://news-at.zhihu.com/api/4,解决跨域问题
4.4 移动端适配方案
- rem 适配:
amfe-flexible动态计算根字体大小 +postcss-pxtorem(rootValue: 75)自动将 px 转换为 rem - Vite 插件:
unplugin-auto-import实现 Vue、Vue Router、Pinia API 的自动导入,减少样板代码 - Vant 组件库:提供符合移动端交互规范的 UI 组件
5. 核心功能与技术实现
5.1 首页新闻流(Home)
- 轮播推荐:使用
van-swipe展示top_stories热门新闻,3 秒自动轮播 - 无限滚动加载:使用
van-list组件的@load事件,滚动到底部自动请求前一天新闻(/api/news/before/{date}),通过维护date链实现按日期倒序的无限加载 - 骨架屏:初始加载时展示带 shimmer 动画的骨架屏,避免白屏
- 粘性导航栏(Head.vue):
position: sticky固定顶部,监听scroll事件实现滚动后毛玻璃背景效果(backdrop-filter: blur)
5.2 新闻详情页(Detail)
详情页是功能最复杂的页面,包含以下技术要点:
- 动态样式注入:知乎 API 返回的新闻包含外部 CSS 链接,通过
document.createElement('link')动态注入到<head>,并在页面卸载时(onUnmounted)清理,防止样式污染 - 图片占位处理:知乎新闻正文的首图通过
.img-place-holder占位元素定位,使用Image对象预加载后插入 DOM,处理加载失败的降级逻辑 - HTML 正文渲染:使用
v-html渲染 API 返回的 HTML 格式正文内容 - 点赞粒子动画:点赞时触发
createParticleEffect,在点赞按钮周围生成多个随机位置、旋转、缩放、透明度的粒子元素,通过requestAnimationFrame驱动动画循环,实现视觉反馈 - 评论弹窗:使用
van-popup底部弹出层,支持发表评论、查看评论列表、删除自己的评论,未登录时引导跳转登录页 - 收藏/点赞状态:通过
computed响应式读取 Pinia store 中的状态,点赞计数合并 API 返回的基础计数与用户本地增量
5.3 登录页(Login)
- SVG 组件化 Logo:使用 Vue 的
h()渲染函数动态创建 SVG 知乎 Logo,无需外部图片资源 - 登录表单动画:点击"手机号登录"后,Logo 区域通过 CSS
transform: translateY上移,表单区域使用transition组件滑入,底部按钮区域绝对定位 - 验证码倒计时:模拟发送验证码流程,60 秒倒计时,自动填入随机 4 位验证码(演示用途)
- 手机号正则校验:
/^1[3-9]\d{9}$/验证中国大陆手机号格式 - 视觉效果:渐变背景 + 浮动装饰元素 + 毛玻璃 Logo 容器 + 呼吸灯动画
5.4 个人中心(Personal)
- 用户统计面板:展示收藏、点赞、评论数量,可点击跳转对应列表页
- 暗黑模式切换:
van-switch绑定themeStore.isDark,切换时同步更新 DOM 属性和 localStorage - 退出登录:
showConfirmDialog二次确认后清除用户状态
5.5 收藏/点赞/评论列表
- 分批加载策略:列表页仅存储新闻 ID,通过
Promise.all并发请求每批 5 条新闻详情,结合van-list的无限滚动实现渐进加载,避免一次性请求过多接口 - 响应式监听:
watch监听 store 中对应数组的变化,数据变更时自动重新加载列表 - 相对时间显示:评论时间格式化为"刚刚/X分钟前/X小时前/X天前"的相对时间
5.6 暗黑模式系统
- CSS 变量方案:在
theme.css中通过[data-theme="dark"]选择器覆盖 Vant 组件的 CSS 变量(背景色、文字色、边框色等) - 组件级适配:各页面样式中使用
var(--van-xxx, fallback)引用变量,确保主题切换时全局生效 - 系统偏好检测:首次访问时通过
window.matchMedia('(prefers-color-scheme: dark)')检测系统主题偏好
6. 技术亮点总结
- 请求并发管理:请求计数器模式解决多并发请求下 Loading 闪烁问题
- 动态资源注入/清理:详情页外部 CSS 的动态注入与卸载清理,避免样式污染
- 响应式数据设计:
reactive({})解决 Pinia 中动态 key 对象的响应式追踪问题 - 粒子动画系统:基于
requestAnimationFrame的点赞粒子特效,无第三方动画库依赖 - 分批并发加载:收藏/点赞列表的
Promise.all+ 分页策略,平衡加载速度与接口压力 - 主题系统:CSS 变量 + data 属性的轻量级暗黑模式方案,覆盖 Vant 组件默认样式
- 移动端适配:rem 方案 + Vant 组件 + 视口配置,完整的移动端适配链路
- SVG 组件化:使用 Vue 渲染函数创建 SVG,零图片依赖