Skip to content
This repository was archived by the owner on Aug 16, 2022. It is now read-only.

Commit 5f7d136

Browse files
committed
feat: add runtime component helpers
1 parent d413d22 commit 5f7d136

File tree

4 files changed

+320
-3
lines changed

4 files changed

+320
-3
lines changed

src/runtime/inject-style-client.js

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

src/runtime/inject-style-server.js

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/* globals __VUE_SSR_CONTEXT__ */
2+
import listToStyles from './list-to-styles'
3+
4+
export default function (parentId, list, isProduction, context) {
5+
if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {
6+
context = __VUE_SSR_CONTEXT__
7+
}
8+
if (context) {
9+
if (!context.hasOwnProperty('styles')) {
10+
Object.defineProperty(context, 'styles', {
11+
enumerable: true,
12+
get: () => renderStyles(context._styles)
13+
})
14+
// expose renderStyles for vue-server-renderer (vuejs/#6353)
15+
context._renderStyles = renderStyles
16+
}
17+
18+
const styles = context._styles || (context._styles = {})
19+
list = listToStyles(parentId, list)
20+
if (isProduction) {
21+
addStyleProd(styles, list)
22+
} else {
23+
addStyleDev(styles, list)
24+
}
25+
}
26+
}
27+
28+
// In production, render as few style tags as possible.
29+
// (mostly because IE9 has a limit on number of style tags)
30+
function addStyleProd (styles, list) {
31+
for (let i = 0; i < list.length; i++) {
32+
const parts = list[i].parts
33+
for (var j = 0; j < parts.length; j++) {
34+
const part = parts[j]
35+
// group style tags by media types.
36+
const id = part.media || 'default'
37+
const style = styles[id]
38+
if (style) {
39+
if (style.ids.indexOf(part.id) < 0) {
40+
style.ids.push(part.id)
41+
style.css += '\n' + part.css
42+
}
43+
} else {
44+
styles[id] = {
45+
ids: [part.id],
46+
css: part.css,
47+
media: part.media
48+
}
49+
}
50+
}
51+
}
52+
}
53+
54+
// In dev we use individual style tag for each module for hot-reload
55+
// and source maps.
56+
function addStyleDev (styles, list) {
57+
for (let i = 0; i < list.length; i++) {
58+
const parts = list[i].parts
59+
for (let j = 0; j < parts.length; j++) {
60+
const part = parts[j]
61+
styles[part.id] = {
62+
ids: [part.id],
63+
css: part.css,
64+
media: part.media
65+
}
66+
}
67+
}
68+
}
69+
70+
function renderStyles (styles) {
71+
var css = ''
72+
for (const key in styles) {
73+
const style = styles[key]
74+
css += '<style data-vue-ssr-id="' + style.ids.join(' ') + '"' +
75+
(style.media ? (' media="' + style.media + '"') : '') + '>' +
76+
style.css + '</style>'
77+
}
78+
return css
79+
}

src/runtime/list-to-styles.js

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* Translates the list format produced by css-loader into something
3+
* easier to manipulate.
4+
*/
5+
export default function listToStyles (parentId, list) {
6+
var styles = []
7+
var newStyles = {}
8+
for (var i = 0; i < list.length; i++) {
9+
var item = list[i]
10+
var id = item[0]
11+
var css = item[1]
12+
var media = item[2]
13+
var sourceMap = item[3]
14+
var part = {
15+
id: parentId + ':' + i,
16+
css: css,
17+
media: media,
18+
sourceMap: sourceMap
19+
}
20+
if (!newStyles[id]) {
21+
styles.push(newStyles[id] = { id: id, parts: [part] })
22+
} else {
23+
newStyles[id].parts.push(part)
24+
}
25+
}
26+
return styles
27+
}

src/normalize-component.js renamed to src/runtime/normalize-component.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
/* globals __VUE_SSR_CONTEXT__ */
22

3-
// this module is a runtime utility for cleaner component module output and will
4-
// be included in the final webpack user bundle
3+
// this module is a runtime utility for cleaner component module output.
54

6-
module.exports = function normalizeComponent (
5+
export default function normalizeComponent (
76
rawScriptExports,
87
compiledTemplate,
98
injectStyles,

0 commit comments

Comments
 (0)