fix(runtime-vapor): patch prop
This commit is contained in:
parent
775491e46d
commit
35334fd33e
|
@ -1,11 +1,8 @@
|
|||
import {
|
||||
isArray,
|
||||
normalizeClass,
|
||||
normalizeStyle,
|
||||
toDisplayString,
|
||||
} from '@vue/shared'
|
||||
import { isArray, toDisplayString } from '@vue/shared'
|
||||
import type { Block, ParentBlock } from './render'
|
||||
|
||||
export * from './dom/patchProp'
|
||||
|
||||
export function insert(block: Block, parent: Node, anchor: Node | null = null) {
|
||||
// if (!isHydrating) {
|
||||
if (block instanceof Node) {
|
||||
|
@ -62,63 +59,6 @@ export function setHtml(el: Element, oldVal: any, newVal: any) {
|
|||
}
|
||||
}
|
||||
|
||||
export function setClass(el: Element, oldVal: any, newVal: any) {
|
||||
if ((newVal = normalizeClass(newVal)) !== oldVal && (newVal || oldVal)) {
|
||||
el.className = newVal
|
||||
}
|
||||
}
|
||||
|
||||
export function setStyle(el: HTMLElement, oldVal: any, newVal: any) {
|
||||
if ((newVal = normalizeStyle(newVal)) !== oldVal && (newVal || oldVal)) {
|
||||
if (typeof newVal === 'string') {
|
||||
el.style.cssText = newVal
|
||||
} else {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function setAttr(el: Element, key: string, oldVal: any, newVal: any) {
|
||||
if (newVal !== oldVal) {
|
||||
if (newVal != null) {
|
||||
el.setAttribute(key, newVal)
|
||||
} else {
|
||||
el.removeAttribute(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function setDOMProp(el: any, key: string, oldVal: any, newVal: any) {
|
||||
// TODO special checks
|
||||
if (newVal !== oldVal) {
|
||||
el[key] = newVal
|
||||
}
|
||||
}
|
||||
|
||||
export function setDynamicProp(
|
||||
el: Element,
|
||||
key: string,
|
||||
oldVal: any,
|
||||
newVal: any,
|
||||
) {
|
||||
if (key === 'class') {
|
||||
setClass(el, oldVal, newVal)
|
||||
} else if (key === 'style') {
|
||||
setStyle(el as HTMLElement, oldVal, newVal)
|
||||
} else if (
|
||||
key[0] === '.'
|
||||
? ((key = key.slice(1)), true)
|
||||
: key[0] === '^'
|
||||
? ((key = key.slice(1)), false)
|
||||
: key in el
|
||||
) {
|
||||
setDOMProp(el, key, oldVal, newVal)
|
||||
} else {
|
||||
// TODO special checks
|
||||
setAttr(el, key, oldVal, newVal)
|
||||
}
|
||||
}
|
||||
|
||||
type Children = Record<number, [ChildNode, Children]>
|
||||
export function children(n: Node): Children {
|
||||
const result: Children = {}
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
import {
|
||||
isFunction,
|
||||
isString,
|
||||
normalizeClass,
|
||||
normalizeStyle,
|
||||
} from '@vue/shared'
|
||||
|
||||
export function setClass(el: Element, oldVal: any, newVal: any) {
|
||||
if ((newVal = normalizeClass(newVal)) !== oldVal && (newVal || oldVal)) {
|
||||
el.className = newVal
|
||||
}
|
||||
}
|
||||
|
||||
export function setStyle(el: HTMLElement, oldVal: any, newVal: any) {
|
||||
if ((newVal = normalizeStyle(newVal)) !== oldVal && (newVal || oldVal)) {
|
||||
if (typeof newVal === 'string') {
|
||||
el.style.cssText = newVal
|
||||
} else {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function setAttr(el: Element, key: string, oldVal: any, newVal: any) {
|
||||
if (newVal !== oldVal) {
|
||||
if (newVal != null) {
|
||||
el.setAttribute(key, newVal)
|
||||
} else {
|
||||
el.removeAttribute(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function setDOMProp(el: any, key: string, oldVal: any, newVal: any) {
|
||||
// TODO special checks
|
||||
if (newVal !== oldVal) {
|
||||
el[key] = newVal
|
||||
}
|
||||
}
|
||||
|
||||
export function setDynamicProp(
|
||||
el: Element,
|
||||
key: string,
|
||||
oldVal: any,
|
||||
newVal: any,
|
||||
) {
|
||||
// TODO
|
||||
const isSVG = false
|
||||
if (key === 'class') {
|
||||
setClass(el, oldVal, newVal)
|
||||
} else if (key === 'style') {
|
||||
setStyle(el as HTMLElement, oldVal, newVal)
|
||||
} else if (
|
||||
key[0] === '.'
|
||||
? ((key = key.slice(1)), true)
|
||||
: key[0] === '^'
|
||||
? ((key = key.slice(1)), false)
|
||||
: shouldSetAsProp(el, key, newVal, isSVG)
|
||||
) {
|
||||
setDOMProp(el, key, oldVal, newVal)
|
||||
} else {
|
||||
// TODO special case for <input v-model type="checkbox">
|
||||
setAttr(el, key, oldVal, newVal)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO copied from runtime-dom
|
||||
const isNativeOn = (key: string) =>
|
||||
key.charCodeAt(0) === 111 /* o */ &&
|
||||
key.charCodeAt(1) === 110 /* n */ &&
|
||||
// lowercase letter
|
||||
key.charCodeAt(2) > 96 &&
|
||||
key.charCodeAt(2) < 123
|
||||
|
||||
function shouldSetAsProp(
|
||||
el: Element,
|
||||
key: string,
|
||||
value: unknown,
|
||||
isSVG: boolean,
|
||||
) {
|
||||
if (isSVG) {
|
||||
// most keys must be set as attribute on svg elements to work
|
||||
// ...except innerHTML & textContent
|
||||
if (key === 'innerHTML' || key === 'textContent') {
|
||||
return true
|
||||
}
|
||||
// or native onclick with function values
|
||||
if (key in el && isNativeOn(key) && isFunction(value)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// these are enumerated attrs, however their corresponding DOM properties
|
||||
// are actually booleans - this leads to setting it with a string "false"
|
||||
// value leading it to be coerced to `true`, so we need to always treat
|
||||
// them as attributes.
|
||||
// Note that `contentEditable` doesn't have this problem: its DOM
|
||||
// property is also enumerated string values.
|
||||
if (key === 'spellcheck' || key === 'draggable' || key === 'translate') {
|
||||
return false
|
||||
}
|
||||
|
||||
// #1787, #2840 form property on form elements is readonly and must be set as
|
||||
// attribute.
|
||||
if (key === 'form') {
|
||||
return false
|
||||
}
|
||||
|
||||
// #1526 <input list> must be set as attribute
|
||||
if (key === 'list' && el.tagName === 'INPUT') {
|
||||
return false
|
||||
}
|
||||
|
||||
// #2766 <textarea type> must be set as attribute
|
||||
if (key === 'type' && el.tagName === 'TEXTAREA') {
|
||||
return false
|
||||
}
|
||||
|
||||
// #8780 the width or height of embedded tags must be set as attribute
|
||||
if (key === 'width' || key === 'height') {
|
||||
const tag = el.tagName
|
||||
if (
|
||||
tag === 'IMG' ||
|
||||
tag === 'VIDEO' ||
|
||||
tag === 'CANVAS' ||
|
||||
tag === 'SOURCE'
|
||||
) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// native onclick with string value, must be set as attribute
|
||||
if (isNativeOn(key) && isString(value)) {
|
||||
return false
|
||||
}
|
||||
|
||||
return key in el
|
||||
}
|
|
@ -6,6 +6,45 @@ interface Task {
|
|||
completed: boolean
|
||||
}
|
||||
const tasks = ref<Task[]>([])
|
||||
const value = ref('hello')
|
||||
|
||||
function handleAdd() {
|
||||
tasks.value.push({
|
||||
title: value.value,
|
||||
completed: false,
|
||||
})
|
||||
// TODO: clear input
|
||||
value.value = ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>{{ tasks }}</template>
|
||||
<template>
|
||||
<ul>
|
||||
<!-- TODO: v-for -->
|
||||
<li>
|
||||
<!-- TODO checked=false -->
|
||||
<input type="checkbox" :checked="tasks[0]?.completed" />
|
||||
{{ tasks[0]?.title }}
|
||||
</li>
|
||||
<li>
|
||||
<input type="checkbox" :checked="tasks[1]?.completed" />
|
||||
{{ tasks[1]?.title }}
|
||||
</li>
|
||||
<li>
|
||||
<input type="checkbox" :checked="tasks[2]?.completed" />
|
||||
{{ tasks[2]?.title }}
|
||||
</li>
|
||||
<li>
|
||||
<input type="checkbox" :checked="tasks[3]?.completed" />
|
||||
{{ tasks[3]?.title }}
|
||||
</li>
|
||||
<li>
|
||||
<input
|
||||
type="text"
|
||||
:value="value"
|
||||
@input="evt => (value = evt.target.value)"
|
||||
/>
|
||||
<button @click="handleAdd">Add</button>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
|
Loading…
Reference in New Issue