在前面的章节已经介绍了 Next.js 是 file-based routing 的架构,在路由至其他页面时,通常会使用到 <Link />
这个 component,这个 component 提供不少的 props 可以针对不同的情况做设定,今天我们要了解 prefetch
这个 props 的功用,它可以让切换页面更有效率。
要注意的是
prefetch
只在 production 环境有用!
也许你会需要修改 global.css: https://gist.github.com/leochiu-a/5276029a65017c660bb91dcba6bab53c
一个很常见的起手式,我们使用 create-next-app
以 typescript 启动一个 Next.js 的专案:
$ npx create-next-app --typescript
创建完後,你可以在 pages/
这个资料夹中看到类似以下的资料夹结构,原本预设的档案没有 products.tsx
,这是後来加上去的,我们将使用 /
与 /products
两个页面来做实验:
我们将 pages/index.tsx
的内容改成以下这个模样,首页中的内容只有包含一个 <Link>
,可以点击路由到 /products
页面。在前面的章节介绍过 <Link>
里面的 children 只能是 string
或是 HTML 的 <a>
,如果使用其他的 tag 则会报错:
import type { NextPage } from "next";
import Link from "next/link";
const Home: NextPage = () => {
return (
<Link href="/products">
<a>link to products</a>
</Link>
);
};
export default Home;
接着看到 pages/products.tsx
的内容,它是一个 SSR 的页面,透过 getServerSideProps
传递一个 products
的字串,并在 component 中渲染这个字串,非常地单纯:
interface Props {
products: string;
}
const Products = ({ products }: Props) => {
return <div>{products}</div>;
};
export const getServerSideProps = async () => {
return {
props: { products: "products" },
};
};
export default Products;
各位读者要先知道「 prefetch
只在 production 环境有用」,亦即使用 yarn dev
启动开发用服务器什麽事情都不会发生,我们要使用 yarn build
与 yarn start
将 Next.js 应用打包後,再启动服务器:
$ yarn build
$ yarn start
接着你可以在浏览器中看到 [http://localhost:3000](http://localhost:3000)
中的首页内容如下:
使用 Chrome 的 devtool 看看 /prodcuts
的 chunk 是不是已经被预先载入了:
cmd + option + i
)你可以看到 /products
的 chunk 已经被预先下载,而且 Size 的栏位是 prefetch cache,意思是会储存在 Chrome 的 prefetch cache 中:
/products
的 chunk 可以预先载入?Next.js 之所以可以做到这件事要归功於 HTML 的 <link>
,在 <head>
加上以下这种写法,可以「预先载入未来可能被用到的资源」:
<link rel="prefetch" href="/images/big.jpeg" />
在 pages/index.tsx
这个页面中可以看到加入了 <Link href='/products'>
的内容,而 Next.js 知道使用者可能会点击连结切换到 /products
这个页面,所以便让这个页面成为会被「prefetch」的资源。
/products
页面被 prefetch在 Next.js 9 以後,使用 <Link>
都会被预设加入到 prefetch 的资源中,如同上面看到的例子,虽然我们没有在 <Link>
传入 prefetch={true}
,但是 /products
这个页面仍然会被加入到 prefetch 的资源中。
但是有时候并不是所有使用 <Link>
的页面都需要被 prefetch,所以很直觉地在 <Link>
上传入 prefetch={false}
,将会使得该页面不会被 prefetch。
我们以 pages/index.tsx
这个页面中的 <Link>
为例,修改页面中的内容:
<Link href="/products" prefetch={false}>
<a>link to products</a>
</Link>
同样执行 yarn build
与 yarn start
後,浏览该页面的 Elements tab,会发现在 <head>
中找不到 /products
被预先载入的讯息。
当然,如果再切换到 Network tab,就看不到 /products
的 chunk 被载入。
<Link>
会触发 prefetch 吗?在页面上总是会有一些 <Link>
并非是一开始就渲染在页面上,而是使用者跟页面互动过後,达成某些条件才渲染在画面上,读者们也许就会有这样的问题「条件式渲染的 <Link>
也可以让页面的 chunk 被 prefetch 吗?」
我们来实验看看,修改 pages/index.tsx
中的内容,以条件式渲染的方式显示 <Link>
,在使用者点击按钮後, <Link>
才会出现在画面上:
const Home: NextPage = () => {
const [visible, setVisible] = useState(false);
return (
<div>
{visible && (
<Link href="/products">
<a>link to products</a>
</Link>
)}
<button onClick={() => setVisible(true)}>show link</button>
</div>
);
};
同样执行 yarn build
与 yarn start
後,浏览该页面的 Elements tab,会发现在 <head>
中找不到 /products
被预先载入的讯息。
但是,在点击「show link」的按钮之後,你会发现 <head>
中动态载入了以下内容,让 /prodcuts
的 chunk 成为会被预先载入的资源:
此时再打开 Network tab,原本没有预先载入的 products chunk 资源,也在 <link>
被设定後被载入储存到 prefetch cache 中。
router.push
的自定义路由也能够 prefetch 吗?在有些情况 <Link>
也许不能够满足需求,必须使用 next/router
的 useRouter
在切换页面之前执行一些操作,例如验证表单、 GA 事件等等,在这种情况要怎麽 prefetch 页面呢?
可以使用 router.prefetch
将指定的页面作为 prefetch 的页面,这种做法跟 conditional rendering 的状况有些相似,从「检视原始码」中会发现原本的页面中是不包含 <link rel="prefetch">
的资源,但是在页面载入之後, router.prefetch
再动态地指定 prefetch 的资源,让浏览器自动预先抓取资源。
const router = useRouter();
useEffect(() => {
router.prefetch("/products");
}, [router]);
要特别注意的是,
router.prefetch
在yarn dev
下不会起作用。
以下为 pages/index.tsx
的范例程序,在页面载入时动态地 prefetch /products
的页面 chunk,在点击按钮後触发 handleClick()
後,此时 /products
页面已经被预先载入,所以就可以有效缩减 /products
的页面载入时间
const Home: NextPage = () => {
const router = useRouter();
useEffect(() => {
router.prefetch("/products");
}, [router]);
const handleClick = () => {
router.push("/products");
};
return (
<div>
<button onClick={handleClick}>to /products</button>
</div>
);
};
在这篇文章中,我们了解了 Next.js 的 prefetch 机制,并且知道 <Link>
在预设的情况下都会 prefetch 页面。如果是在 conditional rendering 的页面中,只要 <Link>
被渲染後,也可以动态地 prefetch 指定的页面。
而有时候 <Link>
无法满足我们的需求,会使用到 router.push
这种自定义路由的方式,此时可以使用 router.prefetch
达到 prefetch 页面的效果。
最後,再次提醒不论是 <Link>
或是 router.prefetch
,prefetch 这个特性只有在 production 的环境下才能使用, yarn dev
是无法触发 prefetch 资源的 。
<<: Day 18:产地直送,先拿再用-Vuex State、Getters
>>: Day 18 Docker Compose 简介和安装
Day 13 - 半自动标签图片的方法与实作 以下介绍一个自制的简易的半自动标签图片的作法,主要是因...
连续 30 天不中断每天上传一支教学影片,教你如何用 React 加上 Firebase 打造社群...
我们写的脚本不仅仅是自己使用,有时需要分享给别人使用。这种情况下,帮助信息可以更好地帮助使用者,使用...
在昨天我们学会了 code splitting 与 dynamic import 的技巧,让程序在...
今天大概会聊到的范围 basic layout arrangement & alignme...