1
0
Fork 0
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:
Li Weinan 2017-11-01 05:52:51 +08:00 committed by Tim Neutkens
parent c8059b9f12
commit 190853b4ff
5 changed files with 48 additions and 1 deletions

View file

@ -48,11 +48,16 @@ const METATYPES = ['name', 'httpEquiv', 'charSet', 'itemProp', 'property']
// which shouldn't be duplicated, like <title/>. // which shouldn't be duplicated, like <title/>.
function unique () { function unique () {
const keys = new Set()
const tags = new Set() const tags = new Set()
const metaTypes = new Set() const metaTypes = new Set()
const metaCategories = {} const metaCategories = {}
return (h) => { return (h) => {
if (h.key) {
if (keys.has(h.key)) return false
keys.add(h.key)
}
switch (h.type) { switch (h.type) {
case 'title': case 'title':
case 'base': case 'base':

View file

@ -197,6 +197,26 @@ export default () =>
</div> </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_ _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 ### Fetching data and component lifecycle

View file

@ -88,7 +88,7 @@ export class Head extends Component {
<link rel='preload' href={`${assetPrefix}/_next/${buildId}/page/_error/index.js`} as='script' /> <link rel='preload' href={`${assetPrefix}/_next/${buildId}/page/_error/index.js`} as='script' />
{this.getPreloadDynamicChunks()} {this.getPreloadDynamicChunks()}
{this.getPreloadMainLinks()} {this.getPreloadMainLinks()}
{(head || []).map((h, i) => React.cloneElement(h, { key: i }))} {(head || []).map((h, i) => React.cloneElement(h, { key: h.key || i }))}
{styles || null} {styles || null}
{this.props.children} {this.props.children}
</head> </head>

View file

@ -3,8 +3,20 @@ import Head from 'next/head'
export default () => <div> export default () => <div>
<Head> <Head>
{/* this will not render */}
<meta charSet='utf-8' />
{/* this will get rendered */}
<meta charSet='iso-8859-5' /> <meta charSet='iso-8859-5' />
<meta content='my meta' /> <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> </Head>
<h1>I can haz meta tags</h1> <h1>I can haz meta tags</h1>
</div> </div>

View file

@ -28,6 +28,16 @@ export default function ({ app }, suiteName, render) {
expect(html.includes('I can haz meta tags')).toBeTruthy() 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 () => { test('renders styled jsx', async () => {
const $ = await get$('/styled-jsx') const $ = await get$('/styled-jsx')
const styleId = $('#blue-box').attr('class') const styleId = $('#blue-box').attr('class')