- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我对 next.js graphQL 世界还很陌生。
我刚找到 useSWR,我想知道我是否可以将它与 Apollo-client 一起使用,不使用 graphql 请求。
最佳答案
是的,你可以,我也可以。我有两个 headless CMS 共存。一种是通过 OneGraph 与 google 捆绑在一起的 headless wordpress;另一个是 Headless Booksy,一个没有可公开访问的端点的闭源 CMS——在用户驱动的事件期间剖析网络选项卡以确定任何所需的 header /参数,并最终反向工程他们的身份验证流程以使用异步分区在我的 repo 中自动化它。
就是说,是的,我同时使用 apollo Client 和 SWR。这是 _app.tsx 配置
import '@/styles/index.css';
import '@/styles/chrome-bug.css';
import 'keen-slider/keen-slider.min.css';
import { AppProps, NextWebVitalsMetric } from 'next/app';
import { useRouter } from 'next/router';
import { ApolloProvider } from '@apollo/client';
import { useEffect, FC } from 'react';
import { useApollo } from '@/lib/apollo';
import * as gtag from '@/lib/analytics';
import { MediaContextProvider } from '@/lib/artsy-fresnel';
import { Head } from '@/components/Head';
import { GTagPageview } from '@/types/analytics';
import { ManagedGlobalContext } from '@/components/Context';
import { SWRConfig } from 'swr';
import { Provider as NextAuthProvider } from 'next-auth/client';
import fetch from 'isomorphic-unfetch';
import { fetcher, fetcherGallery } from '@/lib/swr-fetcher';
import { Configuration, Fetcher } from 'swr/dist/types';
type T = typeof fetcher | typeof fetcherGallery;
interface Combined extends Fetcher<T> {}
const Noop: FC = ({ children }) => <>{children}</>;
export default function NextApp({
Component,
pageProps
}: AppProps) {
const apolloClient = useApollo(pageProps);
const LayoutNoop = (Component as any).LayoutNoop || Noop;
const router = useRouter();
useEffect(() => {
document.body.classList?.remove('loading');
}, []);
useEffect(() => {
const handleRouteChange = (url: GTagPageview) => {
gtag.pageview(url);
};
router.events.on('routeChangeComplete', handleRouteChange);
return () => {
router.events.off('routeChangeComplete', handleRouteChange);
};
}, [router.events]);
return (
<>
<SWRConfig
value={{
errorRetryCount: 5,
refreshInterval: 43200 * 10,
onLoadingSlow: (
key: string,
config: Readonly<
Required<Configuration<any, any, Combined>>
>
) => [key, { ...config }]
}}
>
<ApolloProvider client={apolloClient}>
<NextAuthProvider session={pageProps.session}>
<ManagedGlobalContext>
<MediaContextProvider>
<Head />
<LayoutNoop pageProps={pageProps}>
<Component {...pageProps} />
</LayoutNoop>
</MediaContextProvider>
</ManagedGlobalContext>
</NextAuthProvider>
</ApolloProvider>
</SWRConfig>
</>
);
}
然后,我有这个 api 路由处理评论 + 通过 index.tsx 中的 SWR 对这些评论进行分页
import { NextApiRequest, NextApiResponse } from 'next';
import { BooksyReviewFetchResponse } from '@/types/booksy';
import fetch from 'isomorphic-unfetch';
import { getAccessToken } from '@/lib/booksy';
const API_KEY = process.env.NEXT_PUBLIC_BOOKSY_BIZ_API_KEY ?? '';
const FINGERPRINT =
process.env.NEXT_PUBLIC_BOOKSY_BIZ_X_FINGERPRINT ?? '';
export default async function (
req: NextApiRequest,
res: NextApiResponse<BooksyReviewFetchResponse>
) {
const {
query: { reviews_page, reviews_per_page }
} = req;
const { access_token } = await getAccessToken();
const rev_page_number = reviews_page ? reviews_page : 1;
const reviews_pp = reviews_per_page ? reviews_per_page : 10;
const response = await fetch(
`https://us.booksy.com/api/us/2/business_api/me/businesses/481001/reviews/?reviews_page=${rev_page_number}&reviews_per_page=${reviews_pp}`,
{
headers: {
'X-Api-key': API_KEY,
'X-Access-Token': `${access_token}`,
'X-fingerprint': FINGERPRINT,
Authorization: `s-G1-cvdAC4PrQ ${access_token}`,
'Cache-Control':
's-maxage=86400, stale-while-revalidate=43200',
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.152 Safari/537.36',
Connection: 'keep-alive',
Accept: '*/*',
'Accept-Encoding': 'gzip, deflate, br'
},
method: 'GET',
keepalive: true
}
);
const booksyReviews: BooksyReviewFetchResponse =
await response.json();
res.setHeader(
'Cache-Control',
'public, s-maxage=86400, stale-while-revalidate=43200'
);
return res.status(200).json(booksyReviews);
}
和以下 api 路由处理自定义选取框的图像数据也在 index.tsx 中
import { NextApiRequest, NextApiResponse } from 'next';
import { Gallery } from '@/types/index';
import { getLatestBooksyPhotos } from '@/lib/booksy';
export default async function (
_req: NextApiRequest,
res: NextApiResponse<Gallery>
) {
const response: Response = await getLatestBooksyPhotos();
const booksyImages: Gallery = await response.json();
res.setHeader(
'Cache-Control',
'public, s-maxage=86400, stale-while-revalidate=43200'
);
return res.status(200).json(booksyImages);
}
现在,检查 index.tsx。为了清楚起见,我将代码分为服务器端和客户端
export async function getStaticProps(
ctx: GetStaticPropsContext
): Promise<
GetStaticPropsResult<{
other: LandingDataQuery['other'];
popular: LandingDataQuery['popular'];
Places: LandingDataQuery['Places'];
merchandise: LandingDataQuery['merchandise'];
businessHours: LandingDataQuery['businessHours'];
Header: DynamicNavQuery['Header'];
Footer: DynamicNavQuery['Footer'];
initDataGallery: Partial<
Configuration<Gallery, any, Fetcher<Gallery>>
>;
initialData: Partial<
Configuration<
BooksyReviewFetchResponse,
any,
Fetcher<BooksyReviewFetchResponse>
>
>;
}>
> {
console.log(ctx.params ?? '');
const apolloClient = initializeApollo();
const { data: DynamicSlugs } = await apolloClient.query<
DynamicNavQuery,
DynamicNavQueryVariables
>({
query: DynamicNavDocument,
variables: {
idHead: 'Header',
idTypeHead: WordpressMenuNodeIdTypeEnum.NAME,
idTypeFoot: WordpressMenuNodeIdTypeEnum.NAME,
idFoot: 'Footer'
}
});
const { data: LandingData } = await apolloClient.query<
LandingDataQuery,
LandingDataQueryVariables
>({
query: LandingDataDocument,
variables: {
other: WordPress.Services.Other,
popular: WordPress.Services.Popular,
path: Google.PlacesPath,
googleMapsKey: Google.MapsKey
}
});
const { other, popular, Places, businessHours, merchandise } =
LandingData;
const { Header, Footer } = DynamicSlugs;
const dataGallery = await getLatestBooksyPhotos();
const initDataGallery: Gallery = await dataGallery.json();
const dataInit = await getLatestBooksyReviews({
reviewsPerPage: 10,
pageIndex: 1
});
const initialData: BooksyReviewFetchResponse =
await dataInit.json();
return addApolloState
? addApolloState(apolloClient, {
props: {
Header,
Footer,
other,
popular,
Places,
businessHours,
merchandise
},
revalidate: 600
})
: {
props: {
initialData,
initDataGallery
},
revalidate: 600
};
}
注意返回的 props 是如何根据它是 SWR 还是 Apollo Client 数据来处理的?接下来是非常棒的
const dataGallery = await getLatestBooksyPhotos();
const initDataGallery: Gallery = await dataGallery.json();
const dataInit = await getLatestBooksyReviews({
reviewsPerPage: 10,
pageIndex: 1
});
const initialData: BooksyReviewFetchResponse =
await dataInit.json();
-- 它们来自 lib 目录。它们旨在在页面文件的服务器上使用,以将初始数据注入(inject) SWR。从本质上讲,它实现了与 api 路由文件相同的方法,但由于它们只能在客户端上使用,因此这是一个必要的解决方法。
现在为客户
export default function Index<T extends typeof getStaticProps>({
other,
popular,
Header,
Footer,
merchandise,
Places,
businessHours,
initialData,
initDataGallery
}: InferGetStaticPropsType<T>) {
const GalleryImageLoader = ({
src,
width,
quality
}: ImageLoaderProps) => {
return `${src}?w=${width}&q=${quality || 75}`;
};
const reviews_per_page = 10;
const [reviews_page, set_reviews_page] = useState<number>(1);
const page = useRef<number>(reviews_page);
const { data } = useSWR<BooksyReviewFetchResponse>(
() =>
`/api/booksy-fetch?reviews_page=${reviews_page}&reviews_per_page=${reviews_per_page}`,
fetcher,
initialData
);
const { data: galleryData } = useSWR<Gallery>(
'/api/booksy-images',
fetcherGallery,
initDataGallery
);
// total items
const reviewCount = data?.reviews_count ?? reviews_per_page;
// total pages
const totalPages =
(reviewCount / reviews_per_page) % reviews_per_page === 0
? reviewCount / reviews_per_page
: Math.ceil(reviewCount / reviews_per_page);
// correcting for array indeces starting at 0, not 1
const currentRangeCorrection =
reviews_per_page * page.current - (reviews_per_page - 1);
// current page range end item
const currentRangeEnd =
currentRangeCorrection + reviews_per_page - 1 <= reviewCount
? currentRangeCorrection + reviews_per_page - 1
: currentRangeCorrection +
reviews_per_page -
(reviewCount % reviews_per_page);
// current page range start item
const currentRangeStart =
page.current === 1
? page.current
: reviews_per_page * page.current - (reviews_per_page - 1);
const pages = [];
for (let i = 0; i <= reviews_page; i++) {
pages.push(
data?.reviews ? (
<BooksyReviews pageIndex={i} key={i} reviews={data.reviews}>
<nav aria-label='Pagination'>
<div className='hidden sm:block'>
<p className='text-sm text-gray-50'>
Showing{' '}
<span className='font-medium'>{`${currentRangeStart}`}</span>{' '}
to{' '}
<span className='font-medium'>{`${currentRangeEnd}`}</span>{' '}
of <span className='font-medium'>{reviewCount}</span>{' '}
reviews (page:{' '}
<span className='font-medium'>{page.current}</span> of{' '}
<span className='font-medium'>{totalPages}</span>)
</p>
</div>
<div className='flex-1 inline-flex justify-between sm:justify-center my-auto'>
<button
disabled={page.current - 1 === 0 ? true : false}
onClick={() => set_reviews_page(page.current - 1)}
className={cn('landing-page-pagination-btn', {
' cursor-not-allowed bg-redditSearch':
reviews_page - 1 === 0,
' cursor-pointer': reviews_page - 1 !== 0
})}
>
Previous
</button>
<button
disabled={page.current === totalPages ? true : false}
onClick={() => set_reviews_page(page.current + 1)}
className={cn('landing-page-pagination-btn', {
' cursor-not-allowed bg-redditSearch':
reviews_page === totalPages,
' cursor-pointer': reviews_page < totalPages
})}
>
Next
</button>
</div>
</nav>
</BooksyReviews>
) : (
<ReviewsSkeleton />
)
);
}
useEffect(() => {
(async function Update() {
return (await page.current) === reviews_page
? true
: set_reviews_page((page.current = reviews_page));
})();
}, [page.current, reviews_page]);
return (
<>
<AppLayout
title={'The Fade Room Inc.'}
Header={Header}
Footer={Footer}
>
{galleryData?.images ? (
<Grid>
{galleryData.images
.slice(6, 9)
.map((img, i) => {
<GalleryCard
key={img.image_id}
media={galleryData}
imgProps={{
loader: GalleryImageLoader,
width: i === 0 ? 1080 : 540,
height: i === 0 ? 1080 : 540
}}
/>;
})
.reverse()}
</Grid>
) : (
<LoadingSpinner />
)}
{galleryData?.images ? (
<Marquee variant='secondary'>
{galleryData.images
.slice(3, 6)
.map((img, j) => (
<GalleryCard
key={img.image_id}
media={galleryData}
variant='slim'
imgProps={{
loader: GalleryImageLoader,
width: j === 0 ? 320 : 320,
height: j === 0 ? 320 : 320
}}
/>
))
.reverse()}
</Marquee>
) : (
<LoadingSpinner />
)}
<LandingCoalesced
other={other}
popular={popular}
places={Places}
businessHours={businessHours}
merchandise={merchandise}
>
{data?.reviews ? (
<>
<>{pages[page.current]}</>
<span className='hidden'>
{
pages[
page.current < totalPages
? page.current + 1
: page.current - 1
]
}
</span>
</>
) : (
<ReviewsSkeleton />
)}
</LandingCoalesced>
</AppLayout>
</>
);
}
着陆页上有两个 useSWR 钩子(Hook):
const { data } = useSWR<BooksyReviewFetchResponse>(
() =>
`/api/booksy-fetch?reviews_page=${reviews_page}&reviews_per_page=${reviews_per_page}`,
fetcher,
initialData
);
const { data: galleryData } = useSWR<Gallery>(
'/api/booksy-images',
fetcherGallery,
initDataGallery
);
initialData
和 initDataGallery
在各自的 fetcher 之后列出的值是从服务器传输到客户端并通过 InferGetStaticPropsType<T>
推断的初始数据。 .这为客户端数据获取时的首次加载数据问题提供了解决方案。
当使用 SWR 时,您可以进行一个额外的配置来加快客户端上的数据获取,即指定应在 _document.tsx
中预加载哪些 api 路由。以及它们对应的 fetcher 的名称
<link
rel='preload'
href={`/api/booksy-fetch?reviews_page=1&reviews_per_page=10`}
as='fetcher'
crossOrigin='anonymous'
/>
<link
rel='preload'
href='/api/booksy-images'
as='fetcherGallery'
crossOrigin='anonymous'
/>
我在串联使用这两个方面遇到了 0 个问题,我已经使用了大约一个月,并且实际上将 SWR 纳入了混合 增强 DX/UX 和下一个分析反射(reflect)了(它将 FCP 时间缩短 50% 以上至 0.4 秒,将 LCP 时间缩短至 0.8 秒左右)。
关于graphql - 我可以将 useSWR 与 apollo-client 一起使用吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66851630/
我在网上搜索但没有找到任何合适的文章解释如何使用 javascript 使用 WCF 服务,尤其是 WebScriptEndpoint。 任何人都可以对此给出任何指导吗? 谢谢 最佳答案 这是一篇关于
我正在编写一个将运行 Linux 命令的 C 程序,例如: cat/etc/passwd | grep 列表 |剪切-c 1-5 我没有任何结果 *这里 parent 等待第一个 child (chi
所以我正在尝试处理文件上传,然后将该文件作为二进制文件存储到数据库中。在我存储它之后,我尝试在给定的 URL 上提供文件。我似乎找不到适合这里的方法。我需要使用数据库,因为我使用 Google 应用引
我正在尝试制作一个宏,将下面的公式添加到单元格中,然后将其拖到整个列中并在 H 列中复制相同的公式 我想在 F 和 H 列中输入公式的数据 Range("F1").formula = "=IF(ISE
问题类似于this one ,但我想使用 OperatorPrecedenceParser 解析带有函数应用程序的表达式在 FParsec . 这是我的 AST: type Expression =
我想通过使用 sequelize 和 node.js 将这个查询更改为代码取决于在哪里 select COUNT(gender) as genderCount from customers where
我正在使用GNU bash,版本5.0.3(1)-发行版(x86_64-pc-linux-gnu),我想知道为什么简单的赋值语句会出现语法错误: #/bin/bash var1=/tmp
这里,为什么我的代码在 IE 中不起作用。我的代码适用于所有浏览器。没有问题。但是当我在 IE 上运行我的项目时,它发现错误。 而且我的 jquery 类和 insertadjacentHTMl 也不
我正在尝试更改标签的innerHTML。我无权访问该表单,因此无法编辑 HTML。标签具有的唯一标识符是“for”属性。 这是输入和标签的结构:
我有一个页面,我可以在其中返回用户帖子,可以使用一些 jquery 代码对这些帖子进行即时评论,在发布新评论后,我在帖子下插入新评论以及删除 按钮。问题是 Delete 按钮在新插入的元素上不起作用,
我有一个大约有 20 列的“管道分隔”文件。我只想使用 sha1sum 散列第一列,它是一个数字,如帐号,并按原样返回其余列。 使用 awk 或 sed 执行此操作的最佳方法是什么? Accounti
我需要将以下内容插入到我的表中...我的用户表有五列 id、用户名、密码、名称、条目。 (我还没有提交任何东西到条目中,我稍后会使用 php 来做)但由于某种原因我不断收到这个错误:#1054 - U
所以我试图有一个输入字段,我可以在其中输入任何字符,但然后将输入的值小写,删除任何非字母数字字符,留下“。”而不是空格。 例如,如果我输入: 地球的 70% 是水,-!*#$^^ & 30% 土地 输
我正在尝试做一些我认为非常简单的事情,但出于某种原因我没有得到想要的结果?我是 javascript 的新手,但对 java 有经验,所以我相信我没有使用某种正确的规则。 这是一个获取输入值、检查选择
我想使用 angularjs 从 mysql 数据库加载数据。 这就是应用程序的工作原理;用户登录,他们的用户名存储在 cookie 中。该用户名显示在主页上 我想获取这个值并通过 angularjs
我正在使用 autoLayout,我想在 UITableViewCell 上放置一个 UIlabel,它应该始终位于单元格的右侧和右侧的中心。 这就是我想要实现的目标 所以在这里你可以看到我正在谈论的
我需要与 MySql 等效的 elasticsearch 查询。我的 sql 查询: SELECT DISTINCT t.product_id AS id FROM tbl_sup_price t
我正在实现代码以使用 JSON。 func setup() { if let flickrURL = NSURL(string: "https://api.flickr.com/
我尝试使用for循环声明变量,然后测试cols和rols是否相同。如果是,它将运行递归函数。但是,我在 javascript 中执行 do 时遇到问题。有人可以帮忙吗? 现在,在比较 col.1 和
我举了一个我正在处理的问题的简短示例。 HTML代码: 1 2 3 CSS 代码: .BB a:hover{ color: #000; } .BB > li:after {
我是一名优秀的程序员,十分优秀!