mirror of
https://github.com/vuejs/vue.git
synced 2024-11-22 04:39:46 +00:00
fix(ssr): properly handle invalid and numeric style properties
Ignores values that are not string or numbers, and append px as default unit to appropriate properties. There will still be certain cases where the user simply provides an invalid string value to a property which will be too costly to detect (it's possible by using the `cssstyle` package, but very heavy). But in such cases the user would already notice the style is not working on the client, so it's not really worth it for the perf implications. fix #9231
This commit is contained in:
parent
fe2b27ffb9
commit
7d9cfebe39
@ -1,6 +1,6 @@
|
||||
/* @flow */
|
||||
|
||||
import { escape } from '../util'
|
||||
import { escape, noUnitNumericStyleProps } from '../util'
|
||||
import { hyphenate } from 'shared/util'
|
||||
import { getStyle } from 'web/util/style'
|
||||
|
||||
@ -11,15 +11,34 @@ export function genStyle (style: Object): string {
|
||||
const hyphenatedKey = hyphenate(key)
|
||||
if (Array.isArray(value)) {
|
||||
for (let i = 0, len = value.length; i < len; i++) {
|
||||
styleText += `${hyphenatedKey}:${value[i]};`
|
||||
styleText += normalizeValue(hyphenatedKey, value[i])
|
||||
}
|
||||
} else {
|
||||
styleText += `${hyphenatedKey}:${value};`
|
||||
styleText += normalizeValue(hyphenatedKey, value)
|
||||
}
|
||||
}
|
||||
return styleText
|
||||
}
|
||||
|
||||
function normalizeValue(key: string, value: any): string {
|
||||
if (typeof value === 'string') {
|
||||
return `${key}:${value};`
|
||||
} else if (typeof value === 'number') {
|
||||
// Handle numeric values.
|
||||
// Turns out all evergreen browsers plus IE11 already support setting plain
|
||||
// numbers on the style object and will automatically convert it to px if
|
||||
// applicable, so we should support that on the server too.
|
||||
if (noUnitNumericStyleProps[key]) {
|
||||
return `${key}:${value};`
|
||||
} else {
|
||||
return `${key}:${value}px;`
|
||||
}
|
||||
} else {
|
||||
// invalid values
|
||||
return ``
|
||||
}
|
||||
}
|
||||
|
||||
export default function renderStyle (vnode: VNodeWithData): ?string {
|
||||
const styleText = genStyle(getStyle(vnode, false))
|
||||
if (styleText !== '') {
|
||||
|
@ -54,3 +54,48 @@ export function escape (s: string) {
|
||||
function escapeChar (a) {
|
||||
return ESC[a] || a
|
||||
}
|
||||
|
||||
export const noUnitNumericStyleProps = {
|
||||
"animation-iteration-count": true,
|
||||
"border-image-outset": true,
|
||||
"border-image-slice": true,
|
||||
"border-image-width": true,
|
||||
"box-flex": true,
|
||||
"box-flex-group": true,
|
||||
"box-ordinal-group": true,
|
||||
"column-count": true,
|
||||
"columns": true,
|
||||
"flex": true,
|
||||
"flex-grow": true,
|
||||
"flex-positive": true,
|
||||
"flex-shrink": true,
|
||||
"flex-negative": true,
|
||||
"flex-order": true,
|
||||
"grid-row": true,
|
||||
"grid-row-end": true,
|
||||
"grid-row-span": true,
|
||||
"grid-row-start": true,
|
||||
"grid-column": true,
|
||||
"grid-column-end": true,
|
||||
"grid-column-span": true,
|
||||
"grid-column-start": true,
|
||||
"font-weight": true,
|
||||
"line-clamp": true,
|
||||
"line-height": true,
|
||||
"opacity": true,
|
||||
"order": true,
|
||||
"orphans": true,
|
||||
"tab-size": true,
|
||||
"widows": true,
|
||||
"z-index": true,
|
||||
"zoom": true,
|
||||
// SVG
|
||||
"fill-opacity": true,
|
||||
"flood-opacity": true,
|
||||
"stop-opacity": true,
|
||||
"stroke-dasharray": true,
|
||||
"stroke-dashoffset": true,
|
||||
"stroke-miterlimit": true,
|
||||
"stroke-opacity": true,
|
||||
"stroke-width": true
|
||||
}
|
||||
|
@ -69,4 +69,3 @@ export function getStyle (vnode: VNodeWithData, checkChild: boolean): Object {
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
|
@ -1499,6 +1499,47 @@ describe('SSR: renderToString', () => {
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('invalid style value', done => {
|
||||
renderVmWithOptions({
|
||||
template: '<div :style="style"><p :style="style2"/></div>',
|
||||
data: {
|
||||
// all invalid, should not even have "style" attribute
|
||||
style: {
|
||||
opacity: {},
|
||||
color: null
|
||||
},
|
||||
// mix of valid and invalid
|
||||
style2: {
|
||||
opacity: 0,
|
||||
color: null
|
||||
}
|
||||
}
|
||||
}, result => {
|
||||
expect(result).toContain(
|
||||
'<div data-server-rendered="true"><p style="opacity:0;"></p></div>'
|
||||
)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('numeric style value', done => {
|
||||
renderVmWithOptions({
|
||||
template: '<div :style="style"></div>',
|
||||
data: {
|
||||
style: {
|
||||
opacity: 0, // opacity should display as-is
|
||||
top: 0, // top and margin should be converted to '0px'
|
||||
marginTop: 10
|
||||
}
|
||||
}
|
||||
}, result => {
|
||||
expect(result).toContain(
|
||||
'<div data-server-rendered="true" style="opacity:0;top:0px;margin-top:10px;"></div>'
|
||||
)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function renderVmWithOptions (options, cb) {
|
||||
|
Loading…
Reference in New Issue
Block a user