簡體   English   中英

Next.js - 具有動態路由的淺層路由

[英]Next.js - Shallow routing with dynamic routes

當嘗試在 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 隨時可能發生變化......我可能會遺漏一些東西......但為什么在這種情況下會忽略 shallow ? 似乎很奇怪。

我認為這是預期的行為,因為您正在路由到新頁面。 如果您只是更改查詢參數,淺路由應該可以工作,例如:

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

但是您正在使用路由參數

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

這表明您正在路由到一個新頁面,它會卸載當前頁面,加載新頁面,並等待數據獲取,即使我們要求進行淺層路由,這在文檔中提到過淺路由警告

順便說一句,您說“頁面已刷新”,但即使在沒有使用shallow: true的情況下, router.push也不會刷新頁面。 畢竟這是一個單頁應用程序。 它只是呈現新頁面並運行getStaticPropsgetServerSidePropsgetInitialProps

淺層路由注意事項

淺路由使您能夠更新路徑名或查詢參數而不會丟失 state 即,僅更改路由的 state。但條件是,您必須在同一頁面上(如文檔警告圖像中所示)

為此,您必須將第二個參數傳遞給未定義的 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

實際上,根據文檔描述,我相信您錯誤地使用了此push function。 請參閱來自docs的以下代碼:

import Router from 'next/router'

Router.push(url, as, options)

文檔說:

  • url - 要導航到的 URL。 這通常是頁面的名稱
  • as - 將在瀏覽器中顯示的 URL 的可選裝飾器。 默認為url
  • options - 可選 object,具有以下配置選項: shallow:更新當前頁面的路徑,無需重新運行 getStaticProps、getServerSideProps 或 getInitialProps。 默認為假

這意味着您應該將確切的 URL 作為第一個參數傳遞,如果您想將其顯示為裝飾名稱,則傳遞第二個參數,第三個只需傳遞選項,因此您應該編寫如下:

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

對於淺層路由,您應該使用確切的示例:

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

事實上,用你的代碼,你創建了一個新的路由,刷新是不可避免的。

如果避免在嵌套路由中重新呈現是您的問題,那么解決方案可能是將您的頁面嵌套在布局組件中,如下所述:

https://nextjs.org/docs/basic-features/layouts

https://adamwathan.me/2019/10/17/persistent-layout-patterns-in-nextjs/

好的,所以這個答案基於我的第一個答案(和問題澄清):

#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

[...蛞蝓].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.

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM