fix(runtime-vapor): patch prop
This commit is contained in:
parent
775491e46d
commit
35334fd33e
|
@ -1,11 +1,8 @@
|
||||||
import {
|
import { isArray, toDisplayString } from '@vue/shared'
|
||||||
isArray,
|
|
||||||
normalizeClass,
|
|
||||||
normalizeStyle,
|
|
||||||
toDisplayString,
|
|
||||||
} from '@vue/shared'
|
|
||||||
import type { Block, ParentBlock } from './render'
|
import type { Block, ParentBlock } from './render'
|
||||||
|
|
||||||
|
export * from './dom/patchProp'
|
||||||
|
|
||||||
export function insert(block: Block, parent: Node, anchor: Node | null = null) {
|
export function insert(block: Block, parent: Node, anchor: Node | null = null) {
|
||||||
// if (!isHydrating) {
|
// if (!isHydrating) {
|
||||||
if (block instanceof Node) {
|
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]>
|
type Children = Record<number, [ChildNode, Children]>
|
||||||
export function children(n: Node): Children {
|
export function children(n: Node): Children {
|
||||||
const result: 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
|
completed: boolean
|
||||||
}
|
}
|
||||||
const tasks = ref<Task[]>([])
|
const tasks = ref<Task[]>([])
|
||||||
|
const value = ref('hello')
|
||||||
|
|
||||||
|
function handleAdd() {
|
||||||
|
tasks.value.push({
|
||||||
|
title: value.value,
|
||||||
|
completed: false,
|
||||||
|
})
|
||||||
|
// TODO: clear input
|
||||||
|
value.value = ''
|
||||||
|
}
|
||||||
</script>
|
</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