背景
这周开始尝试阅读 Next.js 源码,有难度,但也很有意思。
对我来说,一个能够极大提高效率的技巧就是:每次先抛出一个自己感兴趣的小问题(多是关于实现原理的细节),然后阅读源码,尝试回答这个问题。解决疑问之后,再放下源码,抛出下一个感兴趣的问题(很可能是解决了上一个问题所引发的新问题),再阅读源码,如此循环往复。
这样的好处是,每次阅读源码都有一个清晰且易实现的目标(解答当前问题),不易陷入无关细节。如此反复数次,解决一个又一个问题,对实现原理的理解自然层层加深了。
首先从 Next.js 的 Link 与 Router 着手,因为感觉这一部分比较直观,好上手;而且还因为我刚写了一篇 一箭双雕:浅谈 Next.js 页面导航(page navigation)原理,和这个关系也比较大。
本篇涉及的源码文件:
packages/next/client/link.tsx # Link 的实现
注:本系列文章基于 Next.js v12.0.4 版本。
热身:a 链接 的 preventDefault
第一个问题非常简单。在上一篇文章 一箭双雕:浅谈 Next.js 页面导航(page navigation)原理 中我提到,Next.js 中每个 <Link />
下都有一个 <a>
元素,用户正常点击的时候 <a>
不会生效,但在 JS 禁用的时候(例如对于不允许 JS 的爬虫来说) <a>
就能派上用场了。
下面通过源码印证一下,正常情况下 Next.js 真的会让 <a>
失效。
我们平常使用的 <Link />
组件,其定义在 packages/next/client/link.tsx
的第 109 行。这里略去大部分无关代码,截取其中最重要的一段做注释:
代码片段 1:Link
// packages/next/client/link.tsx
// 第 109 行
function Link(props: React.PropsWithChildren<LinkProps>) {
// 前面代码略,从第 277 行开始
const childProps: {
onMouseEnter?: React