You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

222 lines
6.1 KiB

4 years ago
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. Modified by Evan You @yyx990803
  5. */
  6. import listToStyles from './listToStyles'
  7. var hasDocument = typeof document !== 'undefined'
  8. if (typeof DEBUG !== 'undefined' && DEBUG) {
  9. if (!hasDocument) {
  10. throw new Error(
  11. 'vue-style-loader cannot be used in a non-browser environment. ' +
  12. "Use { target: 'node' } in your Webpack config to indicate a server-rendering environment."
  13. ) }
  14. }
  15. /*
  16. type StyleObject = {
  17. id: number;
  18. parts: Array<StyleObjectPart>
  19. }
  20. type StyleObjectPart = {
  21. css: string;
  22. media: string;
  23. sourceMap: ?string
  24. }
  25. */
  26. var stylesInDom = {/*
  27. [id: number]: {
  28. id: number,
  29. refs: number,
  30. parts: Array<(obj?: StyleObjectPart) => void>
  31. }
  32. */}
  33. var head = hasDocument && (document.head || document.getElementsByTagName('head')[0])
  34. var singletonElement = null
  35. var singletonCounter = 0
  36. var isProduction = false
  37. var noop = function () {}
  38. var options = null
  39. var ssrIdKey = 'data-vue-ssr-id'
  40. // Force single-tag solution on IE6-9, which has a hard limit on the # of <style>
  41. // tags it will allow on a page
  42. var isOldIE = typeof navigator !== 'undefined' && /msie [6-9]\b/.test(navigator.userAgent.toLowerCase())
  43. export default function addStylesClient (parentId, list, _isProduction, _options) {
  44. isProduction = _isProduction
  45. options = _options || {}
  46. var styles = listToStyles(parentId, list)
  47. addStylesToDom(styles)
  48. return function update (newList) {
  49. var mayRemove = []
  50. for (var i = 0; i < styles.length; i++) {
  51. var item = styles[i]
  52. var domStyle = stylesInDom[item.id]
  53. domStyle.refs--
  54. mayRemove.push(domStyle)
  55. }
  56. if (newList) {
  57. styles = listToStyles(parentId, newList)
  58. addStylesToDom(styles)
  59. } else {
  60. styles = []
  61. }
  62. for (var i = 0; i < mayRemove.length; i++) {
  63. var domStyle = mayRemove[i]
  64. if (domStyle.refs === 0) {
  65. for (var j = 0; j < domStyle.parts.length; j++) {
  66. domStyle.parts[j]()
  67. }
  68. delete stylesInDom[domStyle.id]
  69. }
  70. }
  71. }
  72. }
  73. function addStylesToDom (styles /* Array<StyleObject> */) {
  74. for (var i = 0; i < styles.length; i++) {
  75. var item = styles[i]
  76. var domStyle = stylesInDom[item.id]
  77. if (domStyle) {
  78. domStyle.refs++
  79. for (var j = 0; j < domStyle.parts.length; j++) {
  80. domStyle.parts[j](item.parts[j])
  81. }
  82. for (; j < item.parts.length; j++) {
  83. domStyle.parts.push(addStyle(item.parts[j]))
  84. }
  85. if (domStyle.parts.length > item.parts.length) {
  86. domStyle.parts.length = item.parts.length
  87. }
  88. } else {
  89. var parts = []
  90. for (var j = 0; j < item.parts.length; j++) {
  91. parts.push(addStyle(item.parts[j]))
  92. }
  93. stylesInDom[item.id] = { id: item.id, refs: 1, parts: parts }
  94. }
  95. }
  96. }
  97. function createStyleElement () {
  98. var styleElement = document.createElement('style')
  99. styleElement.type = 'text/css'
  100. head.appendChild(styleElement)
  101. return styleElement
  102. }
  103. function addStyle (obj /* StyleObjectPart */) {
  104. var update, remove
  105. var styleElement = document.querySelector('style[' + ssrIdKey + '~="' + obj.id + '"]')
  106. if (styleElement) {
  107. if (isProduction) {
  108. // has SSR styles and in production mode.
  109. // simply do nothing.
  110. return noop
  111. } else {
  112. // has SSR styles but in dev mode.
  113. // for some reason Chrome can't handle source map in server-rendered
  114. // style tags - source maps in <style> only works if the style tag is
  115. // created and inserted dynamically. So we remove the server rendered
  116. // styles and inject new ones.
  117. styleElement.parentNode.removeChild(styleElement)
  118. }
  119. }
  120. if (isOldIE) {
  121. // use singleton mode for IE9.
  122. var styleIndex = singletonCounter++
  123. styleElement = singletonElement || (singletonElement = createStyleElement())
  124. update = applyToSingletonTag.bind(null, styleElement, styleIndex, false)
  125. remove = applyToSingletonTag.bind(null, styleElement, styleIndex, true)
  126. } else {
  127. // use multi-style-tag mode in all other cases
  128. styleElement = createStyleElement()
  129. update = applyToTag.bind(null, styleElement)
  130. remove = function () {
  131. styleElement.parentNode.removeChild(styleElement)
  132. }
  133. }
  134. update(obj)
  135. return function updateStyle (newObj /* StyleObjectPart */) {
  136. if (newObj) {
  137. if (newObj.css === obj.css &&
  138. newObj.media === obj.media &&
  139. newObj.sourceMap === obj.sourceMap) {
  140. return
  141. }
  142. update(obj = newObj)
  143. } else {
  144. remove()
  145. }
  146. }
  147. }
  148. var replaceText = (function () {
  149. var textStore = []
  150. return function (index, replacement) {
  151. textStore[index] = replacement
  152. return textStore.filter(Boolean).join('\n')
  153. }
  154. })()
  155. function applyToSingletonTag (styleElement, index, remove, obj) {
  156. var css = remove ? '' : obj.css
  157. if (styleElement.styleSheet) {
  158. styleElement.styleSheet.cssText = replaceText(index, css)
  159. } else {
  160. var cssNode = document.createTextNode(css)
  161. var childNodes = styleElement.childNodes
  162. if (childNodes[index]) styleElement.removeChild(childNodes[index])
  163. if (childNodes.length) {
  164. styleElement.insertBefore(cssNode, childNodes[index])
  165. } else {
  166. styleElement.appendChild(cssNode)
  167. }
  168. }
  169. }
  170. function applyToTag (styleElement, obj) {
  171. var css = obj.css
  172. var media = obj.media
  173. var sourceMap = obj.sourceMap
  174. if (media) {
  175. styleElement.setAttribute('media', media)
  176. }
  177. if (options.ssrId) {
  178. styleElement.setAttribute(ssrIdKey, obj.id)
  179. }
  180. if (sourceMap) {
  181. // https://developer.chrome.com/devtools/docs/javascript-debugging
  182. // this makes source maps inside style tags work properly in Chrome
  183. css += '\n/*# sourceURL=' + sourceMap.sources[0] + ' */'
  184. // http://stackoverflow.com/a/26603875
  185. css += '\n/*# sourceMappingURL=data:application/json;base64,' + btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))) + ' */'
  186. }
  187. if (styleElement.styleSheet) {
  188. styleElement.styleSheet.cssText = css
  189. } else {
  190. while (styleElement.firstChild) {
  191. styleElement.removeChild(styleElement.firstChild)
  192. }
  193. styleElement.appendChild(document.createTextNode(css))
  194. }
  195. }