2023,学习Next.js最好的方式是深挖React新官网

692 阅读2分钟

最近Next.js在前端圈很火,比如ChatGPT官网都是用Next写的,甚至连React新官网都指定新项目的脚手架为Next。刚好前段时间做的一个公司项目用到了Next,但由于当时时间仓促,没有仔细研究,只是大致翻阅了下Next官网。现在终于得空静下心来。

我有一个习惯是学习新东西前,都喜欢先找这个东西做出来的成品,找了半天,要么不开源要么都是些不成熟的速成ai项目。无意间翻到React官网,看到居然也是用Next搭的,顿时眼前一亮,真是踏破铁鞋无觅处。

image.png 于是乎就打开了React.dev的git仓库,从头到尾捋它一遍。

看一个开源项目,首先要看他的整体文件目录:

image.png

大体上,这个目录结构与普通的React项目相似,但也有一些显著的不同之处:

  1. 路由方式的不同:React使用useRouter,而Next使用动态文件目录路由,可以理解为pages下的每一个文件,Next会自动生成一个路由,例如:pages下的[[...markdownPath]].js,这是一个动态目录,每个md文件都有其对应的路由。
  2. 数据获取与渲染:Next.js采用服务端渲染,这使得React.dev项目中有大量的工具和函数用于处理服务器上预渲染的内容。
  3. 图片和字体的优化: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感兴趣,我强烈建议你也去看一下这个项目。