我发现了一些东西:
isReady: boolean - 路由字段是否已在客户端更新并准备就绪,仅应在useEffect方法内使用,不用于在服务器上进行有条件的呈现。 https://nextjs.org/docs/api-reference/next/router#router-object
代码如下:
const router = useRouter();
useEffect(()=>{
if(!router.isReady) return;
// codes using router.query
}, [router.isReady]);
在初始渲染期间无法获取query
值。
静态优化页面在没有路由参数的情况下被注入,因此query
是一个空对象({}
)。
页面被注入后,Next.js将填充query
。
要确定路由参数是否准备就绪,您可以在useEffect
钩子内使用router.isReady
。有关示例,请参见@doxylee提供的答案。
在动态路由的初始呈现中,router.asPath
和router.route
相等。一旦query
对象可用,router.asPath
就会反映它。
在asPath
被更改后,您可以在useEffect
钩子中依赖于查询值。
const router = useRouter();
useEffect(() => {
if (router.asPath !== router.route) {
// router.query.lang is defined
}
}, [router])
asPath
和route
可能相等,这并不反映路由器的实际准备情况。只有当你的URL始终具有查询时,才可以依赖它。 - MycolaosgetServerSideProps()
的context
参数获取它们,并将其作为props传递给组件。对于像[id].js
这样的页面,
export function getServerSideProps(context) {
return {
props: {params: context.params}
};
}
export default ({params}) => {
const {id} = params;
return <div>You opened page with {id}</div>;
};
context.props
上下文没有 props
,请查看示例。您的页面属性将从 getServerSideProps
返回您返回的 { props: {} }
。 - Mycolaoscontext.params
而不是context.props
。我刚在Next 10上重新验证了这种方法。参考:https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering - Hunter MedneygetServerSideProps
指令可以让Next.js在每个请求时在服务器端预渲染页面。这意味着它没有进行静态优化和缓存。只有在必须在首次渲染之前获取数据时才使用它,因为这会牺牲性能。 - Nikolai Kiselev这是一个非常好的问题,让我花了几天时间才找到最佳方法。
我个人发现解决动态路由路径参数验证问题或仅仅是路由路径参数的三种可行方案。
这三种解决方案是:
在我的示例中,我将使用需要重置令牌或应该重定向的路线。
SSR
首先是使用getServerSideProps
进行服务器端渲染。
Vercel建议只在万不得已时使用SSR,强烈建议在可以使用时不要使用SSR(字节时间和成本)。
我们建议尝试增量静态生成或客户端获取并查看它们是否符合您的需求。 https://vercel.com/blog/nextjs-server-side-rendering-vs-static-generation
但是,在您需要时,比如说有一些服务器端API验证调用,您需要验证查询参数。
export const getServerSideProps = async (context) => {
const { token } = context.query;
if (!token) {
return {
redirect: {
permanent: false,
destination: "/",
}
}
}
return {
props: {}
// props: { token }
// You could do this either with useRouter or passing props
}
}
useRouter 其次是最简单的 useRouter
。当我第一次使用它时,我遇到了一个问题,即在 nextjs/react 静态生成时,查询参数会为空。幸运的是,useRouter
有isReady属性!
import Router, { useRouter } from "next/router";
const { query, isReady } = useRouter();
useEffect(() => {
if (!isReady) return;
if (!query.token) {
Router.push("/")
}
}, [isReady])
中间件,在我看来,这是一种以清晰的方式分离功能的方法。我发现这个方法基于vercel的一个示例。强烈推荐阅读一些示例以找到最佳实践。 https://github.com/vercel/examples/
import { NextResponse, NextRequest } from 'next/server'
export async function middleware(req) {
const { pathname, searchParams } = req.nextUrl
if (pathname == '/reset-token') {
const index = searchParams.findIndex(x => x.key === "token")
// You could also add token validation here.
if (!index) {
return NextResponse.redirect('/')
}
}
return NextResponse.next()
}
这里是一个仓库,其中包含一些很酷的查询参数过滤。 这是一种更加柔和的方法,而不是强制重定向。 https://github.com/vercel/examples/tree/main/edge-functions/query-params-filter
Nico也对此有一个很好的回答,但我不建议像他的示例中那样使用钩子,而是使用isReady
。
https://dev59.com/alMH5IYBdhLWcg3w0DO9#58182678
更好的方法是使用this.props.router.events.on
方法,在生命周期函数componentDidMount
中监听专门的事件routeChangeComplete
,如果你正在使用类组件 -
routeChangeComplete = () => {
// this WILL have valid query data not empty {}
console.log(this.props.router.query);
};
componentDidMount() {
this.props.router.events.on("routeChangeComplete", this.routeChangeComplete);
}
componentWillUnmount() {
this.props.router.events.off("routeChangeComplete", this.routeChangeComplete);
}
参考:https://nextjs.org/docs/api-reference/next/router#routerevents
routeChangeComplete: 当路由完全变化时触发。
实际上,当 isReady
变为 true
或者 router.query
对象有数据时会触发该事件。
对于NextJS版本12.0.8:
如果您从页面导出名为getServerSideProps(服务器端渲染)的函数,则Next.js将使用由getServerSideProps返回的数据在每个请求上预渲染此页面。
=异步函数
参考:https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props#getserversideprops
只需将该异步函数放在页面上即可通知NextJS其存在。在组件的预渲染阶段,路由器的查询对象将为空。
isReady: boolean - 路由器字段是否已在客户端更新并准备好使用。仅应在useEffect方法内部使用,而不用于在服务器上有条件地呈现。
参考:https://nextjs.org/docs/api-reference/next/router
解决方案:
import { useRouter } from 'next/router';
const Fn = () =>{
const router = useRouter();
const { param } = router.query;
const fetchData = async () => {
await fetch();
}
useEffect(() => {
fetchCat();
}, [router.isReady]);
}
export default function withLocale(WrappedPage) {
const WithLocale = ({ router, ...props }) => {
const { lang } = router.query;
if (!lang || !isLocale(lang)) {
return <Error statusCode={404} />;
}
return (
<LocaleProvider lang={lang}>
<WrappedPage {...props} />
</LocaleProvider>
);
};
return WithLocale;
}
router.query
已被移除(由于新的应用程序路由器),并被useSearchParams();
钩子替代,因此您可以像这样使用它:import { useSearchParams } from "next/navigation";
const query = useSearchParams();
这是一个好的解决方法,我从this comment中发现了它。
添加useQuery.ts
辅助文件
// useQuery.js
import { useRouter } from 'next/router';
// Resolves query or returns null
export default function useQuery() {
const router = useRouter();
const ready = router.asPath !== router.route;
if (!ready) return null;
return router.query;
}
使用方法
// In your components (instead of useRouter)
const query = useQuery();
useEffect(() => {
if (!query) {
return;
}
console.log('my query exists!!', query);
}, [query]);
我找到了那些想要使用类组件的人的答案。这实际上是无处可寻的!享受吧!
您将在render()
中添加if(this.props.router.isReady)
并在条件中包含return
。
.
.
import { withRouter } from 'next/router';
import { Component } from 'react';
class User extends Component {
...
render() {
if(this.props.router.isReady){ // Add this condition and include return ()
// Do anything
console.log(this.props.router.query) // Log 'query' on first render
return (
<div>
<SearchBar pid={this.props.router.query.pid} /> // Pass the query params to another component if needed
</div>
);
}
}
}
export default withRouter(User);