Skip to content

Commit e6e2c58

Browse files
committed
fix(runtime-dom/ssr): properly handle xlink and boolean attributes
1 parent 6f43c4b commit e6e2c58

File tree

3 files changed

+38
-22
lines changed

3 files changed

+38
-22
lines changed
+16-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
// TODO explain why we are no longer checking boolean/enumerated here
1+
import { isSpecialBooleanAttr } from '@vue/shared'
2+
3+
const xlinkNS = 'http://www.w3.org/1999/xlink'
24

35
export function patchAttr(
46
el: Element,
@@ -7,12 +9,19 @@ export function patchAttr(
79
isSVG: boolean
810
) {
911
if (isSVG && key.indexOf('xlink:') === 0) {
10-
// TODO handle xlink
11-
} else if (value == null) {
12-
el.removeAttribute(key)
12+
if (value == null) {
13+
el.removeAttributeNS(xlinkNS, key)
14+
} else {
15+
el.setAttributeNS(xlinkNS, key, value)
16+
}
1317
} else {
14-
// TODO in dev mode, warn against incorrect values for boolean or
15-
// enumerated attributes
16-
el.setAttribute(key, value)
18+
// note we are only checking boolean attributes that don't have a
19+
// correspoding dom prop of the same name here.
20+
const isBoolean = isSpecialBooleanAttr(key)
21+
if (value == null || (isBoolean && value === false)) {
22+
el.removeAttribute(key)
23+
} else {
24+
el.setAttribute(key, isBoolean ? '' : value)
25+
}
1726
}
1827
}

packages/server-renderer/src/renderProps.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ export function renderProps(
3030
? key
3131
: propsToAttrMap[key] || key.toLowerCase()
3232
if (isBooleanAttr(attrKey)) {
33-
ret += ` ${attrKey}=""`
33+
if (value !== false) {
34+
ret += ` ${attrKey}`
35+
}
3436
} else if (isSSRSafeAttrName(attrKey)) {
3537
ret += ` ${attrKey}="${escape(value)}"`
3638
}

packages/shared/src/domAttrConfig.ts

+19-14
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
11
import { makeMap } from './makeMap'
22

3-
// TODO validate this list!
4-
// on the client, most of these probably has corresponding prop
5-
// or, like allowFullscreen on iframe, although case is different, the attr
6-
// affects the property properly...
7-
// Basically, we can skip this check on the client
8-
// but they are still needed during SSR to produce correct initial markup
9-
export const isBooleanAttr = makeMap(
10-
'allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,' +
11-
'default,defaultchecked,defaultmuted,defaultselected,defer,disabled,' +
12-
'enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,' +
13-
'muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,' +
14-
'required,reversed,scoped,seamless,selected,sortable,translate,' +
15-
'truespeed,typemustmatch,visible'
3+
// On the client we only need to offer special cases for boolean attributes that
4+
// have different names from their corresponding dom properties:
5+
// - itemscope -> N/A
6+
// - allowfullscreen -> allowFullscreen
7+
// - formnovalidate -> formNoValidate
8+
// - ismap -> isMap
9+
// - nomodule -> noModule
10+
// - novalidate -> noValidate
11+
// - readonly -> readOnly
12+
const specialBooleanAttrs = `itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly`
13+
export const isSpecialBooleanAttr = /*#__PURE__*/ makeMap(specialBooleanAttrs)
14+
15+
// The full list is needed during SSR to produce the correct initial markup.
16+
export const isBooleanAttr = /*#__PURE__*/ makeMap(
17+
specialBooleanAttrs +
18+
`,async,autofocus,autoplay,controls,default,defer,disabled,hidden,ismap,` +
19+
`loop,nomodule,open,required,reversed,scoped,seamless,` +
20+
`checked,muted,multiple,selected`
1621
)
1722

1823
const unsafeAttrCharRE = /[>/="'\u0009\u000a\u000c\u0020]/
@@ -37,7 +42,7 @@ export const propsToAttrMap: Record<string, string | undefined> = {
3742
}
3843

3944
// CSS properties that accept plain numbers
40-
export const isNoUnitNumericStyleProp = makeMap(
45+
export const isNoUnitNumericStyleProp = /*#__PURE__*/ makeMap(
4146
`animation-iteration-count,border-image-outset,border-image-slice,` +
4247
`border-image-width,box-flex,box-flex-group,box-ordinal-group,column-count,` +
4348
`columns,flex,flex-grow,flex-positive,flex-shrink,flex-negative,flex-order,` +

0 commit comments

Comments
 (0)