From a1f48235377b91eb8902ab1a5fb91f356933fef6 Mon Sep 17 00:00:00 2001 From: yutent Date: Fri, 10 Mar 2023 18:38:52 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=BB=84=E4=BB=B6=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E9=80=BB=E8=BE=91;=E6=94=B9=E7=94=A8Symbol=E8=A7=A3?= =?UTF-8?q?=E5=86=B3=E5=8E=9F=E5=9E=8B=E6=96=B9=E6=B3=95=E8=A2=AB=E8=A6=86?= =?UTF-8?q?=E7=9B=96=E7=9A=84=E9=A3=8E=E9=99=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants.js | 45 +++++++++++-- src/index.js | 168 ++++++++++++++++++++--------------------------- 2 files changed, 113 insertions(+), 100 deletions(-) diff --git a/src/constants.js b/src/constants.js index 088eccb..ccfa6cd 100644 --- a/src/constants.js +++ b/src/constants.js @@ -4,11 +4,45 @@ * @date 2023/03/06 12:08:35 */ -export const FINALIZED = Symbol('finalized') -export const UPDATE = Symbol('update') +const boolMap = {} + +;[ + 'autofocus', + 'autoplay', + 'async', + 'allowTransparency', + 'checked', + 'controls', + 'declare', + 'disabled', + 'defer', + 'defaultChecked', + 'defaultSelected', + 'contentEditable', + 'isMap', + 'loop', + 'multiple', + 'noHref', + 'noResize', + 'noShade', + 'open', + 'readOnly', + 'selected' +].forEach(function (name) { + boolMap[name.toLowerCase()] = name +}) + +export { boolMap } + export const WC_PART = Symbol('wc_path') export const NO_CHANGE = Symbol('wc-noChange') export const NOTHING = Symbol('wc-nothing') +export const __finalized__ = Symbol('finalized') +export const __update__ = Symbol('update') +export const __init__ = Symbol('init') +export const __props__ = Symbol('props') +export const __changed_props__ = Symbol('changed_props') +export const __mounted__ = Symbol('mounted') export const RESET_CSS_STYLE = ` * {box-sizing: border-box;margin: 0;padding: 0;} @@ -19,6 +53,7 @@ export const DEFAULT_CONVERTER = { toAttribute(value, type) { switch (type) { case Boolean: + // console.log(this, '>>>', value) value = value ? '' : null break case Object: @@ -57,7 +92,9 @@ export function notEqual(value, old) { export const DEFAULT_PROPERTY_DECLARATION = { attribute: true, type: String, - converter: DEFAULT_CONVERTER, + formater: DEFAULT_CONVERTER, + // converter: DEFAULT_CONVERTER, reflect: false, - hasChanged: notEqual + hasChanged: notEqual, + default: '' } diff --git a/src/index.js b/src/index.js index 5651f2c..a8e0655 100644 --- a/src/index.js +++ b/src/index.js @@ -4,11 +4,16 @@ * @date 2023/03/07 18:10:43 */ import { - FINALIZED, - UPDATE, DEFAULT_CONVERTER, DEFAULT_PROPERTY_DECLARATION, - notEqual + notEqual, + boolMap, + __finalized__, + __update__, + __init__, + __props__, + __changed_props__, + __mounted__ } from './constants.js' import { css, adoptStyles } from './css.js' import { render, html, svg } from './html.js' @@ -24,78 +29,71 @@ export { export { html, css, svg, bind, unbind } export class Component extends HTMLElement { - constructor() { - super() - - this.__instanceProperties = new Map() - this.isUpdatePending = false - this.hasUpdated = false - this.__reflectingProperty = null - this._initialize() - this.created && this.created() - } - static addInitializer(initializer) { - this.finalize() - if (!this._initializers) { - this._initializers = [] - } - this._initializers.push(initializer) - } + /** + * 声明可监听变化的属性列表 + * @return list + */ static get observedAttributes() { + let list = [] + this.finalize() - const attributes = [] - this.elementProperties.forEach((v, p) => { - const attr = this.__attributeNameForProperty(p, v) - if (attr !== void 0) { - this.__attributeToPropertyMap.set(attr, p) - attributes.push(attr) + + this[__props__].forEach((options, prop) => { + if (options) { + options.watch && list.push(k.toLowerCase()) + } else { + list.push(k.toLowerCase()) } }) - return attributes + return list } static createProperty(name, options = DEFAULT_PROPERTY_DECLARATION) { if (options.state) { options.attribute = false } - this.elementProperties.set(name, options) + this[__props__].set(name, options) let key = Symbol(name) - let descriptor = this.getPropertyDescriptor(name, key, options) - - this.prototype[key] = options.default - Object.defineProperty(this.prototype, name, descriptor) - } - static getPropertyDescriptor(name, key, options) { - return { + let descriptor = { get() { return this[key] }, set(value) { - const oldValue = this[name] + let oldValue = this[key] this[key] = value this.requestUpdate(name, oldValue, options) }, - configurable: true + enumerable: false } + + Object.defineProperty(this.prototype, name, descriptor) + + // this.prototype[name] = options.default } - static getPropertyOptions(name) { - return this.elementProperties.get(name) || DEFAULT_PROPERTY_DECLARATION - } + + // 处理静态声明 static finalize() { - if (this[FINALIZED]) { + if (this[__finalized__]) { return false } - this[FINALIZED] = true + this[__finalized__] = true + + this[__props__] = new Map() - this.elementProperties = new Map() - this.__attributeToPropertyMap = new Map() if (this.hasOwnProperty('props')) { for (let k in this.props) { + if (boolMap[k] && k !== boolMap[k]) { + this.props[boolMap[k]] = this.props[k] + delete this.props[k] + k = boolMap[k] + } this.createProperty(k, this.props[k]) } } + delete this.props + return true } @@ -109,33 +107,25 @@ export class Component extends HTMLElement { ? name.toLowerCase() : void 0 } - _initialize() { - this.__updatePromise = new Promise(res => (this.enableUpdating = res)) - this._$changedProperties = new Map() - this.__saveInstanceProperties() - this.requestUpdate() - this.constructor._initializers?.forEach(i => i(this)) - } - addController(controller) { - if (!this.__controllers) { - this.__controllers = [] - } - this.__controllers.push(controller) - if (this.root !== void 0 && this.isConnected) { - controller.hostConnected?.call(controller) - } + constructor() { + super() + + this.isUpdatePending = false + this.__reflectingProperty = null + this[__mounted__] = false + this[__init__]() + this.created && this.created() } - removeController(controller) { - this.__controllers?.splice(this.__controllers.indexOf(controller) >>> 0, 1) - } - __saveInstanceProperties() { - this.constructor.elementProperties.forEach((_v, p) => { - if (this.hasOwnProperty(p)) { - this.__instanceProperties.set(p, this[p]) - delete this[p] - } + + [__init__]() { + this.__updatePromise = new Promise(res => (this.enableUpdating = res)) + this[__changed_props__] = new Map() // 记录本次变化的属性 + // 初始化 props + this.constructor[__props__].forEach((options, prop) => { + this[prop] = options.default }) + this.requestUpdate() } connectedCallback() { @@ -145,14 +135,10 @@ export class Component extends HTMLElement { this.enableUpdating(true) - this.__controllers?.forEach(it => it.hostConnected?.call(it)) this.__childPart?.setConnected(true) - - this.mounted && this.mounted() } - enableUpdating(_requestedUpdate) {} + disconnectedCallback() { - this.__controllers?.forEach(it => it.hostDisconnected?.call(it)) this.__childPart?.setConnected(false) } attributeChangedCallback(name, _old, value) { @@ -194,11 +180,11 @@ export class Component extends HTMLElement { requestUpdate(name, oldValue, options) { let shouldRequestUpdate = true if (name !== void 0) { - options = options || this.constructor.getPropertyOptions(name) + options = options || this.constructor[__props__][name] const hasChanged = options.hasChanged || notEqual if (hasChanged(this[name], oldValue)) { - if (!this._$changedProperties.has(name)) { - this._$changedProperties.set(name, oldValue) + if (!this[__changed_props__].has(name)) { + this[__changed_props__].set(name, oldValue) } if (options.reflect === true && this.__reflectingProperty !== name) { if (this.__reflectingProperties === void 0) { @@ -235,15 +221,9 @@ export class Component extends HTMLElement { return } - if (this.__instanceProperties) { - this.__instanceProperties.forEach((v, p) => (this[p] = v)) - this.__instanceProperties = void 0 - } - - const changedProperties = this._$changedProperties + const changedProperties = this[__changed_props__] try { - this.__controllers?.forEach(it => it.hostUpdate?.call(it)) - this[UPDATE](changedProperties) + this[__update__](changedProperties) this._$didUpdate(changedProperties) } catch (e) { this.__markUpdated() @@ -251,16 +231,14 @@ export class Component extends HTMLElement { } } _$didUpdate(changedProperties) { - this.__controllers?.forEach(it => it.hostUpdated?.call(it)) - - if (!this.hasUpdated) { - this.hasUpdated = true - this.firstUpdated(changedProperties) + if (!this[__mounted__]) { + this[__mounted__] = true + this.mounted && this.mounted() } this.updated(changedProperties) } __markUpdated() { - this._$changedProperties = new Map() + this[__changed_props__] = new Map() this.isUpdatePending = false } get updateComplete() { @@ -270,8 +248,8 @@ export class Component extends HTMLElement { return this.__updatePromise } - [UPDATE](_changedProperties) { - let value = this.render() + [__update__](_changedProperties) { + let htmlText = this.render() if (this.__reflectingProperties !== void 0) { this.__reflectingProperties.forEach((v, k) => @@ -280,14 +258,12 @@ export class Component extends HTMLElement { this.__reflectingProperties = void 0 } this.__markUpdated() - - this.__childPart = render(value, this.root, { + this.__childPart = render(htmlText, this.root, { host: this, - isConnected: !this.hasUpdated && this.isConnected + isConnected: !this[__mounted__] && this.isConnected }) } updated(_changedProperties) {} - firstUpdated(_changedProperties) {} $on(type, callback) { return bind(this, type, callback)