优化模板解析;修复事件绑定

master
yutent 2023-11-30 00:17:59 +08:00
parent bbe04eae83
commit b4e7f5dfae
3 changed files with 78 additions and 64 deletions

View File

@ -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);}`

View File

@ -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)

View File

@ -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) {