refactor: new attrs merge strategy
This commit is contained in:
parent
85cd69a988
commit
a1b9144009
@ -37,7 +37,7 @@ export interface MountedComponent<D = Data, P = Data> extends Component {
|
||||
$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
|
||||
|
@ -59,7 +59,8 @@ export function renderInstanceRoot(instance: MountedComponent) {
|
||||
vnode = instance.render.call(
|
||||
instance.$proxy,
|
||||
instance.$props,
|
||||
instance.$slots
|
||||
instance.$slots,
|
||||
instance.$attrs
|
||||
)
|
||||
} catch (e1) {
|
||||
handleError(e1, instance, ErrorTypes.RENDER)
|
||||
@ -105,17 +106,13 @@ export function normalizeComponentRoot(
|
||||
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) {
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
createFragment,
|
||||
createPortal
|
||||
} from './vdom'
|
||||
import { isObservable } from '@vue/observer'
|
||||
|
||||
export const Fragment = Symbol()
|
||||
export const Portal = Symbol()
|
||||
@ -36,7 +37,15 @@ export const h = ((tag: ElementType, data?: any, children?: any): VNode => {
|
||||
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
|
||||
|
@ -11,6 +11,43 @@ export const isReservedProp = (key: string): boolean => {
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
|
@ -5,6 +5,7 @@ import {
|
||||
} 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 {
|
||||
@ -95,6 +96,15 @@ export function createVNode(
|
||||
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,
|
||||
@ -104,6 +114,9 @@ export function createElementVNode(
|
||||
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)
|
||||
}
|
||||
|
||||
@ -175,6 +188,11 @@ export function createComponentVNode(
|
||||
}
|
||||
}
|
||||
|
||||
// class & style
|
||||
if (data !== null) {
|
||||
normalizeClassAndStyle(data)
|
||||
}
|
||||
|
||||
return createVNode(
|
||||
flags,
|
||||
comp,
|
||||
@ -248,9 +266,20 @@ export function cloneVNode(vnode: VNode, extraData?: VNodeData): VNode {
|
||||
}
|
||||
}
|
||||
for (const key in extraData) {
|
||||
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(
|
||||
flags,
|
||||
vnode.tag,
|
||||
|
@ -1,29 +1,11 @@
|
||||
// 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()
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
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
|
||||
|
||||
@ -10,44 +8,19 @@ export function patchStyle(el: any, prev: any, next: any, data: any) {
|
||||
} 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
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user