最近Next.js在前端圈很火,比如ChatGPT官网都是用Next写的,甚至连React新官网都指定新项目的脚手架为Next。刚好前段时间做的一个公司项目用到了Next,但由于当时时间仓促,没有仔细研究,只是大致翻阅了下Next官网。现在终于得空静下心来。
我有一个习惯是学习新东西前,都喜欢先找这个东西做出来的成品,找了半天,要么不开源要么都是些不成熟的速成ai项目。无意间翻到React官网,看到居然也是用Next搭的,顿时眼前一亮,真是踏破铁鞋无觅处。
于是乎就打开了React.dev的git仓库,从头到尾捋它一遍。
看一个开源项目,首先要看他的整体文件目录:
大体上,这个目录结构与普通的React项目相似,但也有一些显著的不同之处:
- 路由方式的不同:React使用useRouter,而Next使用动态文件目录路由,可以理解为pages下的每一个文件,Next会自动生成一个路由,例如:pages下的[[...markdownPath]].js,这是一个动态目录,每个md文件都有其对应的路由。
- 数据获取与渲染:Next.js采用服务端渲染,这使得React.dev项目中有大量的工具和函数用于处理服务器上预渲染的内容。
- 图片和字体的优化:Next.js为图片和字体提供了一系列的优化策略,这也是React.dev项目中的一个重要部分。
值得注意的是,由于Next.js使用服务端渲染,React.dev项目在处理服务器返回的JSON反序列化上进行了大量优化。例如,当渲染Layout组件时,它会先解析服务端预渲染的JSON,并且自定义了一个reviver函数,然后使用useMemo来缓存解析结果,最后返回React元素进行渲染。
export default function Layout({content, toc, meta}) {
const parsedContent = useMemo(
() => JSON.parse(content, reviveNodeOnClient),
[content]
);
const parsedToc = useMemo(() => JSON.parse(toc, reviveNodeOnClient), [toc]);
...
return (
<Page toc={parsedToc} routeTree={routeTree} meta={meta} section={section}>
{parsedContent}
</Page>
);
}
// JSON反序列化客户端React元素树
function reviveNodeOnClient(key, val) {
if (Array.isArray(val) && val[0] == '$r') {
// 假定是一个React元素
let type = val[1];
let key = val[2];
let props = val[3];
if (type === 'wrapper') {
type = Fragment;
props = {children: props.children};
}
if (MDXComponents[type]) {
type = MDXComponents[type];
}
if (!type) {
console.error('Unknown type: ' + type);
type = Fragment;
}
return {
$$typeof: Symbol.for('react.element'),
type: type,
key: key,
ref: null,
props: props,
_owner: null,
};
} else {
return val;
}
}
未完待续....
总的来说,通过深入研究React.dev项目,我对Next.js有了更深入的了解。如果你也对Next.js感兴趣,我强烈建议你也去看一下这个项目。