【问题标题】:Next.js - Shallow routing with dynamic routesNext.js - 带有动态路由的浅路由
【发布时间】:2020-09-08 21:26:57
【问题描述】:

在 Next.js 中尝试使用动态路由进行浅层路由时,页面会刷新并忽略浅层。似乎很多人对此感到困惑。

假设我们从下一页开始

router.push(
  '/post/[...slug]',
  '/post/2020/01/01/hello-world',
  { shallow: true }
);

然后我们路由到另一篇博文:

router.push(
  '/post/[...slug]',
  '/post/2020/01/01/foo-bar',
  { shallow: true }
);

这样不触发浅路由,浏览器刷新,为什么?

在代码库中很清楚这是一个特性:

// If asked to change the current URL we should reload the current page
// (not location.reload() but reload getInitialProps and other Next.js stuffs)
// We also need to set the method = replaceState always
// as this should not go into the history (That's how browsers work)
// We should compare the new asPath to the current asPath, not the url
if (!this.urlIsNew(as)) {
  method = 'replaceState'
}

我可以使用window.history.pushState() 手动实现相同的目的,尽管这当然是个坏主意:

window.history.pushState({
  as: '/post/2020/01/01/foo-bar',
  url: '/post/[...slug]',
  options: { shallow: true }
}, '', '/post/2020/01/01/foo-bar');

由于 Next.JS 的内部 API 可能随时更改...我可能会遗漏一些东西...但是为什么在这种情况下会忽略浅层?看起来很奇怪。

【问题讨论】:

  • 我遇到了同样的问题,我现在寻求您的解决方案,但我仍然觉得它是一个黑客:/
  • 我在下一个 github 讨论部分发布了一个未解决的问题
  • 问题的根源可能在组件中或在渲染它的路径中。您能否分享一下您的路由器和组件的代码?
  • 不,这是该功能在 NextJS 中的工作方式。它记录在代码中。
  • 似乎只有当动态路由被设计为“新页面”时才会发生,正如他们在caveats 中提到的那样。很难说为什么没有官方回应,但我猜动态路由的数据需求可能会发生很大变化,因为它们可能涵盖“全部”情况。在旁注中,Router.push('/post/[...slug]', '/post/2020/01/01/foo-bar') 是否表现出相同的行为?

标签: javascript reactjs routes next.js server-side-rendering


【解决方案1】:

我认为这是预期的行为,因为您正在路由到一个新页面。如果您只是更改查询参数,浅层路由应该可以工作,例如:

router.push('/?counter=10', undefined, { shallow: true })

但是你正在使用路由参数

router.push(
  '/post/[...slug]',
  '/post/2020/01/01/hello-world',
  { shallow: true }
);

这表明你正在路由到一个新页面,它会卸载当前页面,加载新页面,并等待数据获取,即使我们要求进行浅层路由,这在Shallow routing caveats的文档中有所提及.

顺便说一句,您说“页面已刷新”,但router.push 即使在没有shallow: true 的情况下使用也不会刷新页面。毕竟这是一个单页应用程序。它只是呈现新页面并运行getStaticPropsgetServerSidePropsgetInitialProps

【讨论】:

    【解决方案2】:

    Shallow Routing 使您能够在不丢失状态的情况下更新路径名或查询参数,即,仅更改路由的状态。但条件是,您必须在同一页面上(如文档中所示警告图片)

    为此,您必须将第二个参数传递给未定义的 router.push 或 Router.push。否则,在卸载第一个页面后将加载新页面,您将无法获得预期的行为。

    我的意思是浅层路由将不再适用于路径名更改,这是因为我们选择加载新页面而不仅仅是 url。希望这会有所帮助?

    示例

    import { useEffect } from 'react'
    import { useRouter } from 'next/router'
    
    // Current URL is '/'
    function Page() {
      const router = useRouter()
    
      useEffect(() => {
        // Always do navigations after the first render
        router.push('/post/[...slug]', undefined, { shallow: true })
      }, [])
    
      useEffect(() => {
        // The pathname changed!
      }, [router.pathname ])
    }
    
    export default Page
    

    【讨论】:

      【解决方案3】:

      实际上,根据文档的描述,我相信你错误地使用了这个push 函数。请看以下来自docs的代码:

      import Router from 'next/router'
      
      Router.push(url, as, options)
      

      文档说:

      • url - 要导航到的 URL。这通常是页面的名称
      • as - 将在浏览器中显示的 URL 的可选装饰器。默认为url
      • options - 具有以下配置选项的可选对象: shallow:更新当前页面的路径,不重新运行 getStaticProps、getServerSideProps 或 getInitialProps。默认为假

      这意味着您应该将确切的 URL 作为第一个参数传递,如果您想将其显示为装饰名称,则传递第二个参数,第三个只需传递选项,因此您应该编写如下:

      router.push(
        '/post/2020/01/01/hello-world',
        undefined,
        undefined
      );
      

      对于shallow routing,您应该使用确切的示例:

      router.push('/?counter=10', undefined, { shallow: true });
      

      其实用你的代码,你创建了一个新路由,刷新是不可避免的。

      【讨论】:

        【解决方案4】:

        好的,所以这个答案基于我的第一个答案(和问题澄清):

        #1 问题是: This does not trigger shallow routing, the browser refreshes, why? => 查看我的最后一个答案,了解next.js 如何处理文件结构。

        #2 如果要处理无限的 URL 参数:

        您必须遵循以下文件结构:

        .
        ├── pages
        │   ├── post
        │   │   └──[...slug].js // or [...slug]/index.js
        │   ├── about.js
        │   └── index.js
        ├── public
        │   └── ...
        ├── .gitignore
        ├── package.json
        ├── package-lock.json
        └── README.md
        

        [...slug].js:

        export default function Post({ post }) {
          return (
            <div>
              <h1>Post is here{}</h1>
              {JSON.stringify(post)}
            </div>
          );
        }
        
        export async function getStaticPaths() {
          return {
            paths: [
              { params: { slug: ["*"] } },
            ],
            fallback: true
          };
        }
            
        export async function getStaticProps(context) {
        
          console.log("SLUG: ", context);
          const { params = [] } = context || {};
          const res = await fetch(`https://api.icndb.com/jokes/${params && params.slug[0] || 3}`);
          const post = await res.json()
          return { props: { post } }
        }
        

        ./index.js:

        import Link from 'next/link';
        import Router from 'next/router'
        
        export default function Home() {
        
          const handleClick = e => {
            e.preventDefault();
        
            Router.push(
              '/post/[...slug]',
              '/post/2/2/4',
              { shallow: true }
            );
          }
        
          return (
            <div className="container">
              <div>
                <Link href="/about">
                  <a>About</a>
                </Link>
              </div>
              <hr />
              <div>
                <Link
                  href={`/post/[...slug]?slug=${2}`}
                  as={`/post/${2}`}
                >
                  <a>
                    <span>
                      Post:
                      </span>
                  </a>
                </Link>
                <hr />
                <div>
                  <button onClick={handleClick}>
                    Push me
                    </button>
                </div>
              </div>
            </div>
          )
        }
        

        任意深/post/any/url/long/will_return_slugfile

        再次!!!next.js 中,您必须注意路由中的文件系统结构。 (正如我在上一个答案中提到的)

        pages/post/[[...slug]].js will match /post, /post/a, /post/a/b, and so on.

        【讨论】:

        • @AndrewMcLagan 能回答你的问题吗?
        猜你喜欢
        • 2021-10-17
        • 2021-11-02
        • 2022-11-05
        • 2022-01-04
        • 2021-11-18
        • 2021-12-03
        • 2022-01-10
        • 2021-12-17
        • 2021-01-29
        相关资源
        最近更新 更多