未验证 提交 27d7b76a 编写于 作者: J Joe Haddad 提交者: GitHub

Prevent Next.js from removing all of its styles on hydration (#18723)

Next.js would try to "recover" if its CSS assets went missing (i.e. a deployment occured) **while the page was initially loading**.

This handled a rare case where we'd try to let the Next.js complete hydrating even though a deployment occured.

However, in practice, this never worked: if the `fetch()` failed, that means the original assets never downloaded themselves (because the `fetch()` should be coming from disk cache).

Instead of letting Next.js get itself into a weird state, let's just stop hydration so that the page doesn't accidentally delete its styles.

The handle-no-styles behavior is already tested in `test/integration/css/test/index.test.js`. There was never a branch for it using its cached styles, so nothing else needs updated.

---

Fixes #17930
上级 b1503e13
......@@ -18,7 +18,11 @@ import * as envConfig from '../next-server/lib/runtime-config'
import type { NEXT_DATA } from '../next-server/lib/utils'
import { getURL, loadGetInitialProps, ST } from '../next-server/lib/utils'
import initHeadManager from './head-manager'
import PageLoader, { looseToArray, StyleSheetTuple } from './page-loader'
import PageLoader, {
INITIAL_CSS_LOAD_ERROR,
looseToArray,
StyleSheetTuple,
} from './page-loader'
import measureWebVitals from './performance-relayer'
import { createRouter, makePublicRouterInstance } from './router'
......@@ -286,6 +290,9 @@ export default async (opts: { webpackHMR?: any } = {}) => {
}
}
} catch (error) {
if (INITIAL_CSS_LOAD_ERROR in error) {
throw error
}
// This catches errors like throwing in the top level of a module
initialErr = error
}
......
import { ComponentType } from 'react'
import type { ClientSsgManifest } from '../build'
import type { ClientBuildManifest } from '../build/webpack/plugins/build-manifest-plugin'
import mitt from '../next-server/lib/mitt'
import type { MittEmitter } from '../next-server/lib/mitt'
import mitt from '../next-server/lib/mitt'
import {
addBasePath,
markLoadingError,
interpolateAs,
addLocale,
interpolateAs,
markLoadingError,
} from '../next-server/lib/router/router'
import getAssetPathFromRoute from '../next-server/lib/router/utils/get-asset-path-from-route'
import { isDynamicRoute } from '../next-server/lib/router/utils/is-dynamic'
import { parseRelativeUrl } from '../next-server/lib/router/utils/parse-relative-url'
......@@ -17,22 +16,6 @@ import { parseRelativeUrl } from '../next-server/lib/router/utils/parse-relative
export const looseToArray = <T extends {}>(input: any): T[] =>
[].slice.call(input)
function getInitialStylesheets(): StyleSheetTuple[] {
return looseToArray<CSSStyleSheet>(document.styleSheets)
.filter(
(el: CSSStyleSheet) =>
el.ownerNode &&
(el.ownerNode as Element).tagName === 'LINK' &&
(el.ownerNode as Element).hasAttribute('data-n-p')
)
.map((sheet) => ({
href: (sheet.ownerNode as Element).getAttribute('href')!,
text: looseToArray<CSSRule>(sheet.cssRules)
.map((r) => r.cssText)
.join(''),
}))
}
function hasRel(rel: string, link?: HTMLLinkElement) {
try {
link = document.createElement('link')
......@@ -44,6 +27,8 @@ function pageLoadError(route: string) {
return markLoadingError(new Error(`Error loading ${route}`))
}
export const INITIAL_CSS_LOAD_ERROR = Symbol('INITIAL_CSS_LOAD_ERROR')
const relPrefetch =
hasRel('preload') && !hasRel('prefetch')
? // https://caniuse.com/#feat=link-rel-preload
......@@ -413,7 +398,9 @@ export default class PageLoader {
// should resolve instantly.
Promise.all(cssFiles.map((d) => fetchStyleSheet(d))).catch(
(err) => {
if (isInitialLoad) return getInitialStylesheets()
if (isInitialLoad) {
Object.defineProperty(err, INITIAL_CSS_LOAD_ERROR, {})
}
throw err
}
)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册