Pages页面用法
1.1 概念介绍
在 Next.js 中,一个 page(页面) 就是一个从 .js、jsx、.ts 或 .tsx 文件导出(export)的 React 组件 ,这些文件存放在 pages 目录下。每个 page(页面)都使用其文件名作为路由(route)。
1.2 创建page页面
- 如果你创建了一个命名为
pages/about.jsx
的文件并导出(export)一个如下所示的 React 组件,则可以通过 /about 路径进行访问。function About() { return <div>About</div> } export default About
- 如果你创建了一个命名为
pages/home/index.jsx
的文件并导出(export)一个如下所示的 React 组件,则可以通过 /home 路径进行访问。function Home() { return <div>Home</div> } export default Home
- 注意:项目启动时默认加载的首页页面冲突问题:
当
src/app/page.jsx
默认首页文件存在的时候,此页面将作为http://localhost:3000
的默认启动页面。如果此时在pages
目录下又新建了index.jsx
页面,就会和src/app/page.jsx
中的页面产生冲突,导致首页无法正常加载。因此,两者只存在一个即可,另外一个相关文件要删除。
1.3 路由分类
-
Index routes:索引路由
路由将自动跳转到
pages
目录下名为index
的路由文件。比如:
pages/index.jsx → / pages/blog/index.jsx → /blog
-
Nested routes:嵌套目录路由
路由器支持嵌套文件。如果创建嵌套文件夹结构,文件仍将以相同的方式自动路由。
比如:
pages/blog/first-post.js → /blog/first-post pages/dashboard/settings/username.js → /dashboard/settings/username
-
Dynamic route segments:动态参数路由
使用
[]
括号语法,可以进行动态路由参数的匹配。比如:
pages/blog/[slug].js → /blog/:slug (/blog/hello-world) pages/[username]/settings.js → /:username/settings (/foo/settings) pages/post/[...all].js → /post/* (/post/2020/id/title)
1.4 声明式路由跳转页面
-
介绍
Next.js路由器允许您在页面之间进行客户端路由转换,类似于单页面应用程序。
-
修改
src/app/page.jsx
默认页面,通过字符串方式配置路由页面的跳转链接:import Link from "next/link"; function App() { return ( <ul> <li> <Link href="/home">主页</Link> </li> <li> <Link href="/profile">个人中心</Link> </li> </ul> ); } export default App;
- 上面的
ul
使用了多个href
链接。每一个(href)都映射到一个已知页面组件:
/home → pages/home/index.jsx /profile → pages/profile/index.jsx
- 上面的
-
通过url对象方式配置路由跳转:
<li> <Link href={{ pathname: "/home/[动态路由]", query: { a: 100 } }}>主页</Link> </li>
-
重定向路由实现页面跳转:
Link 组件默认将 url 推入路由栈中,生成一个路由跳转历史记录。你可以使用replace属性来重定向路由,防止生成路由历史记录。
<li> <Link replace href={{ pathname: "/home", query: { a: 100 } }}> 主页 </Link> </li>
-
Link组件下的children只能是单独的一个,而不能是多个子节点:
<Link href='/a'> <Fragment> <button>按钮</button> <button>按钮</button> </Fragment> </Link>
-
<Link />
组件API汇总:- href - 导航的目标路径或 URL,这是唯一一个必须设置的属性;
- as - 可选的路径装饰符,用于改变显示在浏览器地址栏中的URL格式。如果你在服务器端设置了自定义路由as将会起作用,否则页面切换会出现404情况;
- as 路由映射是指:比如有一个路由是
/post?id=2&articleId=199
,这样的路由看起来是不友好的。我们想要在浏览器地址栏中看到/post/2/199
,这样的路径。从前种方法到后种方法之间的转换就叫做路由映射。 - 使用
next
中的<Link />
组件提供的as属性,就可以实现路由格式的转换。 - as 是浏览器地址栏显示的path,并不是真正的path;而href才是真正的跳转路径。as只是改变了以下路由的外观,本质还是href。
- as 路由映射是指:比如有一个路由是
1.5 编程式路由跳转页面
-
介绍
除了使用声明式路由,还可以用
next/router
或者next/navigation
的编程式路由API实现客户端路由切换。 -
src/app/page.jsx
首页页面如果要配置编程式API进行路由跳转,需要使用:next/navigation
搭配"use client"
才可以正确使用useRouter,因为app/page.jsx
页面默认属于服务端组件了,而不是客户端组件。"use client"; // 必须要添加 import Link from "next/link"; import { useRouter } from "next/navigation"; function App() { const router = useRouter(); return ( <div> <button onClick={() => { router.push("/home"); }} > 主页 </button> </div> ); } export default App;
-
如果
src/app/page.jsx
使用next/router
则会报如下错误:You have a Server Component that imports next/router. Use next/navigation instead. Maybe one of these should be marked as a client entry "use client": ./src\app\page.jsx
-
如果是非
src/app/page.jsx
页面的其它路由组件,配置编程式导航,则使用next/router
,比如:src/pages/home/index.jsx
页面:import { useRouter } from "next/router"; export default function Home() { const router = useRouter(); return ( <div className="Home"> <h4>首页</h4> <button onClick={() => { router.back(); }} > 返回 </button> </div> ); }
-
const router = useRouter()
返回的router对象API汇总:- router.push(url, as, options):进入某个路由;第二个参数as,是为了装饰 URL ,如果你在服务器端设置了自定义路由将会起作用。
- router.replace(url, as, options):重定向路由;
- router.back():返回上一个路由;
- router.reload():重载当前路由;
- router.prefetch(url, as):预读取某个路由的组件内容;仅在生产环境下有效;
- router.beforePopState(cb):在路由器处理事件之前拦截
1.6 路由页面的预读取prefetch
-
介绍
Next.js 一个非常实用的功能就是你在浏览当前页面的时候,Next.js 会自动 prefetch 页面上所有的站内链接页面。这样一来,当你真正点击某个链接时,就可以迅速加载该页面,快得简直就像是在读取本地文件。
可能这就是为什么同样是 client-side navigation(客户端导航),Next.js 要比常规的 React SPA 的页面跳转快这么多吧! -
这个功能在
<Link />
中是默认开启的。除非像下面这样把<Link>
的 prefetch 置为 false,Next.js 就不会在当前页面,再去 prefetch 该路由:<Link href="/home" prefetch={false}> <a>主页</a> </Link>
prefetch 属性会在后台预读取目标路由页面,默认值为 true。它具有以下特性:
- 处于视口(viewport)中(初始或滚动后)的任何
<Link />
都将被预加载。通过设置 prefetch={false} 可以禁止页面预取。 - 当 prefetch 被设置为 false 时,鼠标悬停在
<Link />
上时仍然会触发预取。 - 使用 静态生成 的页面(pages)将预加载存有数据的 JSON 文件,以实现更快的页面切换。
- 预取功能只在生产环境中开启。
- 处于视口(viewport)中(初始或滚动后)的任何
-
router.prefetch(url, as, options) 编程式API进行页面预读取:
// 假设您有一个登录页面,登录后,您将用户重定向到仪表板。 // 对于这种情况,我们可以预取仪表板以进行更快的转换,如以下示例所示: import { useCallback, useEffect } from 'react' import { useRouter } from 'next/router' export default function Login() { const router = useRouter() const handleSubmit = useCallback((e) => { e.preventDefault() fetch('/api/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({}), }).then((res) => { if (res.ok) router.push('/dashboard') }) }, []) useEffect(() => { // 预读取 /dashboard 页面 router.prefetch('/dashboard') }, [router]) return ( <form onSubmit={handleSubmit}> <button type="submit">Login</button> </form> ) }
-
prefetch 的内容是如何存储的?
通过
<link rel="prefetch">
的方式,浏览器会请求相应的 .js 和 .css 文件,但不会执行它们,而是作为缓存把它们储存在浏览器中。等到用户真正跳转到该 route,需要加载这些文件时,就可以直接从本地加载,不需要请求服务器了,其实也是一种前端提高性能的常见方法。