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

fix(next/head): assign bool attrs to match server (#20748)

This pull request correctly assigns boolean attributes for `<script />` to match the element as it is created by a server-side render.

Prior to this pull request, we'd double-execute `<script>` tags with the `async`, `defer`, or `nomodule` property.

---

Fixes #9070
上级 fc75baeb
......@@ -3,10 +3,11 @@ export const DOMAttributeNames: Record<string, string> = {
className: 'class',
htmlFor: 'for',
httpEquiv: 'http-equiv',
noModule: 'noModule',
}
function reactElementToDOM({ type, props }: JSX.Element): HTMLElement {
const el = document.createElement(type)
const el: HTMLElement = document.createElement(type)
for (const p in props) {
if (!props.hasOwnProperty(p)) continue
if (p === 'children' || p === 'dangerouslySetInnerHTML') continue
......@@ -15,7 +16,14 @@ function reactElementToDOM({ type, props }: JSX.Element): HTMLElement {
if (props[p] === undefined) continue
const attr = DOMAttributeNames[p] || p.toLowerCase()
el.setAttribute(attr, props[p])
if (
type === 'script' &&
(attr === 'async' || attr === 'defer' || attr === 'noModule')
) {
;(el as HTMLScriptElement)[attr] = !!props[p]
} else {
el.setAttribute(attr, props[p])
}
}
const { children, dangerouslySetInnerHTML } = props
......
......@@ -115,6 +115,11 @@ export default () => (
<link rel="stylesheet" href="dedupe-style.css" key="my-style" />
<link rel="stylesheet" href="dedupe-style.css" key="my-style" />
{/* this should not execute twice on the client */}
<script src="/test-async.js" async></script>
{/* this should not execute twice on the client (intentionally sets defer to `yas` to test boolean coercion) */}
<script src="/test-defer.js" defer="yas"></script>
{/* such style can be used for alternate links on _app vs individual pages */}
{['pl', 'en'].map((language) => (
<link
......
window.__test_async_executions = (window.__test_async_executions || 0) + 1
window.__test_defer_executions = (window.__test_defer_executions || 0) + 1
......@@ -1314,6 +1314,26 @@ describe('Client Navigation', () => {
})
describe('updating head while client routing', () => {
it('should only execute async and defer scripts once', async () => {
let browser
try {
browser = await webdriver(context.appPort, '/head')
await browser.waitForElementByCss('h1')
await waitFor(2000)
expect(
Number(await browser.eval('window.__test_async_executions'))
).toBe(1)
expect(
Number(await browser.eval('window.__test_defer_executions'))
).toBe(1)
} finally {
if (browser) {
await browser.close()
}
}
})
it('should update head during client routing', async () => {
let browser
try {
......
......@@ -186,6 +186,12 @@ export default function (render, fetch, ctx) {
expect(html).toContain('<meta content="meta fragment"/>')
})
test('header helper renders boolean attributes correctly children', async () => {
const html = await render('/head')
expect(html).toContain('<script src="/test-async.js" async="">')
expect(html).toContain('<script src="/test-defer.js" defer="">')
})
it('should render the page with custom extension', async () => {
const html = await render('/custom-extension')
expect(html).toContain('<div>Hello</div>')
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册