路由

Examples

可以用 <Link> 组件实现客户端的路由切换。

  1. // pages/index.js
  2. import Link from 'next/link'
  3.  
  4. export default () =>
  5. <div>
  6. Click{' '}
  7. <Link href="/about">
  8. <a>here</a>
  9. </Link>{' '}
  10. to read more
  11. </div>
  1. // pages/about.js
  2. export default () => <p>Welcome to About!</p>

注意:可以使用<Link prefetch>使链接和预加载在后台同时进行,来达到页面的最佳性能。

客户端路由行为与浏览器很相似:

  • 组件获取
  • 如果组件定义了getInitialProps,数据获取了。如果有错误情况将会渲染 _error.js。
  • 1和2都完成了,pushState执行,新组件被渲染。
    如果需要注入pathname, queryasPath到你组件中,你可以使用withRouter

URL 对象

Examples
- With URL Object Routing

组件<Link>接收 URL 对象,而且它会自动格式化生成 URL 字符串

  1. // pages/index.js
  2. import Link from 'next/link'
  3.  
  4. export default () =>
  5. <div>
  6. Click{' '}
  7. <Link href={{ pathname: '/about', query: { name: 'Zeit' }}}>
  8. <a>here</a>
  9. </Link>{' '}
  10. to read more
  11. </div>

将生成 URL 字符串/about?name=Zeit,你可以使用任何在Node.js URL module documentation定义过的属性。

替换路由

<Link>组件默认将新 url 推入路由栈中。你可以使用replace属性来防止添加新输入。

  1. // pages/index.js
  2. import Link from 'next/link'
  3.  
  4. export default () =>
  5. <div>
  6. Click{' '}
  7. <Link href="/about" replace>
  8. <a>here</a>
  9. </Link>{' '}
  10. to read more
  11. </div>

组件支持点击事件 onClick

<Link>支持每个组件所支持的onClick事件。如果你不提供<a>标签,只会处理onClick事件而href将不起作用。

  1. // pages/index.js
  2. import Link from 'next/link'
  3.  
  4. export default () =>
  5. <div>
  6. Click{' '}
  7. <Link href="/about">
  8. <img src="/static/image.png" alt="image" />
  9. </Link>
  10. </div>

暴露 href 给子元素

如子元素是一个没有 href 属性的<a>标签,我们将会指定它以免用户重复操作。然而有些时候,我们需要里面有<a>标签,但是Link组件不会被识别成超链接,结果不能将href传递给子元素。在这种场景下,你可以定义一个Link组件中的布尔属性passHref,强制将href传递给子元素。

注意: 使用a之外的标签而且没有通过passHref的链接可能会使导航看上去正确,但是当搜索引擎爬行检测时,将不会识别成链接(由于缺乏 href 属性),这会对你网站的 SEO 产生负面影响。

  1. import Link from 'next/link'
  2. import Unexpected_A from 'third-library'
  3.  
  4. export default ({ href, name }) =>
  5. <Link href={href} passHref>
  6. <Unexpected_A>
  7. {name}
  8. </Unexpected_A>
  9. </Link>

禁止滚动到页面顶部

<Link>的默认行为就是滚到页面顶部。当有 hash 定义时(#),页面将会滚动到对应的 id 上,就像<a>标签一样。为了预防滚动到顶部,可以给<Link>scroll={false}属性:

  1. <Link scroll={false} href="/?counter=10"><a>Disables scrolling</a></Link>
  2. <Link href="/?counter=10"><a>Changes with scrolling to top</a></Link>

命令式

Examples
- Basic routing
- With a page loading indicator

你也可以用next/router实现客户端路由切换

  1. import Router from 'next/router'
  2.  
  3. export default () =>
  4. <div>
  5. Click <span onClick={() => Router.push('/about')}>here</span> to read more
  6. </div>

拦截器 popstate

有些情况(比如使用custom router),你可能想监听popstate,在路由跳转前做一些动作。比如,你可以操作 request 或强制 SSR 刷新

  1. import Router from 'next/router'
  2.  
  3. Router.beforePopState(({ url, as, options }) => {
  4. // I only want to allow these two routes!
  5. if (as !== "/" || as !== "/other") {
  6. // Have SSR render bad routes as a 404.
  7. window.location.href = as
  8. return false
  9. }
  10.  
  11. return true
  12. });

如果你在beforePopState中返回 false,Router将不会执行popstate事件。例如Disabling File-System Routing

以上Router对象的 API 如下:

  • route - 当前路由的String类型
  • pathname - 不包含查询内容的当前路径,为String类型
  • query - 查询内容,被解析成Object类型. 默认为{}
  • asPath - 展现在浏览器上的实际路径,包含查询内容,为String类型
  • push(url, as=url) - 页面渲染第一个参数 url 的页面,浏览器栏显示的是第二个参数 url
  • replace(url, as=url) - performs a replaceState call with the given url
  • beforePopState(cb=function) - 在路由器处理事件之前拦截.
    pushreplace 函数的第二个参数as,是为了装饰 URL 作用。如果你在服务器端设置了自定义路由将会起作用。

URL 对象用法

pushreplace可接收的 URL 对象(<Link>组件的 URL 对象一样)来生成 URL。

  1. import Router from 'next/router'
  2.  
  3. const handler = () =>
  4. Router.push({
  5. pathname: '/about',
  6. query: { name: 'Zeit' }
  7. })
  8.  
  9. export default () =>
  10. <div>
  11. Click <span onClick={handler}>here</span> to read more
  12. </div>

也可以像<Link>组件一样添加额外的参数。

路由事件

你可以监听路由相关事件。下面是事件支持列表:

  • routeChangeStart(url) - 路由开始切换时触发
  • routeChangeComplete(url) - 完成路由切换时触发
  • routeChangeError(err, url) - 路由切换报错时触发
  • beforeHistoryChange(url) - 浏览器 history 模式开始切换时触发
  • hashChangeStart(url) - 开始切换 hash 值但是没有切换页面路由时触发
  • hashChangeComplete(url) - 完成切换 hash 值但是没有切换页面路由时触发
这里的url是指显示在浏览器中的 url。如果你用了Router.push(url, as)(或类似的方法),那浏览器中的 url 将会显示 as 的值。

下面是如何正确使用路由事件routeChangeStart的例子:

  1. const handleRouteChange = url => {
  2. console.log('App is changing to: ', url)
  3. }
  4.  
  5. Router.events.on('routeChangeStart', handleRouteChange)

如果你不想长期监听该事件,你可以用off事件去取消监听:

  1. Router.events.off('routeChangeStart', handleRouteChange)

如果路由加载被取消(比如快速连续双击链接)

  1. Router.events.on('routeChangeError', (err, url) => {
  2. if (err.cancelled) {
  3. console.log(`Route to ${url} was cancelled!`)
  4. }
  5. })

浅层路由

Examples
- Shallow Routing

浅层路由允许你改变 URL 但是不执行getInitialProps生命周期。你可以加载相同页面的 URL,得到更新后的路由属性pathnamequery,并不失去 state 状态。

你可以给Router.pushRouter.replace方法加shallow: true参数。如下面的例子所示:

  1. // Current URL is "/"
  2. const href = '/?counter=10'
  3. const as = href
  4. Router.push(href, as, { shallow: true })

现在 URL 更新为/?counter=10。在组件里查看this.props.router.query你将会看到更新的 URL。

你可以在componentdidupdate钩子函数中监听 URL 的变化。

  1. componentDidUpdate(prevProps) {
  2. const { pathname, query } = this.props.router
  3. // verify props have changed to avoid an infinite loop
  4. if (query.id !== prevProps.router.query.id) {
  5. // fetch data based on the new query
  6. }
  7. }
注意:浅层路由只作用于相同 URL 的参数改变,比如我们假定有个其他路由about,而你向下面代码样运行:
  1. Router.push('/?counter=10', '/about?counter=10', { shallow: true })
那么这将会出现新页面,即使我们加了浅层路由,但是它还是会卸载当前页,会加载新的页面并触发新页面的getInitialProps

高阶组件

Examples
- Using the withRouter utility

如果你想应用里每个组件都处理路由对象,你可以使用withRouter高阶组件。下面是如何使用它:

  1. import { withRouter } from 'next/router'
  2.  
  3. const ActiveLink = ({ children, router, href }) => {
  4. const style = {
  5. marginRight: 10,
  6. color: router.pathname === href? 'red' : 'black'
  7. }
  8.  
  9. const handleClick = (e) => {
  10. e.preventDefault()
  11. router.push(href)
  12. }
  13.  
  14. return (
  15. <a href={href} onClick={handleClick} style={style}>
  16. {children}
  17. </a>
  18. )
  19. }
  20.  
  21. export default withRouter(ActiveLink)

上面路由对象的 API 可以参考next/router.