1
0
mirror of synced 2025-12-23 21:07:12 -05:00

Performance testing NextJS / React (#19540)

* Remove json.parse(json.stringify( usage to improve performance
* Fix missing / duplicate keys in some renders
* Fix missing react-is dependency (only seemed to cause problems from pruning on the heroku deploy)
* Add nextjs tag to datadog-connect config when nextjs is a query param
* Fix router.asPath usage to exclude query param
* Fix styling inconsistencies noticed when testing
* Add a few tests
This commit is contained in:
Mike Surowiec
2021-05-27 15:17:27 -07:00
committed by GitHub
parent 7f65390cf9
commit 2de358187c
13 changed files with 112 additions and 44 deletions

View File

@@ -38,6 +38,7 @@ export const DefaultLayout = (props: Props) => {
{page.languageVariants.map((languageVariant) => {
return (
<link
key={languageVariant.href}
rel="alternate"
hrefLang={languageVariant.hreflang}
href={`https://docs.github.com${languageVariant.href}`}

View File

@@ -3,7 +3,7 @@ import Link from 'next/link'
import { LinkExternalIcon, MarkGithubIcon } from '@primer/octicons-react'
import { useTranslation } from './hooks/useTranslation'
import { useMainContext } from './context/MainContext'
import { ProductSiteTree } from './product/ProductSiteTree'
import { SidebarProduct } from './product/SidebarProduct'
import { AllProductsLink } from './product/AllProductsLink'
import { useVersion } from './hooks/useVersion'
@@ -41,7 +41,7 @@ export const SidebarNav = (props: Props) => {
</ul>
) : (
<ul className="sidebar-products">
<ProductSiteTree />
<SidebarProduct />
</ul>
)}
</nav>

View File

@@ -141,7 +141,11 @@ export const getMainContextFromRequest = (req: any): MainContextT => {
),
hidden: req.context.page.hidden || false,
},
enterpriseServerReleases: JSON.parse(JSON.stringify(req.context.enterpriseServerReleases)),
enterpriseServerReleases: pick(req.context.enterpriseServerReleases, [
'isOldestReleaseDeprecated',
'oldestSupported',
'nextDeprecationDate',
]),
enterpriseServerVersions: req.context.enterpriseServerVersions,
currentLanguage: req.context.currentLanguage,
languages: Object.fromEntries(
@@ -158,12 +162,27 @@ export const getMainContextFromRequest = (req: any): MainContextT => {
})
),
allVersions: req.context.allVersions,
// this gets rid of some `undefined` values, which is necessary so next.js can serialize the data
currentProductTree: JSON.parse(JSON.stringify(req.context.currentProductTree)),
currentProductTree: getCurrentProductTree(req.context.currentProductTree),
featureFlags: {},
}
}
// only pull things we need from the product tree, and make sure there are default values instead of `undefined`
const getCurrentProductTree = (input: any): CurrentProductTree => {
return {
href: input.href,
renderedShortTitle: input.renderedShortTitle || '',
renderedFullTitle: input.renderedFullTitle || '',
page: {
hidden: input.page.hidden || false,
documentType: input.page.documentType,
title: input.page.title,
shortTitle: input.page.shortTitle || '',
},
childPages: (input.childPages || []).map(getCurrentProductTree),
}
}
export const MainContext = createContext<MainContextT | null>(null)
export const useMainContext = (): MainContextT => {

View File

@@ -21,7 +21,7 @@ export const FeaturedArticles = () => {
{featuredArticles.map((section, i) => {
return (
<div
key={section.label}
key={section.label + i}
className={cx('col-12 mb-4 mb-lg-0', changelog ? 'col-lg-4' : 'col-lg-6')}
>
<ArticleList
@@ -72,7 +72,7 @@ const ArticleList = ({ title, viewAllHref, articles }: ArticleListProps) => {
</div>
<ul className="list-style-none">
{articles.map((link) => {
{articles.map((link, i) => {
return (
<li key={link.href} className="border-top">
<Link href={link.href}>

View File

@@ -16,12 +16,12 @@ export const ProductArticlesList = () => {
return (
<div className="d-flex gutter flex-wrap">
{currentProductTree.childPages.map((childPage) => {
{currentProductTree.childPages.map((childPage, i) => {
if (childPage.page.documentType === 'article') {
return null
}
return <ArticleList key={childPage.href} page={childPage} />
return <ArticleList key={childPage.href + i} page={childPage} />
})}
</div>
)
@@ -45,7 +45,10 @@ const ArticleList = ({ page }: { page: CurrentProductTree }) => {
}
return (
<li className={cx('mb-3', index >= maxArticles ? 'd-none' : null)}>
<li
key={grandchildPage.href + index}
className={cx('mb-3', index >= maxArticles ? 'd-none' : null)}
>
<Link href={grandchildPage.href}>
<a>{grandchildPage.page.title}</a>
</Link>

View File

@@ -9,7 +9,11 @@ export const TableOfContents = (props: { items?: Array<TocItem> }) => {
return (
<div>
{(props.items || []).map(({ fullPath: href, title, intro }) => {
{(props.items || []).map((obj) => {
if (!obj) {
return null
}
const { fullPath: href, title, intro } = obj
const isActive = router.pathname === href
return (
<div key={href} className={cx('mb-5', isActive && 'color-auto-gray-4')}>

View File

@@ -11,7 +11,7 @@ import { AllProductsLink } from 'components/product/AllProductsLink'
// hierarchy of the current page. If an item's URL is also the same as the current URL, the item
// also gets an `is-current-page` class.
// -->
export const ProductSiteTree = () => {
export const SidebarProduct = () => {
const router = useRouter()
const { currentProductTree: currentProductTree } = useMainContext()
@@ -20,14 +20,16 @@ export const ProductSiteTree = () => {
}
const productTitle = currentProductTree.renderedShortTitle || currentProductTree.renderedFullTitle
const routePath = `/${router.locale}${router.asPath.split('?')[0]}` // remove query string
return (
<>
<AllProductsLink />
{!currentProductTree.page.hidden && (
<>
<li title="" className="sidebar-product mb-2">
<Link href={currentProductTree.href}>
<a className="pl-4 pr-5 pb-1 f4">{productTitle}</a>
<a className="pl-4 pr-5 pb-1 f4 color-text-primary">{productTitle}</a>
</Link>
</li>
@@ -37,10 +39,11 @@ export const ProductSiteTree = () => {
const isStandaloneCategory = childPage.page.documentType === 'article'
const childTitle = childPage.renderedShortTitle || childPage.renderedFullTitle
const isActive = router.asPath.includes(childPage.href)
const isCurrent = router.asPath === childPage.href
const isActive = routePath.includes(routePath)
const isCurrent = routePath === childPage.href
return (
<li
key={childPage.href + i}
className={cx(
'sidebar-category py-1',
isActive && 'active',
@@ -50,21 +53,19 @@ export const ProductSiteTree = () => {
>
{isStandaloneCategory ? (
<Link href={childPage.href}>
<a className="pl-4 pr-2 py-2 f6 text-uppercase d-block flex-auto mr-3">
<a className="pl-4 pr-2 py-2 f6 text-uppercase d-block flex-auto mr-3 color-text-primary">
{childTitle}
</a>
</Link>
) : (
<details
className={cx(
'dropdown-withArrow details details-reset',
router.asPath.includes(childPage.href) || i < 3 ? 'open' : ''
)}
className={cx('dropdown-withArrow details details-reset')}
open={routePath.includes(childPage.href) || i < 3}
>
<summary>
<div className="d-flex flex-justify-between">
<Link href={childPage.href}>
<a className="pl-4 pr-2 py-2 f6 text-uppercase d-block flex-auto mr-3">
<a className="pl-4 pr-2 py-2 f6 text-uppercase d-block flex-auto mr-3 color-text-primary">
{childTitle}
</a>
</Link>
@@ -87,19 +88,20 @@ export const ProductSiteTree = () => {
)}
</div>
</summary>
{router.asPath.includes(childPage.href) || i < 3 ? (
{routePath.includes(childPage.href) || i < 3 ? (
<>
{/* <!-- some categories have maptopics with child articles --> */}
{childPage.childPages[0].page.documentType === 'mapTopic' ? (
<ul className="sidebar-topics list-style-none position-relative">
{childPage.childPages.map((grandchildPage) => {
{childPage.childPages.map((grandchildPage, i) => {
const grandchildTitle =
grandchildPage.renderedShortTitle ||
grandchildPage.renderedFullTitle
const isActive = router.asPath.includes(grandchildPage.href)
const isCurrent = router.asPath === grandchildPage.href
const isActive = routePath.includes(grandchildPage.href)
const isCurrent = routePath === grandchildPage.href
return (
<li
key={childPage.href + i}
className={cx(
'sidebar-maptopic',
isActive && 'active',
@@ -107,7 +109,7 @@ export const ProductSiteTree = () => {
)}
>
<Link href={grandchildPage.href}>
<a className="pl-4 pr-5 py-2">{grandchildTitle}</a>
<a className="pl-4 pr-5 py-2 color-text-primary">{grandchildTitle}</a>
</Link>
<ul className="sidebar-articles my-2">
{grandchildPage.childPages.map(
@@ -116,13 +118,13 @@ export const ProductSiteTree = () => {
greatgrandchildPage.renderedShortTitle ||
greatgrandchildPage.renderedFullTitle
const isLast = i === arr.length - 1
const isActive = router.asPath.includes(
const isActive = routePath.includes(
greatgrandchildPage.href
)
const isCurrent =
router.asPath === greatgrandchildPage.href
const isCurrent = routePath === greatgrandchildPage.href
return (
<li
key={greatgrandchildPage.href + i}
className={cx(
'sidebar-article',
isActive && 'active',
@@ -132,7 +134,7 @@ export const ProductSiteTree = () => {
<Link href={greatgrandchildPage.href}>
<a
className={cx(
'pl-6 pr-5 py-1',
'pl-6 pr-5 py-1 color-text-primary',
isLast && 'pb-2'
)}
>
@@ -156,10 +158,11 @@ export const ProductSiteTree = () => {
grandchildPage.renderedShortTitle ||
grandchildPage.renderedFullTitle
const isLast = i === arr.length - 1
const isActive = router.asPath.includes(grandchildPage.href)
const isCurrent = router.asPath === grandchildPage.href
const isActive = routePath.includes(grandchildPage.href)
const isCurrent = routePath === grandchildPage.href
return (
<li
key={grandchildPage.href + i}
className={cx(
'sidebar-article',
isActive && 'active',
@@ -167,7 +170,7 @@ export const ProductSiteTree = () => {
)}
>
<Link href={grandchildPage.href}>
<a className={cx('pl-6 pr-5 py-1', isLast && 'pb-2')}>
<a className={cx('pl-6 pr-5 py-1 color-text-primary', isLast && 'pb-2')}>
{grandchildTitle}
</a>
</Link>