$children: MountedComponent[]
$options: ComponentOptions<D, P>
- render(props: P, slots: Slots): any
+ render(props: P, slots: Slots, attrs: Data): any
renderError?(e: Error): any
renderTracked?(e: DebuggerEvent): void
renderTriggered?(e: DebuggerEvent): void
vnode = instance.render.call(
instance.$proxy,
instance.$props,
- instance.$slots
+ instance.$slots,
+ instance.$attrs
)
} catch (e1) {
handleError(e1, instance, ErrorTypes.RENDER)
vnode = createFragment(vnode)
} else {
const { flags } = vnode
- // parentVNode data merge down
if (
componentVNode &&
(flags & VNodeFlags.COMPONENT || flags & VNodeFlags.ELEMENT)
) {
if (inheritAttrs !== false && attrs !== void 0) {
- // TODO should merge
- console.log(attrs)
vnode = cloneVNode(vnode, attrs)
- }
- if (vnode.el) {
+ } else if (vnode.el) {
vnode = cloneVNode(vnode)
}
if (flags & VNodeFlags.COMPONENT) {
createFragment,
createPortal
} from './vdom'
+import { isObservable } from '@vue/observer'
export const Fragment = Symbol()
export const Portal = Symbol()
data = null
}
- // TODO clone data if it is observed
+ if (data === void 0) data = null
+ if (children === void 0) children = null
+
+ if (__DEV__ && isObservable(data)) {
+ console.warn(
+ `Do not used observed state as VNode data - always create fresh objects.`,
+ data
+ )
+ }
let key = null
let ref = null
}
}
+export function normalizeStyle(
+ value: any
+): Record<string, string | number> | void {
+ if (Array.isArray(value)) {
+ const res: Record<string, string | number> = {}
+ for (let i = 0; i < value.length; i++) {
+ const normalized = normalizeStyle(value[i])
+ if (normalized) {
+ for (const key in normalized) {
+ res[key] = normalized[key]
+ }
+ }
+ }
+ return res
+ } else if (value && typeof value === 'object') {
+ return value
+ }
+}
+
+export function normalizeClass(value: any): string {
+ let res = ''
+ if (typeof value === 'string') {
+ res = value
+ } else if (Array.isArray(value)) {
+ for (let i = 0; i < value.length; i++) {
+ res += normalizeClass(value[i]) + ' '
+ }
+ } else if (typeof value === 'object') {
+ for (const name in value) {
+ if (value[name]) {
+ res += name + ' '
+ }
+ }
+ }
+ return res.trim()
+}
+
// https://en.wikipedia.org/wiki/Longest_increasing_subsequence
export function lis(arr: number[]): number[] {
const p = arr.slice()
} from './component'
import { VNodeFlags, ChildrenFlags } from './flags'
import { createComponentClassFromOptions } from './componentUtils'
+import { normalizeClass, normalizeStyle } from './utils'
// Vue core is platform agnostic, so we are not using Element for "DOM" nodes.
export interface RenderNode {
return vnode
}
+function normalizeClassAndStyle(data: VNodeData) {
+ if (data.class != null) {
+ data.class = normalizeClass(data.class)
+ }
+ if (data.style != null) {
+ data.style = normalizeStyle(data.style)
+ }
+}
+
export function createElementVNode(
tag: string,
data: VNodeData | null,
ref?: Ref | null
) {
const flags = tag === 'svg' ? VNodeFlags.ELEMENT_SVG : VNodeFlags.ELEMENT_HTML
+ if (data !== null) {
+ normalizeClassAndStyle(data)
+ }
return createVNode(flags, tag, data, children, childFlags, key, ref, null)
}
}
}
+ // class & style
+ if (data !== null) {
+ normalizeClassAndStyle(data)
+ }
+
return createVNode(
flags,
comp,
}
}
for (const key in extraData) {
- clonedData[key] = extraData[key]
+ if (key === 'class') {
+ clonedData.class = normalizeClass([clonedData.class, extraData.class])
+ } else if (key === 'style') {
+ clonedData.style = normalizeStyle([clonedData.style, extraData.style])
+ } else if (key.startsWith('on')) {
+ const existing = clonedData[key]
+ clonedData[key] = existing
+ ? [].concat(existing, extraData[key])
+ : extraData[key]
+ } else {
+ clonedData[key] = extraData[key]
+ }
}
}
return createVNode(
// compiler should normlaize class + :class bindings on the same element
// into a single binding ['staticClass', dynamic]
-export function patchClass(el: Element, value: any, isSVG: boolean) {
+export function patchClass(el: Element, value: string, isSVG: boolean) {
// directly setting className should be faster than setAttribute in theory
if (isSVG) {
- el.setAttribute('class', normalizeClass(value))
+ el.setAttribute('class', value)
} else {
- el.className = normalizeClass(value)
+ el.className = value
}
}
-
-function normalizeClass(value: any): string {
- let res = ''
- if (typeof value === 'string') {
- res = value
- } else if (Array.isArray(value)) {
- for (let i = 0; i < value.length; i++) {
- res += normalizeClass(value[i]) + ' '
- }
- } else if (typeof value === 'object') {
- for (const name in value) {
- if (value[name]) {
- res += name + ' '
- }
- }
- }
- return res.trim()
-}
-import { isObservable } from '@vue/core'
-
// style properties that should NOT have "px" added when numeric
const nonNumericRE = /acit|ex(?:s|g|n|p|$)|rph|ows|mnc|ntw|ine[ch]|zoo|^ord/i
} else if (typeof next === 'string') {
style.cssText = next
} else {
- // TODO: warn invalid value in dev
- const normalizedNext: any = normalizeStyle(next)
- // If next is observed, the user is likely to mutate the style object.
- // We need to replace data.style with the normalized clone.
- if (isObservable(next)) {
- data.style = normalizedNext
- }
- for (const key in normalizedNext) {
- let value = normalizedNext[key]
+ for (const key in next) {
+ let value = next[key]
if (typeof value === 'number' && !nonNumericRE.test(key)) {
value = value + 'px'
}
style[key] = value
}
if (prev && typeof prev !== 'string') {
- prev = normalizeStyle(prev)
for (const key in prev) {
- if (!normalizedNext[key]) {
+ if (!next[key]) {
style[key] = ''
}
}
}
}
}
-
-function normalizeStyle(value: any): Record<string, string | number> | void {
- if (value && typeof value === 'object') {
- return value
- } else if (Array.isArray(value)) {
- const res: Record<string, string | number> = {}
- for (let i = 0; i < value.length; i++) {
- const normalized = normalizeStyle(value[i])
- if (normalized) {
- for (const key in normalized) {
- res[key] = normalized[key]
- }
- }
- }
- return res
- }
-}