优化模板解析;修复属性代理逻辑;优化事件销毁机制

master 1.11.2
yutent 2023-11-30 11:35:43 +08:00
parent b4e7f5dfae
commit ea6010c782
3 changed files with 69 additions and 56 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "wkit", "name": "wkit",
"version": "1.11.1", "version": "1.11.2",
"type": "module", "type": "module",
"description": "A library for building fast, lightweight web components.", "description": "A library for building fast, lightweight web components.",
"main": "dist/index.js", "main": "dist/index.js",

View File

@ -143,16 +143,20 @@ function getTemplateHtml(strings, type) {
} }
class Template { class Template {
parts = []
constructor({ strings, ['__dom_type__']: type }, options) { constructor({ strings, ['__dom_type__']: type }, options) {
this.parts = []
let node let node
let nodeIndex = 0 let nodeIndex = 0
let attrNameIndex = 0 let attrNameIndex = 0
let partCount = strings.length - 1 let partCount = strings.length - 1
let parts = this.parts let parts = this.parts
let [html2, attrNames] = getTemplateHtml(strings, type) let [html2, attrNames] = getTemplateHtml(strings, type)
this.el = Template.createElement(html2) this.el = Template.createElement(html2)
walker.currentNode = this.el.content walker.currentNode = this.el.content
if (type === SVG_RESULT) { if (type === SVG_RESULT) {
let content = this.el.content let content = this.el.content
let svgElement = content.firstChild let svgElement = content.firstChild
@ -327,6 +331,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) {
@ -344,10 +349,10 @@ class TemplateInstance {
} }
class ChildPart { class ChildPart {
type = CHILD_PART
#value = NOTHING #value = NOTHING
constructor(startNode, endNode, parent, options) { constructor(startNode, endNode, parent, options) {
this.type = CHILD_PART
this.startNode = startNode this.startNode = startNode
this.endNode = endNode this.endNode = endNode
this.$parent = parent this.$parent = parent
@ -363,7 +368,7 @@ class ChildPart {
return parentNode return parentNode
} }
$setValue(value) { $setValue(value, clear) {
if (isPrimitive(value)) { if (isPrimitive(value)) {
if (value === NOTHING || value == null || value === '') { if (value === NOTHING || value == null || value === '') {
if (this.#value !== NOTHING) { if (this.#value !== NOTHING) {
@ -382,16 +387,22 @@ class ChildPart {
} else { } else {
this.#commitText(value) this.#commitText(value)
} }
if (clear) {
nextTick(_ => this.#clearBindings())
} }
}
#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.#value !== value) { if (this.#value !== value) {
this.#clear() this.#clear()
this.#value = this.#insert(value) this.#value = this.#insert(value)
} }
} }
#commitText(value) { #commitText(value) {
if (this.#value !== NOTHING && isPrimitive(this.#value)) { if (this.#value !== NOTHING && isPrimitive(this.#value)) {
let node = this.startNode.nextSibling let node = this.startNode.nextSibling
@ -402,6 +413,7 @@ class ChildPart {
} }
this.#value = value this.#value = value
} }
#commitTemplateResult(result) { #commitTemplateResult(result) {
let { values, ['__dom_type__']: type } = result let { values, ['__dom_type__']: type } = result
let template = let template =
@ -422,12 +434,14 @@ class ChildPart {
this.#value = instance this.#value = instance
} }
} }
#getTemplate(result) { #getTemplate(result) {
let template = TEMPLATE_CACHE.get(result.strings.join()) let key = result.strings.join()
let template = TEMPLATE_CACHE.get(key)
if (template === void 0) { if (template === void 0) {
template = new Template(result, this.options) template = new Template(result, this.options)
TEMPLATE_CACHE.set(result.strings.join(), template) TEMPLATE_CACHE.set(key, template)
} }
return template return template
} }
@ -469,14 +483,32 @@ class ChildPart {
start = node start = node
} }
} }
/**
* 清除已被移除的节点事件
*/
#clearBindings() {
let host = this.options.host
let events = host.$events
for (let ev in events) {
for (let i = -1, it; (it = events[ev][++i]); ) {
if (!host.root.contains(it.el)) {
it.el.removeEventListener(this.name, it.listener, it.options)
events[ev][i] = null
}
}
events[ev] = events[ev].filter(e => e !== null)
}
}
} }
// 常规属性 // 常规属性
class AttributePart { class AttributePart {
type = ATTRIBUTE_PART
#value = NOTHING #value = NOTHING
constructor(element, name, strings, decorates, parent, options = {}) { constructor(element, name, strings, decorates, parent, options = {}) {
this.type = ATTRIBUTE_PART
this.element = element this.element = element
this.name = name this.name = name
this.decorates = decorates this.decorates = decorates
@ -494,6 +526,7 @@ class AttributePart {
$setValue(value, valueIndex) { $setValue(value, valueIndex) {
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.#value changed = !isPrimitive(value) || value !== this.#value
if (changed) { if (changed) {
@ -521,26 +554,28 @@ class AttributePart {
} }
commitValue(value) { commitValue(value) {
let isBoolAttr = boolMap[this.name] let elem = this.element
let attr = this.name
let isBoolAttr = boolMap[attr]
// ref属性不渲染到节点上 // ref属性不渲染到节点上
if (this.name === 'ref') { if (attr === 'ref') {
this.options.host.$refs[value] = this.element this.options.host.$refs[value] = elem
return return
} }
if (isBoolAttr) { if (isBoolAttr) {
this.element[isBoolAttr] = !(value === false || value === null) elem[isBoolAttr] = !(value === false || value === null)
if (this.element[isBoolAttr]) { if (elem[isBoolAttr]) {
this.element.setAttribute(this.name, '') elem.setAttribute(attr, '')
} else { } else {
this.element.removeAttribute(this.name) elem.removeAttribute(attr)
} }
} else { } else {
if (value === null || value === void 0) { if (value === null || value === void 0) {
this.element.removeAttribute(this.name) elem.removeAttribute(attr)
} else { } else {
this.element.setAttribute(this.name, value) elem.setAttribute(attr, value)
} }
} }
} }
@ -562,20 +597,23 @@ class AnimPart extends AttributePart {
} }
commitValue({ type = 'fade', duration, custom, immediate = false } = {}) { commitValue({ type = 'fade', duration, custom, immediate = false } = {}) {
let elem = this.element
let fromto = MODES[type] || MODES.fade let fromto = MODES[type] || MODES.fade
if (custom) { if (custom) {
fromto = custom fromto = custom
} }
this.element.$animate = function (out = false) { elem.$animate = function (out = false) {
return animate.call(this, duration, fromto, out) return animate.call(this, duration, fromto, out)
} }
this.element.$animate.immediate = immediate elem.$animate.immediate = immediate
} }
} }
// 事件属性 // 事件属性
class EventPart extends AttributePart { class EventPart extends AttributePart {
type = EVENT_PART
#listener = null #listener = null
#prevent = noop #prevent = noop
@ -584,30 +622,14 @@ class EventPart extends AttributePart {
constructor(...args) { constructor(...args) {
super(...args) super(...args)
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 elem = this.element
let name = this.name
let options = {} let options = {}
let events = host.$events[this.name] let events = host.$events[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) {
@ -619,7 +641,7 @@ class EventPart extends AttributePart {
this.#prevent = ev => ev.preventDefault() this.#prevent = ev => ev.preventDefault()
break break
case 'self': case 'self':
this.#checkSelf = ev => ev.target === this.element this.#checkSelf = ev => ev.target === elem
break break
case 'capture': case 'capture':
case 'once': case 'once':
@ -634,14 +656,14 @@ class EventPart extends AttributePart {
if (this.#listener) { if (this.#listener) {
for (let i = -1, it; (it = events[++i]); ) { for (let i = -1, it; (it = events[++i]); ) {
if (it.el === this.element) { if (it.el === elem) {
shouldRemove = 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) it.el.removeEventListener(name, it.listener, it.options)
events[i] = null events[i] = null
} }
break break
@ -650,20 +672,14 @@ class EventPart extends AttributePart {
} }
events = events.filter(it => it !== null) events = events.filter(it => it !== null)
host.$events[this.name] = events host.$events[name] = events
if (listener && shouldRemove) { if (listener && shouldRemove) {
this.element.addEventListener(this.name, this, options) elem.addEventListener(name, this, options)
this.#listener = listener this.#listener = listener
events.push({ events.push({ el: elem, listener: this, options })
el: this.element,
listener: this,
options
})
} }
nextTick(_ => this.#clearBindings())
} }
handleEvent(ev) { handleEvent(ev) {
@ -677,14 +693,14 @@ class EventPart extends AttributePart {
} }
class ElementPart { class ElementPart {
type = ELEMENT_PART
constructor(element, parent, options) { constructor(element, parent, options) {
this.element = element this.element = element
this.type = ELEMENT_PART
this.$parent = parent this.$parent = parent
this.options = options this.options = options
} }
$setValue(value) {} $setValue() {}
} }
export function render(value, container, options = {}) { export function render(value, container, options = {}) {
@ -699,7 +715,7 @@ export function render(value, container, options = {}) {
) )
container[WC_PART] = part container[WC_PART] = part
} }
part.$setValue(value) part.$setValue(value, true)
return part return part
} }

View File

@ -223,9 +223,6 @@ export class Component extends HTMLElement {
return value return value
}, },
set: (target, prop, value, receiver) => { set: (target, prop, value, receiver) => {
if (prop === 'length' && options.type === Array) {
return true
}
let oldValue = target[prop] let oldValue = target[prop]
Reflect.set(target, prop, value, receiver) Reflect.set(target, prop, value, receiver)