先别急着写业务逻辑!打好地基 + 精准还原设计稿,才是专业前端的第一步
🌍 开头:为什么我的旅游 App 一启动就“赢了”?
那天,我刷着 Airbnb 的丝滑轮播、小红书的瀑布流、Google Travel 的极简 UI,突然冒出一个念头:
“如果我能用 React 从零复刻一款高保真旅游 App,再悄悄加上 AI 聊天、智能推荐……那简历上是不是能多一行‘亮点项目’?”
于是,TripApp 诞生了。
但和大多数人不同,我没有一上来就写 <div>首页</div>,而是只做两件事:
- 搭一个让面试官眼前一亮的工程骨架
- 确保设计师甩来的 750px 稿,1px 都不差地还原
因为我知道——细节,才是专业和业余的分水岭。
🛠️ 第一步:克制地选择技术栈,拒绝“全家桶焦虑”
很多人一建项目就狂装库:Redux、Antd Mobile、Tailwind、Webpack、Sass……结果项目跑起来慢如蜗牛,自己都搞不清哪个是核心依赖。
而我,只选了这几样“趁手兵器”:
- React + Hooks + Router(SPA 核心)
- Zustand(轻量状态管理,API 简洁到哭)
- react-vant(70% UI 组件开箱即用)
- Vite(快如闪电的开发体验)
- lib-flexible + postcss-pxtorem(精准适配移动端)
- Axios + Mock(前后端并行开发)
- stylus(CSS 预处理器,支持嵌套 & 变量)
为什么不用 Redux?Zustand 一行
create就搞定状态,还支持 TypeScript 推导。
为什么选 react-vant?它的组件风格接近 iOS/Android 原生,且体积小、文档全。
技术不是越多越好,而是“刚好够用”。
🗂️ 项目结构:让代码自己会说话
我的 src 目录长这样:
src/
├── components/ // 通用组件(Waterfall, Loading, SearchBox...)
├── pages/ // 页面(Home, Search, Detail, Account...)
├── stores/ // Zustand 状态(useImageStore, useSearchStore...)
├── hooks/ // 自定义 Hooks(useTitle, useDebounce...)
├── api/ // 接口封装(getDetail, getImages...)
├── mock/ // 模拟数据(开发无需后端)
├── llm/ // LLM 封装(chat, kimiChat, generateAvatar...)
├── styles/ // 全局样式(可选)
└── App.css // 全局样式 + 原子类
这种分层不是炫技,而是为了让三个月后的我(或接手的同事)一眼看懂:“哦,搜索逻辑在
stores/useSearchStore.js,改这里就行。”
⚙️ Vite 配置:藏在 .env 里的小心机
我在 .env.local 里放了这些(已加入 .gitignore):
VITE_DEEPSEEK_API_KEY=sk-xxxx
VITE_KIM_API_KEY=sk-yyyy
然后在 llm/chat.js 中安全调用:
const api_key = import.meta.env.VITE_DEEPSEEK_API_KEY;
面试官问:“你怎么管理敏感密钥?”
我微微一笑:“Vite 的import.meta.env在构建时自动替换,根本不会打包进前端代码。”
同时,vite.config.js 里配置了 alias、mock、CSS 预处理:
// vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import pxtorem from 'postcss-pxtorem'
export default defineConfig({
plugins: [react()],
resolve: {
alias: { '@': path.resolve(__dirname, 'src') }
},
css: {
preprocessorOptions: {
stylus: { /* ... */ }
},
postcss: {
plugins: [
pxtorem({ rootValue: 37.5, propList: ['*'] })
]
}
}
})
📱 移动端适配:设计师说“1px都不能差”,我笑了
设计师甩来一张 750px 宽度 的 Figma 稿(iPhone 6/7/8 标准),要求“像素级还原”。
我知道,直接写 px 是死路一条——在 iPhone 14 Pro Max 上会小得看不见,在小米低端机上又会撑爆屏幕。
于是,我祭出阿里开源的 lib-flexible + postcss-pxtorem 组合拳:
Step 1:安装并引入 lib-flexible
npm install lib-flexible
在 main.js 顶部引入:
import 'lib-flexible'
它会在页面加载时动态设置:
// 例如:屏幕宽度 375px → html { font-size: 37.5px }
// 因为 375 / 10 = 37.5
Step 2:配置 postcss-pxtorem
// postcss.config.js
module.exports = {
plugins: {
'postcss-pxtorem': {
rootValue: 37.5, // 750 设计稿 / 2 (dpr) / 10 = 37.5
propList: ['*'],
exclude: /node_modules/
}
}
}
现在,我在 CSS 里写:
.title {
font-size: 32px; /* 设计稿上是 32px */
margin: 20px 0;
}
Vite 构建时自动转成:
.title {
font-size: 0.85333rem;
margin: 0.53333rem 0;
}
在任何设备上,
1rem = 屏幕宽度 / 10,所以比例永远一致。
从此告别手动除以 75 的痛苦!
🎨 原子 CSS:让样式复用率提升 80%
除了模块化 CSS(.module.css),我在 App.css 里还定义了一些原子类:
/* App.css */
.mt4 { margin-top: 0.4rem; }
.flex { display: flex; }
.flex-col { flex-direction: column; }
.flex-1 { flex: 1; }
.h-all { height: 100vh; }
.text-center { text-align: center; }
于是,在组件里可以这样写:
<div className="flex flex-col h-all mt4">
<Header />
<Main />
</div>
不用重复写
display: flex,也不怕样式污染。
原子类 + module.css,是我目前最舒服的 CSS 组合。
🧭 结尾:地基已打,旅程刚开始
现在,我的 TripApp 还没有用户登录、没有真实行程、没有 AI 生成头像……但它已经有了:
- 清晰的工程架构
- 专业的移动端适配方案
- 对设计稿的极致尊重
- 安全的密钥管理
- 高复用的样式体系
这就像旅行前收拾行李:护照、充电器、转换插头、防晒霜都齐了,只等买机票出发。
下一站,我会带你深入“路由与 TabBar”的实现——如何让底部导航高亮精准匹配当前路径?如何用 Layout + Outlet 实现多模板 SPA?