如果你熟悉 react-router,你一定喜欢它提供单页路由。NextJs 也支持这一功能。

代码

这里,我们先定义一个 Nav.js 的组件,用于多个页面的公共导航部分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// ./components/route/Nav.js
import React from 'react';
import Link from 'next/link';

const InactiveLink = ({ path, name }) => (
<li>
<Link href={path}>
<a><strong>{name}</strong></a>
</Link>
</li>
);
const ActiveLink = ({ name }) => (
<li>
{name}
</li>
);

const Nav = ({ location }) => {
const navs = [
{ path: 'home', name: '首页' },
{ path: 'other', name: '其他' },
{ path: 'about', name: '关于' }
];
return (
<ul>
{
navs.map((nav) => {
if (nav.path === location) {
return <ActiveLink key={nav.path} name={nav.name} />
}
return <InactiveLink key={nav.path} path={`./${nav.path}`} name={nav.name} />
})
}
</ul>
);
};

export default Nav;

然后在 pages/route/ 下新建 home.js, other.js, about.js 三个文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// home.js
import React from 'react';
import Nav from '../../components/route/nav';
export default () => (
<div>
<Nav location="home" />
<p>这是首页</p>
</div>
);

// other.js
import React from 'react';
import Nav from '../../components/route/nav';
export default = () => (
<div>
<Nav location="other" />
<p>其他内容</p>
</div>
);

// about.js
import React from 'react';
import Nav from '../../components/route/nav';

export default () => (
<div>
<Nav location="about" />
<p>关于我们</p>
</div>
);

npm run dev 启动项目,点击不同的链接,页面不刷新,但是会动态的切换内容。

说明

上面的代码中,唯一陌生的部分应该就是 <Link> 了。检查元素,发现 Link 被解析到了 a 标签上。当然,a 标签并不是必须的。

这里还有一个担心就是,单个页面包含太多文件,导致页面加载缓慢。开发者工具观察网络,发现页面所用到的文件是动态加载的,所以这个担心是不必要的。

如果子页面过大,路由切换的时候还是会导致较长时间的等待,影响用户体验。NextJsLink 提供 prefetch 属性,可以预加载子页面。修改 nav.js 代码如下:

1
2
3
4
5
6
7
8
9
// ...
const InactiveLink = ({ path, name }) => (
<li>
<Link prefetch href={path}>
<a><strong>{name}</strong></a>
</Link>
</li>
);
// ...

prefetch 是一个生成专用的功能,需要注意的是:通过 prefetch Next.js 只下载 JS 代码。当页面呈现时,您可能需要等待数据。除了使用 Link,也可以通过 Router.prefetch('/dynamic') 这样的高级用法实现。

有的时候,Link 不方便使用,也可以使用 Router.push() 代替。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import Router from 'next/router'

// 请求 /about?name=Zeit
const handler = () =>
Router.push({
pathname: '/about',
query: { name: 'Zeit' }
})

export default () => (
<div>
Click <span onClick={handler}>here</span> to read more
</div>
);

Router 还提供了一系列路由变化周期中的钩子函数,这里不作重点说明:

  • onRouteChangeStart(url) - 当路由开始改变时触发
  • onRouteChangeComplete(url) - 当路由完全改变时触发
  • onRouteChangeError(err, url) - 更换路由时发生错误时触发
  • onBeforeHistoryChange(url) - 在更改浏览器的历史记录之前触发
  • onAppUpdated(nextRoute) - 切换页面时触发,并有一个新版本的应用程序