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

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",
"version": "1.11.1",
"version": "1.11.2",
"type": "module",
"description": "A library for building fast, lightweight web components.",
"main": "dist/index.js",

View File

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

View File

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