“用JavaScript写HTML?你们Facebook的人是不是疯了?!”
2015年的某天,林小凡在技术论坛上第一次看到React的JSX语法时,发出了和全世界传统前端开发者一样的灵魂质问。
屏幕上那段代码像基因突变的怪物:
jsx
function Welcome() {
return <h1 className="title">Hello, World!</h1>;
}
“这算什么?HTML标签直接写在JavaScript里?!”他猛拍键盘,“这违反了关注点分离原则!这亵渎了Web标准!”
墨尘的消息幽幽浮现:
“三年前Angular刚出来时,你也说过类似的话。”
第一节:虚拟DOM的魔法
被迫接手一个React项目时,林小凡依然满腹怀疑。
直到他亲眼见证了这个场景——
在传统的jQuery项目里,一个简单的“点赞按钮”需要:
javascript
// 1. 获取DOM元素
const button = $('#like-btn');
const counter = $('#like-count');
// 2. 绑定事件
button.on('click', function() {
// 3. 发起请求
$.post('/api/like', function(res) {
// 4. 手动更新DOM
counter.text(res.likes);
button.toggleClass('active');
});
});
而React的版本却是:
jsx
function LikeButton() {
const [liked, setLiked] = useState(false);
const [count, setCount] = useState(0);
const handleClick = () => {
fetch('/api/like', { method: 'POST' })
.then(res => res.json())
.then(data => {
setLiked(!liked);
setCount(data.likes);
});
};
return (
<button
className={liked ? 'active' : ''}
onClick={handleClick}
>
点赞 {count}
</button>
);
}
“这不就是把逻辑和UI混在一起了吗?!”林小凡刚说完,突然意识到——
当点击发生时,他完全不需要思考DOM操作。
React像有个隐形的机器人,自动帮他处理了所有界面更新。
第二节:JSX的认知革命
真正让林小凡三观碎裂的是组件化开发。
那个React项目里有个CommentList
组件:
jsx
function CommentList({ comments }) {
return (
<div className="comment-list">
{comments.map(comment => (
<CommentItem
key={comment.id}
author={comment.author}
content={comment.text}
/>
))}
</div>
);
}
配套的CommentItem
:
jsx
function CommentItem({ author, content }) {
return (
<div className="comment">
<Avatar user={author} />
<div className="content">{content}</div>
</div>
);
}
“这不就是乐高积木吗?!”他对着屏幕自言自语。
突然理解了为什么React宣传语是**"Learn Once, Write Anywhere"**——
组件像函数一样接收参数(props),返回UI描述(JSX),然后组合成完整应用。
墨尘适时补刀:
“现在知道为什么Angular的ng-include
看起来像石器时代产物了吧?”
第三节:Hooks的降维打击
当林小凡还在钻研class组件
的componentDidMount
时,世界已经转向了Hooks。
他永远记得第一次看到这个代码时的震撼:
jsx
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setSeconds(s => s + 1);
}, 1000);
return () => clearInterval(id);
}, []);
return <div>已运行: {seconds}秒</div>;
}
“等等!”他疯狂翻阅文档,“生命周期呢?this
呢?bind
呢?”
那些在类组件里需要小心翼翼处理的componentWillUnmount
和this.setState
,现在被简化为:
useState
管理状态useEffect
处理副作用- 不需要
class
,不需要this
“这太反直觉了!”林小凡刚抱怨完,却发现自己已经写了三个函数组件,根本回不去了。
第四节:Redux的过载
项目引入Redux的那天,林小凡经历了认知过载。
他盯着这个目录结构发呆:
/src
/actions
- userActions.js
/reducers
- userReducer.js
/store
- configureStore.js
/components
- UserProfile.js
为了改个用户头像,代码要穿越四个文件:
- 组件里
dispatch(changeAvatar(url))
- action里定义
CHANGE_AVATAR
常量 - reducer里处理状态变更
- store里配置中间件
“就改个头像而已啊!”他对着combineReducers
的文档哀嚎。
直到发现useSelector
和useDispatch
:
jsx
function UserProfile() {
const avatar = useSelector(state => state.user.avatar);
const dispatch = useDispatch();
const handleUpload = (file) => {
dispatch({ type: 'CHANGE_AVATAR', payload: file.url });
};
return <img src={avatar} onClick={handleUpload} />;
}
“早该这样了!”他感动得差点亲吻屏幕。
第五节:Next.js的救赎
当林小凡被SEO问题折磨时,Next.js像救世主降临。
从纯React SPA迁移到Next.js只需要:
- 把
App.js
改成pages/_app.js
- 把路由文件删掉(Next.js自动路由)
- 享受开箱即用的:
- 服务端渲染
- 静态生成
- API路由
最神奇的是这个:
jsx
export async function getServerSideProps() {
const res = await fetch('https://api.example.com/data');
return { props: { data: res.json() } };
}
function Page({ data }) { // 数据在服务端就注入了!
return <div>{data.title}</div>;
}
“这不就是当年PHP的做法吗?!”林小凡突然有种历史轮回的眩晕感。
终节:新时代的曙光
某个加班的深夜,林小凡在Git记录里发现一段2016年的注释:
jsx
// 这个React组件可能太激进了
// 但未来一定是函数式组件的天下
// ——留给后来者
窗外,月光照在最新版的React文档上,Server Components
和Suspense
等新特性闪烁着微光。
他突然理解了React团队的野心——
不是做一个框架,而是重新定义UI开发的范式。
(第九章 完)
下一章预告:
第十章:Vue的温柔陷阱——渐进式的魅力
“当React和Angular在打架时,Vue悄悄偷走了开发者的心。”——某转职Vue的React程序员