mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
Support de-deduping head tags by setting key (#3170)
* Support de-deduping head tags by setting key * move dedupe logic to `unique` function * fix head tag deduping logic * remove console.log * use `toContain` assertions * update de-duping head tags section in README
This commit is contained in:
parent
c8059b9f12
commit
190853b4ff
|
@ -48,11 +48,16 @@ const METATYPES = ['name', 'httpEquiv', 'charSet', 'itemProp', 'property']
|
|||
// which shouldn't be duplicated, like <title/>.
|
||||
|
||||
function unique () {
|
||||
const keys = new Set()
|
||||
const tags = new Set()
|
||||
const metaTypes = new Set()
|
||||
const metaCategories = {}
|
||||
|
||||
return (h) => {
|
||||
if (h.key) {
|
||||
if (keys.has(h.key)) return false
|
||||
keys.add(h.key)
|
||||
}
|
||||
switch (h.type) {
|
||||
case 'title':
|
||||
case 'base':
|
||||
|
|
20
readme.md
20
readme.md
|
@ -197,6 +197,26 @@ export default () =>
|
|||
</div>
|
||||
```
|
||||
|
||||
To avoid duplicate tags in your `<head>` you can use the `key` property, which will make sure the tag is only rendered once:
|
||||
|
||||
```jsx
|
||||
import Head from 'next/head'
|
||||
export default () => (
|
||||
<div>
|
||||
<Head>
|
||||
<title>My page title</title>
|
||||
<meta name="viewport" content="initial-scale=1.0, width=device-width" key="viewport" />
|
||||
</Head>
|
||||
<Head>
|
||||
<meta name="viewport" content="initial-scale=1.2, width=device-width" key="viewport" />
|
||||
</Head>
|
||||
<p>Hello world!</p>
|
||||
</div>
|
||||
)
|
||||
```
|
||||
|
||||
In this case only the second `<meta name="viewport" />` is rendered.
|
||||
|
||||
_Note: The contents of `<head>` get cleared upon unmounting the component, so make sure each page completely defines what it needs in `<head>`, without making assumptions about what other pages added_
|
||||
|
||||
### Fetching data and component lifecycle
|
||||
|
|
|
@ -88,7 +88,7 @@ export class Head extends Component {
|
|||
<link rel='preload' href={`${assetPrefix}/_next/${buildId}/page/_error/index.js`} as='script' />
|
||||
{this.getPreloadDynamicChunks()}
|
||||
{this.getPreloadMainLinks()}
|
||||
{(head || []).map((h, i) => React.cloneElement(h, { key: i }))}
|
||||
{(head || []).map((h, i) => React.cloneElement(h, { key: h.key || i }))}
|
||||
{styles || null}
|
||||
{this.props.children}
|
||||
</head>
|
||||
|
|
|
@ -3,8 +3,20 @@ import Head from 'next/head'
|
|||
|
||||
export default () => <div>
|
||||
<Head>
|
||||
{/* this will not render */}
|
||||
<meta charSet='utf-8' />
|
||||
{/* this will get rendered */}
|
||||
<meta charSet='iso-8859-5' />
|
||||
|
||||
<meta content='my meta' />
|
||||
|
||||
{/* the following 2 links tag will be rendered both */}
|
||||
<link rel='stylesheet' href='/dup-style.css' />
|
||||
<link rel='stylesheet' href='/dup-style.css' />
|
||||
|
||||
{/* only one tag will be rendered as they have the same key */}
|
||||
<link rel='stylesheet' href='dedupe-style.css' key='my-style' />
|
||||
<link rel='stylesheet' href='dedupe-style.css' key='my-style' />
|
||||
</Head>
|
||||
<h1>I can haz meta tags</h1>
|
||||
</div>
|
||||
|
|
|
@ -28,6 +28,16 @@ export default function ({ app }, suiteName, render) {
|
|||
expect(html.includes('I can haz meta tags')).toBeTruthy()
|
||||
})
|
||||
|
||||
test('header helper dedupes tags', async () => {
|
||||
const html = await (render('/head'))
|
||||
expect(html).toContain('<meta charSet="iso-8859-5" class="next-head"/>')
|
||||
expect(html).not.toContain('<meta charSet="utf-8" class="next-head"/>')
|
||||
expect(html).toContain('<meta content="my meta" class="next-head"/>')
|
||||
expect(html).toContain('<link rel="stylesheet" href="/dup-style.css" class="next-head"/><link rel="stylesheet" href="/dup-style.css" class="next-head"/>')
|
||||
expect(html).toContain('<link rel="stylesheet" href="dedupe-style.css" class="next-head"/>')
|
||||
expect(html).not.toContain('<link rel="stylesheet" href="dedupe-style.css" class="next-head"/><link rel="stylesheet" href="dedupe-style.css" class="next-head"/>')
|
||||
})
|
||||
|
||||
test('renders styled jsx', async () => {
|
||||
const $ = await get$('/styled-jsx')
|
||||
const styleId = $('#blue-box').attr('class')
|
||||
|
|
Loading…
Reference in a new issue