从零开始构建一个现代化前端项目:一个iOS老炮的“被迫转型”记
去年双11前夜,我正窝在成都太古里的咖啡馆里,一边啃着兔头一边给公司App修复一个诡异的内存泄漏——是的,我是个做了6年iOS开发的老兵,见证了Swift从2.0一路卷到5.x,也亲手把Objective-C项目一点点迁移到SwiftUI。本以为这辈子就跟UIKit和Combine锁死了,结果领导一句话:“小张啊,咱们要搞个Web端的区块链数据看板,你不是学得快嘛?下个月上线。”
我当时差点把豆花饭喷出来。区块链?React?我不是连npm install都只在帮同事看报错时才敲过吗?
但没办法,成都的生活节奏虽舒服,可跳槽市场不等人。翻了翻BOSS直聘,清一色写着“熟悉React/Vue,有区块链项目经验优先”。行吧,为了简历上多一行“全栈能力”,硬着头皮上。
需求来了:一个“看起来很酷”的区块链浏览器
产品需求很简单(他们总这么说):用户输入一个钱包地址,就能看到该地址在以太坊上的所有交易记录、NFT持仓、代币余额,还得支持实时更新。UI要“赛博朋克风”,加载不能卡,最好还能分享截图。
我内心OS:你当我是Tony Stark啊?
但吐槽归吐槽,活儿得干。我决定从零搭一个现代化前端项目,目标是:结构清晰、性能拉满、部署丝滑、代码能让人看懂——毕竟以后可能还要交接给真正的前端同事(别让他们骂我祖宗十八代)。
第一步:脚手架选型,别被生态吓死
React 生态简直比 iOS 的 Cocoapods + Swift Package Manager + Carthage 混合使用还乱。Vite?Create React App?Next.js?Remix?
我翻了 GitHub 上几个热门开源区块链项目(比如 Etherscan 的简化版),发现大多数新项目都用 Vite + TypeScript + Tailwind CSS 这套组合拳。理由很实在:
- Vite 启动快得离谱,改一行代码秒级热更新,再也不用等 Webpack 像煮泡面;
- TS 能避免我这种 JS 新手写出
undefined is not a function的史诗级 Bug; - Tailwind 写 UI 快,而且“赛博朋克风”无非就是紫蓝渐变 + 发光字体,Tailwind 配个
text-transparent bg-clip-text bg-gradient-to-r from-purple-500 to-blue-500就搞定。
于是初始化:
npm create vite@latest crypto-explorer -- --template react-ts
cd crypto-explorer
npm install
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
然后按官方文档配好 tailwind.config.cjs 和 postcss.config.cjs,把 main.tsx 里引入 Tailwind 的 CSS。搞定,骨架有了。
第二步:状态管理?先别急着上 Redux
很多新手(包括曾经的我)一上来就搞 Redux Toolkit,结果写了一堆 boilerplate,其实项目根本不需要全局状态。
我的策略是:能用 React Context + useReducer 解决的,绝不引入第三方库。这个项目的核心状态就两个:当前查询的钱包地址、以及对应的链上数据(transactions, tokens, nfts)。
// context/BlockchainContext.tsx
import { createContext, useContext, useReducer } from 'react';
type State = {
address: string | null;
loading: boolean;
data: BlockchainData | null;
};
type Action =
| { type: 'SET_ADDRESS'; payload: string }
| { type: 'FETCH_START' }
| { type: 'FETCH_SUCCESS'; payload: BlockchainData }
| { type: 'FETCH_ERROR' };
const initialState: State = { address: null, loading: false, data: null };
function reducer(state: State, action: Action): State {
switch (action.type) {
case 'SET_ADDRESS': return { ...state, address: action.payload };
case 'FETCH_START': return { ...state, loading: true };
case 'FETCH_SUCCESS': return { ...state, loading: false, data: action.payload };
case 'FETCH_ERROR': return { ...state, loading: false, data: null };
default: throw new Error('Unknown action');
}
}
const BlockchainContext = createContext<{ state: State; dispatch: React.Dispatch<Action> } | undefined>(undefined);
export function BlockchainProvider({ children }: { children: React.ReactNode }) {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<BlockchainContext.Provider value={{ state, dispatch }}>
{children}
</BlockchainContext.Provider>
);
}
export function useBlockchain() {
const context = useContext(BlockchainContext);
if (!context) throw new Error('useBlockchain must be used within BlockchainProvider');
return context;
}
简单、可控、没魔法。后续如果真需要复杂状态流(比如 WebSocket 实时推送),再考虑 Zustand 或 Jotai,它们比 Redux 轻量太多。
第三步:资源加载与性能优化——别让用户等成化石
区块链数据接口通常又慢又大。一次请求可能返回几百条交易记录,每条还带时间戳、gas fee、token symbol……直接渲染?页面直接卡死。
我用了三个招:
- 虚拟滚动(Virtualized List):用
react-window只渲染可视区域内的交易项。 - 懒加载 + 骨架屏:数据没回来前显示炫酷的骨架动画(Tailwind 配合
animate-pulse一行搞定)。 - 缓存机制:用
localStorage缓存最近查过的地址数据,加个 TTL(比如 5 分钟),避免重复请求。
// components/TransactionList.tsx
import { FixedSizeList as List } from 'react-window';
const TransactionRow = ({ index, style, data }: { index: number; style: React.CSSProperties; data: Transaction[] }) => (
<div style={style} className="border-b p-4 hover:bg-gray-50">
<div className="font-mono text-sm">{data[index].hash}</div>
<div className="text-green-500">+{data[index].value} ETH</div>
</div>
);
export const TransactionList = ({ transactions }: { transactions: Transaction[] }) => (
<List
height={600}
itemCount={transactions.length}
itemSize={80}
itemData={transactions}
>
{TransactionRow}
</List>
);
测试时,Chrome DevTools 的 Performance 面板跑出来 FPS 稳稳 60,产品经理看了直呼“这流畅度可以发朋友圈”。
第四步:区块链集成——别被 Web3.js 吓退
真正让我头疼的是连接区块链。早期项目常用 Web3.js,但现在社区更推荐 ethers.js —— API 更简洁,TypeScript 支持更好,bundle size 也小。
// utils/blockchain.ts
import { ethers } from 'ethers';
const provider = new ethers.JsonRpcProvider('https://mainnet.infura.io/v3/YOUR_KEY');
export async function fetchTokenBalance(address: string, tokenAddress: string) {
const contract = new ethers.Contract(
tokenAddress,
['function balanceOf(address) view returns (uint256)'],
provider
);
const balance = await contract.balanceOf(address);
return ethers.formatUnits(balance, 18); // 自动处理精度
}
Infura 免费额度够用,但如果用户量大了就得考虑 Alchemy 或自建节点。不过对我们这个 MVP 来说,够了。
第五步:部署上线——让运维大哥少骂两句
本地跑得欢,上线就翻车?我可吃过亏。这次学乖了:
- 用 Vercel 一键部署,免费、快、还带 Preview URL(PR 自动部署预览),产品经理再也不用催“你本地好了算什么,线上呢?”
- 所有 API Key 通过 Vercel 的环境变量注入,绝不提交到 Git
- 开启 Brotli 压缩 + CDN,首屏加载压到 1.2s 以内
// vercel.json
{
"headers": [
{
"source": "/(.*)",
"headers": [
{ "key": "Cache-Control", "value": "public, max-age=300" }
]
}
]
}
上周五晚上 11 点,终于点下 Deploy。刷新页面,紫蓝色界面流畅加载出我的钱包交易记录——那一刻,感觉比当年第一次用 Swift 写出 UITableView 还爽。
总结:iOS 老兵的前端初体验
这次“被迫转型”让我意识到:现代前端工程化程度其实和 iOS 不相上下。Vite ≈ Xcode 编译速度,TypeScript ≈ Swift 类型安全,Tailwind ≈ SwiftUI 声明式 UI。底层逻辑相通,只是语法糖不同。
如果你也在求职或想拓展技术栈,别怕从零开始。资源(教程、开源项目、Stack Overflow)多到爆炸,关键是要动手做个项目,哪怕是为了应付面试。
最后附上我的技术选型清单,供参考:
| 类别 | 选择 | 理由 |
|---|---|---|
| 构建工具 | Vite | 快,配置简单,HMR 秒级响应 |
| 状态管理 | React Context + useReducer | 轻量,够用,避免过度设计 |
| UI 框架 | Tailwind CSS | 快速实现设计稿,主题定制灵活 |
| 区块链 SDK | ethers.js | 简洁、TS 友好、社区活跃 |
| 部署平台 | Vercel | 免费、自动 HTTPS、Preview URL 神器 |
| 性能优化 | react-window + localStorage 缓存 | 避免卡顿,提升用户体验 |
现在,我的简历上多了“React 项目实战经验”,BOSS 直聘消息多了好几条。虽然可能还是回 iOS 岗,但至少下次团建,前端同事不会再笑我连 useState 都写错 hooks 顺序了。
对了,项目源码已开源(私信我拿链接),欢迎 Star —— 毕竟,谁不想在成都的慢生活里,悄悄卷赢别人呢?

评论 0