在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随时可能更改...我可能会遗漏某些东西...但是为什么在这种情况下浅浅忽略了呢?似乎很奇怪。
答案 0 :(得分:4)
我认为这是预期的行为,因为您正在路由到新页面。如果您只是更改查询参数,则浅路由应该起作用,例如:
router.push('/?counter=10', undefined, { shallow: true })
但是您正在使用路由参数
router.push(
'/post/[...slug]',
'/post/2020/01/01/hello-world',
{ shallow: true }
);
这表示您正在路由到新页面,即使我们要求进行浅层路由,它也会卸载当前页面,加载新页面并等待数据获取,这在此处的文档中{{3 }}。
顺便说一句,您说的是“页面已刷新”,但是router.push
也不会刷新页面,即使在没有shallow: true
的情况下使用也是如此。毕竟这是一个单页面的应用程序。它只是呈现新页面并运行getStaticProps
,getServerSideProps
或getInitialProps
。
答案 1 :(得分:1)
好的,这个答案基于我的第一个答案(以及问题的澄清):
#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.
答案 2 :(得分:1)
“浅层路由”使您能够更新路径名或查询参数而不会丢失状态,即仅更改了路由状态。但是条件是,您必须位于同一页面上(如docs中所示)注意事项图片)。
为此,您必须将第二个arg作为未定义传递给router.push或Router.push。否则,将在卸载第一页后加载新页面,而您将无法获得预期的行为。
我的意思是浅路径路由在更改路径名方面将不再起作用,这是因为我们选择加载的是新页面,而不仅仅是URL。希望这可以帮助helps
示例
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 :(得分:1)
实际上,基于文档的描述,我认为您错误地使用了此push
函数。请参阅来自docs的以下代码:
import Router from 'next/router'
Router.push(url, as, options)
文档说:
url
-导航到的URL。通常是页面的名称as
-将在浏览器中显示的URL的可选修饰符。默认为url
options
-具有以下配置选项的可选对象: 浅:无需重新运行getStaticProps,getServerSideProps或getInitialProps,即可更新当前页面的路径。默认为false
这意味着您应该将确切的URL作为第一个参数传递,并且如果您想将其显示为装饰名称,请传递第二个参数,对于第三个参数只需传递选项,所以您应该这样写:
router.push(
'/post/2020/01/01/hello-world',
undefined,
undefined
);
对于shallow routing,您应该使用确切的示例:
router.push('/?counter=10', undefined, { shallow: true });
实际上,使用您的代码,您可以创建一条新路线,并且不可避免地需要刷新。
答案 4 :(得分:-1)
据我了解:
.
├── post
│ └──[id].js
├── about.js
├── index.js
├── public
│ └── ...
├── .gitignore
├── package.json
├── package-lock.json
└── README.md
在index.js中:
import Link from 'next/link';
import Router from 'next/router'
export default function Home() {
const handleClick = e => {
e.preventDefault();
Router.push(
'/post/[id]',
'/post/2',
{ shallow: true }
);
}
return (
<div className="container">
<div>
<Link href="/about">
<a>About</a>
</Link>
</div>
<hr />
<div>
<Link
href={`/post/[id]?id=${2}`}
as={`/post/${2}`}
>
<a>
<span>
Post:
</span>
</a>
</Link>
<hr />
<div>
<button onClick={handleClick}>
Push me
</button>
</div>
</div>
</div>
)
}
[id] .js:
export default function Post({ post }) {
return (
<div>
<h1>Post is here{}</h1>
{JSON.stringify(post)}
</div>
);
}
export async function getStaticPaths() {
return {
paths: [
{ params: { id: '0' } }
],
fallback: true
};
}
export async function getStaticProps(context) {
const res = await fetch(`https://api.icndb.com/jokes/${context.params.id}`);
const post = await res.json()
return { props: { post } }
}
在您的示例中:
router.push(
'/post/[...slug]',
'/post/2020/01/01/foo-bar',
{ shallow: true }
);
我看到[... slug]来自官方文档,但是应该像这样:
Router.push(
'/post/[id]',
'/post/2',
{ shallow: true }
);
结论:
我不知道幕后到底发生了什么,但是当我将[...slug]
替换为[id]
时似乎很正常。
我认为:使用[...slug]
后,文件系统中的内容尚不清楚,服务器尝试提供接近[...slug]
的路径。文件提供中可能会发生某些fallback
/ error handling
。那可能就是刷新的原因
注意:
Router.push()
和<Link>
仅用于测试目的。