优化模板解析;修复事件绑定
parent
bbe04eae83
commit
b4e7f5dfae
|
@ -44,7 +44,6 @@ export const __props__ = Symbol('props')
|
||||||
export const __changed_props__ = Symbol('changed_props')
|
export const __changed_props__ = Symbol('changed_props')
|
||||||
export const __mounted__ = Symbol('mounted')
|
export const __mounted__ = Symbol('mounted')
|
||||||
export const __pending__ = Symbol('pending')
|
export const __pending__ = Symbol('pending')
|
||||||
export const __children__ = Symbol('children')
|
|
||||||
|
|
||||||
export const RESET_CSS_STYLE = `* {box-sizing: border-box;margin: 0;padding: 0;}::before,::after {box-sizing: border-box;}::selection {background: var(--selection-background, var(--color-plain-a));color: var(--selection-color, inherit);}`
|
export const RESET_CSS_STYLE = `* {box-sizing: border-box;margin: 0;padding: 0;}::before,::after {box-sizing: border-box;}::selection {background: var(--selection-background, var(--color-plain-a));color: var(--selection-color, inherit);}`
|
||||||
|
|
||||||
|
|
128
src/html.js
128
src/html.js
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
import { boolMap, WC_PART, NOTHING } from './constants.js'
|
import { boolMap, WC_PART, NOTHING } from './constants.js'
|
||||||
import { animate, MODES } from './anim.js'
|
import { animate, MODES } from './anim.js'
|
||||||
|
import { nextTick } from './utils.js'
|
||||||
|
|
||||||
const boundAttributeSuffix = '$wc$'
|
const boundAttributeSuffix = '$wc$'
|
||||||
const marker = `wc$${String(Math.random()).slice(9)}$`
|
const marker = `wc$${String(Math.random()).slice(9)}$`
|
||||||
|
@ -265,7 +266,7 @@ class TemplateInstance {
|
||||||
} while ((node = walker.nextNode()) !== null)
|
} while ((node = walker.nextNode()) !== null)
|
||||||
}
|
}
|
||||||
|
|
||||||
_clone(options) {
|
clone(options) {
|
||||||
let {
|
let {
|
||||||
el: { content },
|
el: { content },
|
||||||
parts
|
parts
|
||||||
|
@ -326,7 +327,7 @@ class TemplateInstance {
|
||||||
|
|
||||||
return fragment
|
return fragment
|
||||||
}
|
}
|
||||||
_update(values) {
|
update(values) {
|
||||||
let i = 0
|
let i = 0
|
||||||
for (let part of this.$parts) {
|
for (let part of this.$parts) {
|
||||||
if (part !== void 0) {
|
if (part !== void 0) {
|
||||||
|
@ -343,9 +344,10 @@ class TemplateInstance {
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChildPart {
|
class ChildPart {
|
||||||
|
#value = NOTHING
|
||||||
|
|
||||||
constructor(startNode, endNode, parent, options) {
|
constructor(startNode, endNode, parent, options) {
|
||||||
this.type = CHILD_PART
|
this.type = CHILD_PART
|
||||||
this._$committedValue = NOTHING
|
|
||||||
this.startNode = startNode
|
this.startNode = startNode
|
||||||
this.endNode = endNode
|
this.endNode = endNode
|
||||||
this.$parent = parent
|
this.$parent = parent
|
||||||
|
@ -364,46 +366,43 @@ class ChildPart {
|
||||||
$setValue(value) {
|
$setValue(value) {
|
||||||
if (isPrimitive(value)) {
|
if (isPrimitive(value)) {
|
||||||
if (value === NOTHING || value == null || value === '') {
|
if (value === NOTHING || value == null || value === '') {
|
||||||
if (this._$committedValue !== NOTHING) {
|
if (this.#value !== NOTHING) {
|
||||||
this.#clear()
|
this.#clear()
|
||||||
}
|
}
|
||||||
this._$committedValue = NOTHING
|
this.#value = NOTHING
|
||||||
} else if (value !== this._$committedValue) {
|
} else if (value !== this.#value) {
|
||||||
this._commitText(value)
|
this.#commitText(value)
|
||||||
}
|
}
|
||||||
} else if (value['__dom_type__'] !== void 0) {
|
} else if (value['__dom_type__'] !== void 0) {
|
||||||
this._commitTemplateResult(value)
|
this.#commitTemplateResult(value)
|
||||||
} else if (value.nodeType !== void 0) {
|
} else if (value.nodeType !== void 0) {
|
||||||
this._commitNode(value)
|
this.#commitNode(value)
|
||||||
} else if (isIterable(value)) {
|
} else if (isIterable(value)) {
|
||||||
this._commitIterable(value)
|
this.#commitIterable(value)
|
||||||
} else {
|
} else {
|
||||||
this._commitText(value)
|
this.#commitText(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_insert(node, target = this.endNode) {
|
#insert(node, target = this.endNode) {
|
||||||
return this.startNode.parentNode.insertBefore(node, target)
|
return this.startNode.parentNode.insertBefore(node, target)
|
||||||
}
|
}
|
||||||
_commitNode(value) {
|
#commitNode(value) {
|
||||||
if (this._$committedValue !== value) {
|
if (this.#value !== value) {
|
||||||
this.#clear()
|
this.#clear()
|
||||||
this._$committedValue = this._insert(value)
|
this.#value = this.#insert(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_commitText(value) {
|
#commitText(value) {
|
||||||
if (
|
if (this.#value !== NOTHING && isPrimitive(this.#value)) {
|
||||||
this._$committedValue !== NOTHING &&
|
|
||||||
isPrimitive(this._$committedValue)
|
|
||||||
) {
|
|
||||||
let node = this.startNode.nextSibling
|
let node = this.startNode.nextSibling
|
||||||
|
|
||||||
node.data = value
|
node.data = value
|
||||||
} else {
|
} else {
|
||||||
this._commitNode(document.createTextNode(value))
|
this.#commitNode(document.createTextNode(value))
|
||||||
}
|
}
|
||||||
this._$committedValue = value
|
this.#value = value
|
||||||
}
|
}
|
||||||
_commitTemplateResult(result) {
|
#commitTemplateResult(result) {
|
||||||
let { values, ['__dom_type__']: type } = result
|
let { values, ['__dom_type__']: type } = result
|
||||||
let template =
|
let template =
|
||||||
typeof type === 'number'
|
typeof type === 'number'
|
||||||
|
@ -411,16 +410,16 @@ class ChildPart {
|
||||||
: (type.el === void 0 && (type.el = Template.createElement(type.h)),
|
: (type.el === void 0 && (type.el = Template.createElement(type.h)),
|
||||||
type)
|
type)
|
||||||
|
|
||||||
if (this._$committedValue?.$template === template) {
|
if (this.#value?.$template === template) {
|
||||||
this._$committedValue._update(values)
|
this.#value.update(values)
|
||||||
} else {
|
} else {
|
||||||
let instance = new TemplateInstance(template, this)
|
let instance = new TemplateInstance(template, this)
|
||||||
let fragment = instance._clone(this.options)
|
let fragment = instance.clone(this.options)
|
||||||
|
|
||||||
instance._update(values)
|
instance.update(values)
|
||||||
|
|
||||||
this._commitNode(fragment)
|
this.#commitNode(fragment)
|
||||||
this._$committedValue = instance
|
this.#value = instance
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#getTemplate(result) {
|
#getTemplate(result) {
|
||||||
|
@ -432,20 +431,21 @@ class ChildPart {
|
||||||
}
|
}
|
||||||
return template
|
return template
|
||||||
}
|
}
|
||||||
_commitIterable(value) {
|
|
||||||
if (!isArray(this._$committedValue)) {
|
#commitIterable(value) {
|
||||||
this._$committedValue = []
|
if (!isArray(this.#value)) {
|
||||||
|
this.#value = []
|
||||||
this.#clear()
|
this.#clear()
|
||||||
}
|
}
|
||||||
let itemParts = this._$committedValue
|
let itemParts = this.#value
|
||||||
let partIndex = 0
|
let partIndex = 0
|
||||||
let itemPart
|
let itemPart
|
||||||
for (let item of value) {
|
for (let item of value) {
|
||||||
if (partIndex === itemParts.length) {
|
if (partIndex === itemParts.length) {
|
||||||
itemParts.push(
|
itemParts.push(
|
||||||
(itemPart = new ChildPart(
|
(itemPart = new ChildPart(
|
||||||
this._insert(createMarker()),
|
this.#insert(createMarker()),
|
||||||
this._insert(createMarker()),
|
this.#insert(createMarker()),
|
||||||
this,
|
this,
|
||||||
this.options
|
this.options
|
||||||
))
|
))
|
||||||
|
@ -472,19 +472,19 @@ class ChildPart {
|
||||||
}
|
}
|
||||||
// 常规属性
|
// 常规属性
|
||||||
class AttributePart {
|
class AttributePart {
|
||||||
|
#value = NOTHING
|
||||||
|
|
||||||
constructor(element, name, strings, decorates, parent, options = {}) {
|
constructor(element, name, strings, decorates, parent, options = {}) {
|
||||||
this.type = ATTRIBUTE_PART
|
this.type = ATTRIBUTE_PART
|
||||||
this._$committedValue = NOTHING
|
|
||||||
this.element = element
|
this.element = element
|
||||||
this.name = name
|
this.name = name
|
||||||
this.decorates = decorates
|
this.decorates = decorates
|
||||||
this.$parent = parent
|
this.$parent = parent
|
||||||
this.options = options
|
this.options = options
|
||||||
if (strings.length > 2 || strings[0] !== '' || strings[1] !== '') {
|
if (strings.length > 2 || strings[0] !== '' || strings[1] !== '') {
|
||||||
this._$committedValue = Array(strings.length - 1).fill(null)
|
this.#value = Array(strings.length - 1).fill(null)
|
||||||
this.strings = strings
|
this.strings = strings
|
||||||
} else {
|
|
||||||
this._$committedValue = NOTHING
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
get tagName() {
|
get tagName() {
|
||||||
|
@ -495,9 +495,9 @@ class AttributePart {
|
||||||
let strings = this.strings
|
let strings = this.strings
|
||||||
let changed = false
|
let changed = false
|
||||||
if (strings === void 0) {
|
if (strings === void 0) {
|
||||||
changed = !isPrimitive(value) || value !== this._$committedValue
|
changed = !isPrimitive(value) || value !== this.#value
|
||||||
if (changed) {
|
if (changed) {
|
||||||
this._$committedValue = value
|
this.#value = value
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let values = value
|
let values = value
|
||||||
|
@ -506,13 +506,13 @@ class AttributePart {
|
||||||
for (let i = 0; i < strings.length - 1; i++) {
|
for (let i = 0; i < strings.length - 1; i++) {
|
||||||
let v = values[valueIndex + i]
|
let v = values[valueIndex + i]
|
||||||
|
|
||||||
changed || (changed = !isPrimitive(v) || v !== this._$committedValue[i])
|
changed || (changed = !isPrimitive(v) || v !== this.#value[i])
|
||||||
if (v === NOTHING) {
|
if (v === NOTHING) {
|
||||||
value = NOTHING
|
value = NOTHING
|
||||||
} else if (value !== NOTHING) {
|
} else if (value !== NOTHING) {
|
||||||
value += (v !== null && v !== void 0 ? v : '') + strings[i + 1]
|
value += (v !== null && v !== void 0 ? v : '') + strings[i + 1]
|
||||||
}
|
}
|
||||||
this._$committedValue[i] = v
|
this.#value[i] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (changed) {
|
if (changed) {
|
||||||
|
@ -587,9 +587,27 @@ class EventPart extends AttributePart {
|
||||||
this.type = EVENT_PART
|
this.type = EVENT_PART
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#clearBindings() {
|
||||||
|
let host = this.options.host
|
||||||
|
let events = host.$events[this.name]
|
||||||
|
|
||||||
|
for (let i = -1, it; (it = events[++i]); ) {
|
||||||
|
if (!host.root.contains(it.el)) {
|
||||||
|
this.element.removeEventListener(this.name, it.listener, it.options)
|
||||||
|
events[i] = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
host.$events[this.name] = events.filter(it => it !== null)
|
||||||
|
}
|
||||||
|
|
||||||
$setValue(listener) {
|
$setValue(listener) {
|
||||||
let host = this.options.host
|
let host = this.options.host
|
||||||
let options = {}
|
let options = {}
|
||||||
|
let events = host.$events[this.name]
|
||||||
|
|
||||||
|
if (!events) {
|
||||||
|
events = host.$events[this.name] = []
|
||||||
|
}
|
||||||
|
|
||||||
if (this.decorates.length) {
|
if (this.decorates.length) {
|
||||||
for (let it of this.decorates) {
|
for (let it of this.decorates) {
|
||||||
|
@ -614,42 +632,40 @@ class EventPart extends AttributePart {
|
||||||
|
|
||||||
let shouldRemove = listener !== this.#listener
|
let shouldRemove = listener !== this.#listener
|
||||||
|
|
||||||
if (this.#listener && host.$events[this.name]) {
|
if (this.#listener) {
|
||||||
for (let it of host.$events[this.name]) {
|
for (let i = -1, it; (it = events[++i]); ) {
|
||||||
if (it.el === this.element) {
|
if (it.el === this.element) {
|
||||||
shouldRemove =
|
shouldRemove =
|
||||||
|
shouldRemove ||
|
||||||
options.capture !== it.capture ||
|
options.capture !== it.capture ||
|
||||||
options.once !== it.once ||
|
options.once !== it.once ||
|
||||||
options.passive !== it.passive
|
options.passive !== it.passive
|
||||||
if (shouldRemove) {
|
if (shouldRemove) {
|
||||||
this.element.removeEventListener(this.name, it.listener, it.options)
|
this.element.removeEventListener(this.name, it.listener, it.options)
|
||||||
|
events[i] = null
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
events = events.filter(it => it !== null)
|
||||||
|
host.$events[this.name] = events
|
||||||
|
|
||||||
if (listener && shouldRemove) {
|
if (listener && shouldRemove) {
|
||||||
this.element.addEventListener(this.name, this, options)
|
this.element.addEventListener(this.name, this, options)
|
||||||
this.#listener = listener
|
this.#listener = listener
|
||||||
|
|
||||||
if (host.$events[this.name]) {
|
events.push({
|
||||||
host.$events[this.name].push({
|
|
||||||
el: this.element,
|
el: this.element,
|
||||||
listener: this,
|
listener: this,
|
||||||
options
|
options
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
host.$events[this.name] = [
|
|
||||||
{
|
|
||||||
el: this.element,
|
|
||||||
listener: this,
|
|
||||||
options
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nextTick(_ => this.#clearBindings())
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEvent(ev) {
|
handleEvent(ev) {
|
||||||
this.#stop(ev)
|
this.#stop(ev)
|
||||||
this.#prevent(ev)
|
this.#prevent(ev)
|
||||||
|
|
|
@ -13,8 +13,7 @@ import {
|
||||||
__props__,
|
__props__,
|
||||||
__changed_props__,
|
__changed_props__,
|
||||||
__mounted__,
|
__mounted__,
|
||||||
__pending__,
|
__pending__
|
||||||
__children__
|
|
||||||
} from './constants.js'
|
} from './constants.js'
|
||||||
import { css, adoptStyles } from './css.js'
|
import { css, adoptStyles } from './css.js'
|
||||||
import { render, html, svg, raw } from './html.js'
|
import { render, html, svg, raw } from './html.js'
|
||||||
|
@ -406,7 +405,7 @@ export class Component extends HTMLElement {
|
||||||
#render() {
|
#render() {
|
||||||
try {
|
try {
|
||||||
let ast = this.render()
|
let ast = this.render()
|
||||||
this[__children__] = render(ast, this.root, {
|
render(ast, this.root, {
|
||||||
host: this
|
host: this
|
||||||
})
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
Loading…
Reference in New Issue