样式同构-核心库isomorphic-style-loader 学习

61 阅读1分钟

前言

前后端渲染样式同构,React版本的公共库isomorphic-style-loader。

源码学习

前端渲染入口代码块:

const insertCss = (...styles) => {
    const removeCss = styles.map(style => style._insertCss())
    return () => removeCss.forEach(dispose => dispose())
}

样式标签插入 insertCss

//  备注来自参考文章
//  全局的给各个样式引用次数计数的obj
const inserted = {}

//  组件卸载时的样式标签移除
function removeCss(ids) {
  ids.forEach((id) => {
    //  通过inserted的对应id来计数,如果不再有引用就移除节点
    if (--inserted[id] <= 0) {
      const elem = document.getElementById(id)
      if (elem) {
        elem.parentNode.removeChild(elem)
      }
    }
  })
}

/**
 * Example:
 *   // Insert CSS styles object generated by `css-loader` into DOM
 *   var removeCss = insertCss([[1, 'body { color: red; }']]);
 *
 *   // Remove it from the DOM
 *   removeCss();
 */
 // 插入样式
function insertCss(styles, { replace = false, prepend = false, prefix = 's' } = {}) {
  const ids = []
  for (let i = 0; i < styles.length; i++) {
    const [moduleId, css, media, sourceMap] = styles[i]
    //  生成唯一样式id
    const id = `${prefix}${moduleId}-${i}`

    ids.push(id)
    //  如果用同id的样式且不启用replace(即启用同名样式替换)选项,则增加该id样式的引用次数
    if (inserted[id]) {
      if (!replace) {
        inserted[id]++
        continue
      }
    }
    //  如果启用replace,计数置1
    inserted[id] = 1

    let elem = document.getElementById(id)
    let create = false

    //  如果没有对应的节点,则创建一个新的节点
    if (!elem) {
      create = true
      //    创建一个style标签,并且设置type和id
      elem = document.createElement('style')
      elem.setAttribute('type', 'text/css')
      elem.id = id

      if (media) {
        elem.setAttribute('media', media)
      }
    }

    let cssText = css
    //  sourceMap相关,支持调试
    if (sourceMap && typeof btoa === 'function') {
      // skip IE9 and below, see http://caniuse.com/atob-btoa
      cssText += `\n/*# sourceMappingURL=data:application/json;base64,${b64EncodeUnicode(
        JSON.stringify(sourceMap),
      )}*/`
      cssText += `\n/*# sourceURL=${sourceMap.file}?${id}*/`
    }

    //  给标签插入样式内容,如果是老IE浏览器,则使用styleSheet.cssText来注册样式,常规情况使用textContent属性
    if ('textContent' in elem) {
      elem.textContent = cssText
    } else {
      elem.styleSheet.cssText = cssText
    }

    // 在HTML文件中挂载标签
    if (create) {
      //    如果使用prepend(前置),则将标签挂载在第一个元素
      if (prepend) {
        document.head.insertBefore(elem, document.head.childNodes[0])
      } else {
        document.head.appendChild(elem)
      }
    }
  }
  //  返回卸载样式的函数
  return removeCss.bind(null, ids)
}

参考文章

isomorphic-style-loader在前后端渲染样式同构中的应用与源码分析