head-manager.js 2.0 KB
Newer Older
N
nkzawa 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
import HTMLDOMPropertyConfig from 'react/lib/HTMLDOMPropertyConfig'

const DEFAULT_TITLE = ''

export default class HeadManager {
  updateHead (head) {
    const tags = {}
    head.forEach((h) => {
      const components = tags[h.type] || []
      components.push(h)
      tags[h.type] = components
    })

    this.updateTitle(tags.title ? tags.title[0] : null)

    const types = ['meta', 'base', 'link', 'style', 'script']
    types.forEach((type) => {
      this.updateElements(type, tags[type] || [])
    })
  }

  updateTitle (component) {
    let title
    if (component) {
      const { children } = component.props
D
Dan Zajdband 已提交
26
      title = typeof children === 'string' ? children : children.join('')
N
nkzawa 已提交
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
    } else {
      title = DEFAULT_TITLE
    }
    if (title !== document.title) document.title = title
  }

  updateElements (type, components) {
    const headEl = document.getElementsByTagName('head')[0]
    const oldTags = Array.prototype.slice.call(headEl.querySelectorAll(type + '.next-head'))
    const newTags = components.map(reactElementToDOM).filter((newTag) => {
      for (let i = 0, len = oldTags.length; i < len; i++) {
        const oldTag = oldTags[i]
        if (oldTag.isEqualNode(newTag)) {
          oldTags.splice(i, 1)
          return false
        }
      }
      return true
    })

    oldTags.forEach((t) => t.parentNode.removeChild(t))
    newTags.forEach((t) => headEl.appendChild(t))
  }
}

function reactElementToDOM ({ type, props }) {
  const el = document.createElement(type)
  for (const p in props) {
    if (!props.hasOwnProperty(p)) continue
D
Dan Zajdband 已提交
56
    if (p === 'children' || p === 'dangerouslySetInnerHTML') continue
N
nkzawa 已提交
57 58 59 60 61 62 63 64 65

    const attr = HTMLDOMPropertyConfig.DOMAttributeNames[p] || p.toLowerCase()
    el.setAttribute(attr, props[p])
  }

  const { children, dangerouslySetInnerHTML } = props
  if (dangerouslySetInnerHTML) {
    el.innerHTML = dangerouslySetInnerHTML.__html || ''
  } else if (children) {
D
Dan Zajdband 已提交
66
    el.textContent = typeof children === 'string' ? children : children.join('')
N
nkzawa 已提交
67 68 69
  }
  return el
}