From 30344bed024a5f83097f506d4ed0f01f9f97ae58 Mon Sep 17 00:00:00 2001 From: errorrik Date: Sat, 30 Sep 2023 10:47:06 +0800 Subject: [PATCH 01/19] document -> ownerDocument --- src/browser/trigger.js | 2 +- src/view/async-component.js | 6 ++---- src/view/component.js | 10 +++++----- src/view/dom-children-walker.js | 1 + src/view/element.js | 9 +++++---- src/view/for-node.js | 8 +++----- src/view/fragment-node.js | 4 ++-- src/view/if-node.js | 6 ++---- src/view/node-own-create-stump.js | 19 ------------------- src/view/node-own-only-children-attach.js | 5 +++-- src/view/preheat-el.js | 8 ++++---- src/view/slot-node.js | 4 ++-- src/view/template-component.js | 9 +++++---- src/view/text-node.js | 20 +++++++++----------- src/view/warn-set-html.js | 3 +-- 15 files changed, 45 insertions(+), 69 deletions(-) delete mode 100644 src/view/node-own-create-stump.js diff --git a/src/browser/trigger.js b/src/browser/trigger.js index 87f240261..c755cf6ea 100644 --- a/src/browser/trigger.js +++ b/src/browser/trigger.js @@ -15,7 +15,7 @@ * @param {string} eventName 事件名 */ function trigger(el, eventName) { - var event = document.createEvent('HTMLEvents'); + var event = el.ownerDocument.createEvent('HTMLEvents'); event.initEvent(eventName, true, true); el.dispatchEvent(event); } diff --git a/src/view/async-component.js b/src/view/async-component.js index 55bdf6590..46868b243 100644 --- a/src/view/async-component.js +++ b/src/view/async-component.js @@ -10,7 +10,6 @@ var guid = require('../util/guid'); var each = require('../util/each'); var insertBefore = require('../browser/insert-before'); -var nodeOwnCreateStump = require('./node-own-create-stump'); var nodeOwnSimpleDispose = require('./node-own-simple-dispose'); @@ -35,7 +34,7 @@ function AsyncComponent(options, loader) { this.children[0] = new PlaceholderComponent(options); } - this._create(); + this.el = hydrateWalker.doc.createComment(this.id); insertBefore(this.el, hydrateWalker.target, hydrateWalker.current); var me = this; @@ -47,7 +46,6 @@ function AsyncComponent(options, loader) { // #[end] } -AsyncComponent.prototype._create = nodeOwnCreateStump; AsyncComponent.prototype.dispose = nodeOwnSimpleDispose; /** @@ -64,7 +62,7 @@ AsyncComponent.prototype.attach = function (parentEl, beforeEl) { component.attach(parentEl, beforeEl); } - this._create(); + this.el = parentEl.ownerDocument.createComment(this.id); insertBefore(this.el, parentEl, beforeEl); var me = this; diff --git a/src/view/component.js b/src/view/component.js index 8f802bd40..7f4988ac3 100644 --- a/src/view/component.js +++ b/src/view/component.js @@ -1027,16 +1027,16 @@ Component.prototype.attach = function (parentEl, beforeEl) { // #[end] var props; - + var doc = parentEl.ownerDocument; if (aNode._ce && aNode._i > 2) { props = aNode._dp; - this.el = (aNode._el || preheatEl(aNode)).cloneNode(false); + this.el = (aNode._el || preheatEl(aNode, doc)).cloneNode(false); } else { props = aNode.props; - this.el = svgTags[this.tagName] && document.createElementNS - ? document.createElementNS('http://www.w3.org/2000/svg', this.tagName) - : document.createElement(this.tagName); + this.el = svgTags[this.tagName] && doc.createElementNS + ? doc.createElementNS('http://www.w3.org/2000/svg', this.tagName) + : doc.createElement(this.tagName); } if (this._sbindData) { diff --git a/src/view/dom-children-walker.js b/src/view/dom-children-walker.js index f827c07ac..813fa9568 100644 --- a/src/view/dom-children-walker.js +++ b/src/view/dom-children-walker.js @@ -21,6 +21,7 @@ var removeEl = require('../browser/remove-el'); function DOMChildrenWalker(el, onlyCurrent) { this.index = 0; this.target = el; + this.doc = el.ownerDocument; if (onlyCurrent) { this.children = [onlyCurrent, onlyCurrent.nextSibling]; diff --git a/src/view/element.js b/src/view/element.js index 7b4103500..32d43cb91 100644 --- a/src/view/element.js +++ b/src/view/element.js @@ -116,19 +116,20 @@ Element.prototype.nodeType = NodeType.ELEM; Element.prototype.attach = function (parentEl, beforeEl) { if (!this.lifeCycle.attached) { var aNode = this.aNode; + var doc = parentEl.ownerDocument; if (!this.el) { var props; if (aNode._ce && aNode._i > 2) { props = aNode._dp; - this.el = (aNode._el || preheatEl(aNode)).cloneNode(false); + this.el = (aNode._el || preheatEl(aNode, doc)).cloneNode(false); } else { props = aNode.props; - this.el = svgTags[this.tagName] && document.createElementNS - ? document.createElementNS('http://www.w3.org/2000/svg', this.tagName) - : document.createElement(this.tagName); + this.el = svgTags[this.tagName] && doc.createElementNS + ? doc.createElementNS('http://www.w3.org/2000/svg', this.tagName) + : doc.createElement(this.tagName); } if (this._sbindData) { diff --git a/src/view/for-node.js b/src/view/for-node.js index c8fff83cf..20dbb24bd 100644 --- a/src/view/for-node.js +++ b/src/view/for-node.js @@ -22,7 +22,6 @@ var NodeType = require('./node-type'); var createNode = require('./create-node'); var createHydrateNode = require('./create-hydrate-node'); var nodeOwnSimpleDispose = require('./node-own-simple-dispose'); -var nodeOwnCreateStump = require('./node-own-create-stump'); /** @@ -200,7 +199,7 @@ function ForNode(aNode, parent, scope, owner, hydrateWalker) { } } - this._create(); + this.el = hydrateWalker.doc.createComment(this.id); insertBefore(this.el, hydrateWalker.target, hydrateWalker.current); } // #[end] @@ -208,7 +207,6 @@ function ForNode(aNode, parent, scope, owner, hydrateWalker) { ForNode.prototype.nodeType = NodeType.FOR; -ForNode.prototype._create = nodeOwnCreateStump; ForNode.prototype.dispose = nodeOwnSimpleDispose; /** @@ -218,7 +216,7 @@ ForNode.prototype.dispose = nodeOwnSimpleDispose; * @param {HTMLElement=} beforeEl 要添加到哪个元素之前 */ ForNode.prototype.attach = function (parentEl, beforeEl) { - this._create(); + this.el = parentEl.ownerDocument.createComment(this.id); insertBefore(this.el, parentEl, beforeEl); this.listData = evalExpr(this.param.value, this.scope, this.owner); @@ -359,7 +357,7 @@ ForNode.prototype._disposeChildren = function (children, callback) { } // #[end] - this.el = document.createComment(this.id); + this.el = parentEl.ownerDocument.createComment(this.id); parentEl.appendChild(this.el); callback && callback(); } diff --git a/src/view/fragment-node.js b/src/view/fragment-node.js index b7d2ecb4c..b7fa7581c 100644 --- a/src/view/fragment-node.js +++ b/src/view/fragment-node.js @@ -50,7 +50,7 @@ function FragmentNode(aNode, parent, scope, owner, hydrateWalker) { hydrateWalker.goNext(); } else { - this.sel = document.createComment(this.id); + this.sel = hydrateWalker.doc.createComment(this.id); insertBefore(this.sel, hydrateWalker.target, hydrateWalker.current); } @@ -68,7 +68,7 @@ function FragmentNode(aNode, parent, scope, owner, hydrateWalker) { hydrateWalker.goNext(); } else { - this.el = document.createComment(this.id); + this.el = hydrateWalker.doc.createComment(this.id); insertBefore(this.el, hydrateWalker.target, hydrateWalker.current); } diff --git a/src/view/if-node.js b/src/view/if-node.js index 49cf2ca7b..a9c352df1 100644 --- a/src/view/if-node.js +++ b/src/view/if-node.js @@ -13,7 +13,6 @@ var evalExpr = require('../runtime/eval-expr'); var NodeType = require('./node-type'); var createNode = require('./create-node'); var createHydrateNode = require('./create-hydrate-node'); -var nodeOwnCreateStump = require('./node-own-create-stump'); var nodeOwnSimpleDispose = require('./node-own-simple-dispose'); /** @@ -72,7 +71,7 @@ function IfNode(aNode, parent, scope, owner, hydrateWalker) { } } - this._create(); + this.el = hydrateWalker.doc.createComment(this.id); insertBefore(this.el, hydrateWalker.target, hydrateWalker.current); } // #[end] @@ -80,7 +79,6 @@ function IfNode(aNode, parent, scope, owner, hydrateWalker) { IfNode.prototype.nodeType = NodeType.IF; -IfNode.prototype._create = nodeOwnCreateStump; IfNode.prototype.dispose = nodeOwnSimpleDispose; /** @@ -118,7 +116,7 @@ IfNode.prototype.attach = function (parentEl, beforeEl) { } - this._create(); + this.el = parentEl.ownerDocument.createComment(this.id); insertBefore(this.el, parentEl, beforeEl); }; diff --git a/src/view/node-own-create-stump.js b/src/view/node-own-create-stump.js deleted file mode 100644 index 5fca1cc01..000000000 --- a/src/view/node-own-create-stump.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) Baidu Inc. All rights reserved. - * - * This source code is licensed under the MIT license. - * See LICENSE file in the project root for license information. - * - * @file 创建节点对应的 stump comment 元素 - */ - - - -/** - * 创建节点对应的 stump comment 主元素 - */ -function nodeOwnCreateStump() { - this.el = this.el || document.createComment(this.id); -} - -exports = module.exports = nodeOwnCreateStump; diff --git a/src/view/node-own-only-children-attach.js b/src/view/node-own-only-children-attach.js index 71f3ca8df..f4401c6cd 100644 --- a/src/view/node-own-only-children-attach.js +++ b/src/view/node-own-only-children-attach.js @@ -20,7 +20,8 @@ var createNode = require('./create-node'); * @param {HTMLElement=} beforeEl 要添加到哪个元素之前 */ function nodeOwnOnlyChildrenAttach(parentEl, beforeEl) { - this.sel = document.createComment(this.id); + var doc = parentEl.ownerDocument; + this.sel = doc.createComment(this.id); insertBefore(this.sel, parentEl, beforeEl); for (var i = 0; i < this.aNode.children.length; i++) { @@ -34,7 +35,7 @@ function nodeOwnOnlyChildrenAttach(parentEl, beforeEl) { child.attach(parentEl, beforeEl); } - this.el = document.createComment(this.id); + this.el = doc.createComment(this.id); insertBefore(this.el, parentEl, beforeEl); this.lifeCycle = LifeCycle.attached; diff --git a/src/view/preheat-el.js b/src/view/preheat-el.js index 0ffff2d99..74953a98e 100644 --- a/src/view/preheat-el.js +++ b/src/view/preheat-el.js @@ -15,10 +15,10 @@ var svgTags = require('../browser/svg-tags'); * @param {Object} aNode 要预热的ANode * @return {HTMLElement} */ -function preheatEl(aNode) { - var el = svgTags[aNode.tagName] && document.createElementNS - ? document.createElementNS('http://www.w3.org/2000/svg', aNode.tagName) - : document.createElement(aNode.tagName); +function preheatEl(aNode, doc) { + var el = svgTags[aNode.tagName] && doc.createElementNS + ? doc.createElementNS('http://www.w3.org/2000/svg', aNode.tagName) + : doc.createElement(aNode.tagName); aNode._el = el; for (var i = 0, l = aNode.props.length; i < l; i++) { diff --git a/src/view/slot-node.js b/src/view/slot-node.js index 7eb299f45..a1ef3bc9c 100644 --- a/src/view/slot-node.js +++ b/src/view/slot-node.js @@ -116,7 +116,7 @@ function SlotNode(aNode, parent, scope, owner, hydrateWalker) { hydrateWalker.goNext(); } else { - this.sel = document.createComment(this.id); + this.sel = hydrateWalker.doc.createComment(this.id); hydrateWalker.current ? hydrateWalker.target.insertBefore(this.sel, hydrateWalker.current) : hydrateWalker.target.appendChild(this.sel); @@ -139,7 +139,7 @@ function SlotNode(aNode, parent, scope, owner, hydrateWalker) { hydrateWalker.goNext(); } else { - this.el = document.createComment(this.id); + this.el = hydrateWalker.doc.createComment(this.id); hydrateWalker.current ? hydrateWalker.target.insertBefore(this.el, hydrateWalker.current) : hydrateWalker.target.appendChild(this.el); diff --git a/src/view/template-component.js b/src/view/template-component.js index 5541416aa..15800c368 100644 --- a/src/view/template-component.js +++ b/src/view/template-component.js @@ -246,16 +246,17 @@ TemplateComponent.prototype.attach = function (parentEl, beforeEl) { // #[end] var props; + var doc = parentEl.ownerDocument; if (aNode._ce && aNode._i > 2) { props = aNode._dp; - this.el = (aNode._el || preheatEl(aNode)).cloneNode(false); + this.el = (aNode._el || preheatEl(aNode, doc)).cloneNode(false); } else { props = aNode.props; - this.el = svgTags[this.tagName] && document.createElementNS - ? document.createElementNS('http://www.w3.org/2000/svg', this.tagName) - : document.createElement(this.tagName); + this.el = svgTags[this.tagName] && doc.createElementNS + ? doc.createElementNS('http://www.w3.org/2000/svg', this.tagName) + : doc.createElement(this.tagName); } if (this._sbindData) { diff --git a/src/view/text-node.js b/src/view/text-node.js index 2f385723b..8bdd906b9 100644 --- a/src/view/text-node.js +++ b/src/view/text-node.js @@ -8,7 +8,7 @@ */ var guid = require('../util/guid'); -var isBrowser = require('../browser/is-browser'); +var ie = require('../browser/ie'); var removeEl = require('../browser/remove-el'); var insertBefore = require('../browser/insert-before'); var changeExprCompare = require('../runtime/change-expr-compare'); @@ -76,7 +76,7 @@ function TextNode(aNode, parent, scope, owner, hydrateWalker) { } } else { - this.el = document.createTextNode(''); + this.el = hydrateWalker.doc.createTextNode(''); insertBefore(this.el, hydrateWalker.target, hydrateWalker.current); } } @@ -97,21 +97,22 @@ TextNode.prototype.attach = function (parentEl, beforeEl) { this.content = ''; } + var doc = parentEl.ownerDocument; if (this.aNode.textExpr.original) { this.id = this.id || guid++; - this.sel = document.createComment(this.id); + this.sel = doc.createComment(this.id); insertBefore(this.sel, parentEl, beforeEl); - this.el = document.createComment(this.id); + this.el = doc.createComment(this.id); insertBefore(this.el, parentEl, beforeEl); - var tempFlag = document.createElement('script'); + var tempFlag = doc.createElement('script'); parentEl.insertBefore(tempFlag, this.el); tempFlag.insertAdjacentHTML('beforebegin', this.content); parentEl.removeChild(tempFlag); } else { - this.el = document.createTextNode(this.content); + this.el = doc.createTextNode(this.content); insertBefore(this.el, parentEl, beforeEl); } }; @@ -131,10 +132,7 @@ TextNode.prototype.dispose = function (noDetach) { this.sel = null; }; -var textUpdateProp = isBrowser - && (typeof document.createTextNode('').textContent === 'string' - ? 'textContent' - : 'data'); +var textUpdateProp = ie && ie < 9 ? 'data' : 'textContent'; /** * 更新 text 节点的视图 @@ -171,7 +169,7 @@ TextNode.prototype._update = function (changes) { warnSetHTML(parentEl); // #[end] - var tempFlag = document.createElement('script'); + var tempFlag = parentEl.ownerDocument.createElement('script'); parentEl.insertBefore(tempFlag, this.el); tempFlag.insertAdjacentHTML('beforebegin', text); parentEl.removeChild(tempFlag); diff --git a/src/view/warn-set-html.js b/src/view/warn-set-html.js index 8c1d8d231..09e3c99ec 100644 --- a/src/view/warn-set-html.js +++ b/src/view/warn-set-html.js @@ -18,13 +18,12 @@ var warn = require('../util/warn'); function warnSetHTML(el) { // dont warn if not in browser runtime /* istanbul ignore if */ - if (!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document)) { + if (!(typeof window !== 'undefined' && typeof navigator !== 'undefined')) { return; } // some html elements cannot set innerHTML in old ie // see: https://msdn.microsoft.com/en-us/library/ms533897(VS.85).aspx - if (/^(col|colgroup|frameset|style|table|tbody|tfoot|thead|tr|select)$/i.test(el.tagName)) { warn('set html for element "' + el.tagName + '" may cause an error in old IE'); } From ad01ca53a5478adc66b47d773f8a619e17c7c387 Mon Sep 17 00:00:00 2001 From: errorrik Date: Sat, 30 Sep 2023 11:30:38 +0800 Subject: [PATCH 02/19] move ie9 input event compatible code to element attached function --- src/browser/input-event-compatible.js | 24 ------------------------ src/main.js | 1 - src/view/element-own-attached.js | 23 +++++++++++++++++++++++ 3 files changed, 23 insertions(+), 25 deletions(-) delete mode 100644 src/browser/input-event-compatible.js diff --git a/src/browser/input-event-compatible.js b/src/browser/input-event-compatible.js deleted file mode 100644 index 067d1ddd9..000000000 --- a/src/browser/input-event-compatible.js +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) Baidu Inc. All rights reserved. - * - * This source code is licensed under the MIT license. - * See LICENSE file in the project root for license information. - * - * @file 解决 IE9 在表单元素中删除字符时不触发事件的问题 - */ - -var ie = require('./ie'); -var on = require('./on'); -var trigger = require('./trigger'); - -// #[begin] allua -/* istanbul ignore if */ -if (ie === 9) { - on(document, 'selectionchange', function () { - var el = document.activeElement; - if (el.tagName === 'TEXTAREA' || el.tagName === 'INPUT') { - trigger(el, 'input'); - } - }); -} -// #[end] diff --git a/src/main.js b/src/main.js index 8dc18f366..587318110 100644 --- a/src/main.js +++ b/src/main.js @@ -22,7 +22,6 @@ // require('./util/next-tick'); // require('./browser/ie'); // require('./browser/ie-old-than-9'); - // require('./browser/input-event-compatible'); // require('./browser/auto-close-tags'); // require('./util/data-types.js'); // require('./util/create-data-types-checker.js'); diff --git a/src/view/element-own-attached.js b/src/view/element-own-attached.js index ee59ae63a..b0b9ed99f 100644 --- a/src/view/element-own-attached.js +++ b/src/view/element-own-attached.js @@ -11,6 +11,8 @@ var empty = require('../util/empty'); var isBrowser = require('../browser/is-browser'); var trigger = require('../browser/trigger'); +var ie = require('../browser/ie'); +var on = require('../browser/on'); var NodeType = require('./node-type'); var elementGetTransition = require('./element-get-transition'); var getEventListener = require('./get-event-listener'); @@ -133,6 +135,17 @@ function xPropOutput(element, bindInfo, data) { on(element.el, name, listener, capture); } +// #[begin] allua +function ie9InputEventCompatible(doc) { + on(doc, 'selectionchange', function () { + var el = doc.activeElement; + if (el.tagName === 'TEXTAREA' || el.tagName === 'INPUT') { + trigger(el, 'input'); + } + }); +} +// #[end] + /** * 完成元素 attached 后的行为 * @@ -158,6 +171,16 @@ function elementOwnAttached() { switch (this.tagName) { case 'input': case 'textarea': + // #[begin] allua + if (ie === 9) { + var doc = this.el.ownerDocument; + if (!doc.__sanInputEventCompatible) { + doc.__sanInputEventCompatible = true; + ie9InputEventCompatible(doc); + } + } + // #[end] + if (isBrowser) { elementOnEl(this, 'change', inputOnCompositionEnd); elementOnEl(this, 'compositionstart', inputOnCompositionStart); From e438445cf5cede1abba5a27dbc0fdf90f215cfbd Mon Sep 17 00:00:00 2001 From: errorrik Date: Sat, 30 Sep 2023 13:57:58 +0800 Subject: [PATCH 03/19] remove unreasonable spec --- test/component.spec.js | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/test/component.spec.js b/test/component.spec.js index 52d61da76..775a9a6e8 100644 --- a/test/component.spec.js +++ b/test/component.spec.js @@ -534,20 +534,6 @@ describe("Component", function () { }) }); - it("attach without parentEl, and dispose, got collect life cycle", function () { - var MyComponent = san.defineComponent({ - template: '
hello san
' - }); - - var myComponent = new MyComponent(); - myComponent.attach(); - - expect(!!myComponent.lifeCycle.is('attached')).toBeTruthy(); - - myComponent.dispose(); - expect(!!myComponent.lifeCycle.is('disposed')).toBeTruthy(); - - }); it("data set in inited should not update view", function (done) { var up = false; From e425cc5688049b6ab2ada688459567961e74bbf8 Mon Sep 17 00:00:00 2001 From: errorrik Date: Sat, 30 Sep 2023 13:58:38 +0800 Subject: [PATCH 04/19] preheat just for same task --- src/view/preheat-el.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/view/preheat-el.js b/src/view/preheat-el.js index 74953a98e..350d60037 100644 --- a/src/view/preheat-el.js +++ b/src/view/preheat-el.js @@ -8,6 +8,7 @@ */ var svgTags = require('../browser/svg-tags'); +const nextTick = require('../util/next-tick'); /** * ANode预热HTML元素,用于循环创建时clone @@ -28,6 +29,11 @@ function preheatEl(aNode, doc) { } } + nextTick(function () { + aNode._el = null; + aNode._i = 0; + }); + return el; } From 6e0c35830f4cb003e19f9ffda22abf402e3a3f48 Mon Sep 17 00:00:00 2001 From: errorrik Date: Sat, 30 Sep 2023 16:14:44 +0800 Subject: [PATCH 05/19] fix modern dist bug --- src/view/text-node.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/view/text-node.js b/src/view/text-node.js index 8bdd906b9..b93f21fa1 100644 --- a/src/view/text-node.js +++ b/src/view/text-node.js @@ -132,7 +132,13 @@ TextNode.prototype.dispose = function (noDetach) { this.sel = null; }; -var textUpdateProp = ie && ie < 9 ? 'data' : 'textContent'; + +var textUpdateProp = +// #[begin] allua + ie && ie < 9 ? 'data' : + // #[end] + 'textContent'; + /** * 更新 text 节点的视图 From 5e4647017a707ce53cdbc4011eee113c6cc86b0c Mon Sep 17 00:00:00 2001 From: errorrik Date: Sat, 30 Sep 2023 16:26:30 +0800 Subject: [PATCH 06/19] opti textnode update prop set --- src/view/text-node.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/view/text-node.js b/src/view/text-node.js index b93f21fa1..8e7453f3f 100644 --- a/src/view/text-node.js +++ b/src/view/text-node.js @@ -133,11 +133,10 @@ TextNode.prototype.dispose = function (noDetach) { }; -var textUpdateProp = // #[begin] allua - ie && ie < 9 ? 'data' : - // #[end] - 'textContent'; +var textUpdateProp = ie && ie < 9 ? 'data' : 'textContent'; +// #[end] + /** @@ -181,7 +180,12 @@ TextNode.prototype._update = function (changes) { parentEl.removeChild(tempFlag); } else { + // #[begin] allua this.el[textUpdateProp] = text; + // #[end] + // #[begin] modern + this.el.textContent = text; + // #[end] } } From 235ef0760d6b8868e72c6b595563db81b841edea Mon Sep 17 00:00:00 2001 From: errorrik Date: Sat, 2 Dec 2023 10:26:15 +0800 Subject: [PATCH 07/19] add #772 specs --- test/component-attr-inherit.spec.js | 279 ++++++++++++++++++++++++++++ test/index-min.html | 1 + test/index-modern.html | 1 + test/index.html | 1 + 4 files changed, 282 insertions(+) create mode 100644 test/component-attr-inherit.spec.js diff --git a/test/component-attr-inherit.spec.js b/test/component-attr-inherit.spec.js new file mode 100644 index 000000000..c1f93d5ce --- /dev/null +++ b/test/component-attr-inherit.spec.js @@ -0,0 +1,279 @@ +describe("Component Attribute Inherit", function () { + + it("1 level", function (done) { + var Inner = san.defineComponent({ + template: '' + + }); + + var MyComponent = san.defineComponent({ + template: '
{{text}}
', + + components: { + 'ui-inner': Inner + } + }); + + var myComponent = new MyComponent({ + data: { + text: 'Hahaha' + } + }); + + var wrap = document.createElement('div'); + document.body.appendChild(wrap); + myComponent.attach(wrap); + + var span = wrap.getElementsByTagName('span')[0]; + expect(span.innerHTML).toContain('Hahaha'); + expect(span.getAttribute('title')).toBe('Hahaha'); + expect(span.getAttribute('data-title')).toBe('state:Hahaha'); + + myComponent.data.set('text', 'Wuwuwu'); + + myComponent.nextTick(function () { + expect(span.innerHTML).toContain('Wuwuwu'); + expect(span.getAttribute('title')).toBe('Wuwuwu'); + expect(span.getAttribute('data-title')).toBe('state:Wuwuwu'); + + myComponent.dispose(); + document.body.removeChild(wrap); + done(); + }); + }); + + it("multi level", function (done) { + var DeepInner = san.defineComponent({ + template: '' + }); + + var Inner = san.defineComponent({ + template: '', + + components: { + 'ui-inner': DeepInner + } + }); + + var MyComponent = san.defineComponent({ + template: '
{{text}}
', + + components: { + 'ui-inner': Inner + } + }); + + var myComponent = new MyComponent({ + data: { + text: 'Hahaha' + } + }); + + var wrap = document.createElement('div'); + document.body.appendChild(wrap); + myComponent.attach(wrap); + + var span = wrap.getElementsByTagName('span')[0]; + expect(span.innerHTML).toContain('Hahaha'); + expect(span.getAttribute('title')).toBe('Hahaha'); + expect(span.getAttribute('data-title')).toBe('state:Hahaha'); + + myComponent.data.set('text', 'Wuwuwu'); + + myComponent.nextTick(function () { + expect(span.innerHTML).toContain('Wuwuwu'); + expect(span.getAttribute('title')).toBe('Wuwuwu'); + expect(span.getAttribute('data-title')).toBe('state:Wuwuwu'); + + myComponent.dispose(); + document.body.removeChild(wrap); + done(); + }); + }); + + it("inheritAttrs option to disable attr inherit, include style/class/id", function (done) { + var Inner = san.defineComponent({ + template: '', + inheritAttrs: false + }); + + var MyComponent = san.defineComponent({ + template: '
', + + components: { + 'ui-inner': Inner + } + }); + + var myComponent = new MyComponent({ + data: { + text: 'Hahaha' + } + }); + + var wrap = document.createElement('div'); + document.body.appendChild(wrap); + myComponent.attach(wrap); + + var span = wrap.getElementsByTagName('span')[0]; + expect(span.innerHTML).toContain('Hahaha'); + expect(span.hasAttribute('title')).toBeFalsy(); + expect(span.hasAttribute('data-title')).toBeFalsy(); + expect(span.className).not.toContain('a'); + expect(span.id).not.toContain('happy'); + expect(span.style.display).not.toContain('none'); + + + myComponent.data.set('text', 'Wuwuwu'); + + myComponent.nextTick(function () { + expect(span.innerHTML).toContain('Wuwuwu'); + expect(span.hasAttribute('title')).toBeFalsy(); + expect(span.hasAttribute('data-title')).toBeFalsy(); + expect(span.className).not.toContain('a'); + expect(span.id).not.toContain('happy'); + expect(span.style.display).not.toContain('none'); + + myComponent.dispose(); + document.body.removeChild(wrap); + done(); + }); + }); + + it("component template declaration take precedence", function (done) { + var Inner = san.defineComponent({ + template: '' + }); + + var MyComponent = san.defineComponent({ + template: '
{{text}}
', + + components: { + 'ui-inner': Inner + } + }); + + var myComponent = new MyComponent({ + data: { + text: 'Hahaha' + } + }); + + var wrap = document.createElement('div'); + document.body.appendChild(wrap); + myComponent.attach(wrap); + + var span = wrap.getElementsByTagName('span')[0]; + expect(span.innerHTML).toContain('Hahaha'); + expect(span.getAttribute('title')).toBe('nothing'); + expect(span.getAttribute('data-title')).toBe('state:Hahaha'); + + myComponent.data.set('text', 'Wuwuwu'); + + myComponent.nextTick(function () { + expect(span.innerHTML).toContain('Wuwuwu'); + expect(span.getAttribute('title')).toBe('nothing'); + expect(span.getAttribute('data-title')).toBe('state:Wuwuwu'); + + myComponent.dispose(); + document.body.removeChild(wrap); + done(); + }); + }); + + it("inner component has $attrs data, not has attrXxx data", function (done) { + var Inner = san.defineComponent({ + template: '

' + }); + + var MyComponent = san.defineComponent({ + template: '
{{text}}
', + + components: { + 'ui-inner': Inner + } + }); + + var myComponent = new MyComponent({ + data: { + text: 'Hahaha' + } + }); + + var wrap = document.createElement('div'); + document.body.appendChild(wrap); + myComponent.attach(wrap); + + var innComponent = myComponent.ref('inn'); + var innAttrs = innComponent.data.get('$attrs'); + expect(innAttrs.title).toBe('Hahaha'); + expect(innAttrs['data-title']).toBe('state:Hahaha'); + expect(innComponent.data.get('attrTitle')).toBeUndefined(); + + var span = wrap.getElementsByTagName('span')[0]; + expect(span.innerHTML).toContain('Hahaha'); + expect(span.getAttribute('title')).toBe('Hahaha'); + expect(span.getAttribute('data-title')).toBe('state:Hahaha'); + + myComponent.data.set('text', 'Wuwuwu'); + + myComponent.nextTick(function () { + expect(span.innerHTML).toContain('Wuwuwu'); + expect(span.getAttribute('title')).toBe('Wuwuwu'); + expect(span.getAttribute('data-title')).toBe('state:Wuwuwu'); + + + var innAttrs = innComponent.data.get('$attrs'); + expect(innAttrs.title).toBe('Wuwuwu'); + expect(innAttrs['data-title']).toBe('state:Wuwuwu'); + + expect(innComponent.data.get('attrTitle')).toBeUndefined(); + + myComponent.dispose(); + document.body.removeChild(wrap); + done(); + }); + }); + + it("spread inherit attrs to other element", function (done) { + var Inner = san.defineComponent({ + template: '

' + }); + + var MyComponent = san.defineComponent({ + template: '
{{text}}
', + + components: { + 'ui-inner': Inner + } + }); + + var myComponent = new MyComponent({ + data: { + text: 'Hahaha' + } + }); + + var wrap = document.createElement('div'); + document.body.appendChild(wrap); + myComponent.attach(wrap); + + var span = wrap.getElementsByTagName('span')[0]; + expect(span.innerHTML).toContain('Hahaha'); + expect(span.getAttribute('title')).toBe('Hahaha'); + expect(span.getAttribute('data-title')).toBe('state:Hahaha'); + + myComponent.data.set('text', 'Wuwuwu'); + + myComponent.nextTick(function () { + expect(span.innerHTML).toContain('Wuwuwu'); + expect(span.getAttribute('title')).toBe('Wuwuwu'); + expect(span.getAttribute('data-title')).toBe('state:Wuwuwu'); + + myComponent.dispose(); + document.body.removeChild(wrap); + done(); + }); + }); + +}); diff --git a/test/index-min.html b/test/index-min.html index ca0018187..e9b1bab3e 100755 --- a/test/index-min.html +++ b/test/index-min.html @@ -33,6 +33,7 @@ + diff --git a/test/index-modern.html b/test/index-modern.html index 7aca7ccdd..64bb6797d 100755 --- a/test/index-modern.html +++ b/test/index-modern.html @@ -33,6 +33,7 @@ + diff --git a/test/index.html b/test/index.html index 278e655fd..e178e5da3 100755 --- a/test/index.html +++ b/test/index.html @@ -96,6 +96,7 @@ + From 5146bf77b336a47a6edb0106ae46cc2b43bea73d Mon Sep 17 00:00:00 2001 From: errorrik Date: Sat, 2 Dec 2023 17:39:39 +0800 Subject: [PATCH 08/19] fix and add #772 specs --- test/component-attr-inherit.spec.js | 80 ++++++++++++++++++++++++----- 1 file changed, 66 insertions(+), 14 deletions(-) diff --git a/test/component-attr-inherit.spec.js b/test/component-attr-inherit.spec.js index c1f93d5ce..889303565 100644 --- a/test/component-attr-inherit.spec.js +++ b/test/component-attr-inherit.spec.js @@ -27,14 +27,14 @@ describe("Component Attribute Inherit", function () { var span = wrap.getElementsByTagName('span')[0]; expect(span.innerHTML).toContain('Hahaha'); expect(span.getAttribute('title')).toBe('Hahaha'); - expect(span.getAttribute('data-title')).toBe('state:Hahaha'); + expect(span.getAttribute('data-t')).toBe('state:Hahaha'); myComponent.data.set('text', 'Wuwuwu'); myComponent.nextTick(function () { expect(span.innerHTML).toContain('Wuwuwu'); expect(span.getAttribute('title')).toBe('Wuwuwu'); - expect(span.getAttribute('data-title')).toBe('state:Wuwuwu'); + expect(span.getAttribute('data-t')).toBe('state:Wuwuwu'); myComponent.dispose(); document.body.removeChild(wrap); @@ -76,14 +76,14 @@ describe("Component Attribute Inherit", function () { var span = wrap.getElementsByTagName('span')[0]; expect(span.innerHTML).toContain('Hahaha'); expect(span.getAttribute('title')).toBe('Hahaha'); - expect(span.getAttribute('data-title')).toBe('state:Hahaha'); + expect(span.getAttribute('data-t')).toBe('state:Hahaha'); myComponent.data.set('text', 'Wuwuwu'); myComponent.nextTick(function () { expect(span.innerHTML).toContain('Wuwuwu'); expect(span.getAttribute('title')).toBe('Wuwuwu'); - expect(span.getAttribute('data-title')).toBe('state:Wuwuwu'); + expect(span.getAttribute('data-t')).toBe('state:Wuwuwu'); myComponent.dispose(); document.body.removeChild(wrap); @@ -97,6 +97,58 @@ describe("Component Attribute Inherit", function () { inheritAttrs: false }); + var MyComponent = san.defineComponent({ + template: '
', + + components: { + 'ui-inner': Inner + } + }); + + var myComponent = new MyComponent({ + data: { + text: 'Hahaha' + } + }); + + var wrap = document.createElement('div'); + document.body.appendChild(wrap); + myComponent.attach(wrap); + + var innerComponent = myComponent.ref('inner'); + expect(innerComponent.data.get('$attrs')).toBeUndefined(); + + var span = wrap.getElementsByTagName('span')[0]; + expect(span.innerHTML).toContain('Hahaha'); + expect(span.hasAttribute('title')).toBeFalsy(); + expect(span.hasAttribute('data-t')).toBeFalsy(); + expect(span.className).not.toContain('a'); + expect(span.id).not.toContain('happy'); + expect(span.style.display).not.toContain('none'); + + + myComponent.data.set('text', 'Wuwuwu'); + + myComponent.nextTick(function () { + expect(span.innerHTML).toContain('Wuwuwu'); + expect(span.hasAttribute('title')).toBeFalsy(); + expect(span.hasAttribute('data-t')).toBeFalsy(); + expect(span.className).not.toContain('a'); + expect(span.id).not.toContain('happy'); + expect(span.style.display).not.toContain('none'); + + myComponent.dispose(); + document.body.removeChild(wrap); + done(); + }); + }); + + it("inheritAttrs static prop to disable attr inherit, include style/class/id", function (done) { + var Inner = san.defineComponent({ + template: '' + }); + Inner.inheritAttrs = false; + var MyComponent = san.defineComponent({ template: '
', @@ -118,7 +170,7 @@ describe("Component Attribute Inherit", function () { var span = wrap.getElementsByTagName('span')[0]; expect(span.innerHTML).toContain('Hahaha'); expect(span.hasAttribute('title')).toBeFalsy(); - expect(span.hasAttribute('data-title')).toBeFalsy(); + expect(span.hasAttribute('data-t')).toBeFalsy(); expect(span.className).not.toContain('a'); expect(span.id).not.toContain('happy'); expect(span.style.display).not.toContain('none'); @@ -129,7 +181,7 @@ describe("Component Attribute Inherit", function () { myComponent.nextTick(function () { expect(span.innerHTML).toContain('Wuwuwu'); expect(span.hasAttribute('title')).toBeFalsy(); - expect(span.hasAttribute('data-title')).toBeFalsy(); + expect(span.hasAttribute('data-t')).toBeFalsy(); expect(span.className).not.toContain('a'); expect(span.id).not.toContain('happy'); expect(span.style.display).not.toContain('none'); @@ -166,14 +218,14 @@ describe("Component Attribute Inherit", function () { var span = wrap.getElementsByTagName('span')[0]; expect(span.innerHTML).toContain('Hahaha'); expect(span.getAttribute('title')).toBe('nothing'); - expect(span.getAttribute('data-title')).toBe('state:Hahaha'); + expect(span.getAttribute('data-t')).toBe('state:Hahaha'); myComponent.data.set('text', 'Wuwuwu'); myComponent.nextTick(function () { expect(span.innerHTML).toContain('Wuwuwu'); expect(span.getAttribute('title')).toBe('nothing'); - expect(span.getAttribute('data-title')).toBe('state:Wuwuwu'); + expect(span.getAttribute('data-t')).toBe('state:Wuwuwu'); myComponent.dispose(); document.body.removeChild(wrap); @@ -207,25 +259,25 @@ describe("Component Attribute Inherit", function () { var innComponent = myComponent.ref('inn'); var innAttrs = innComponent.data.get('$attrs'); expect(innAttrs.title).toBe('Hahaha'); - expect(innAttrs['data-title']).toBe('state:Hahaha'); + expect(innAttrs['data-t']).toBe('state:Hahaha'); expect(innComponent.data.get('attrTitle')).toBeUndefined(); var span = wrap.getElementsByTagName('span')[0]; expect(span.innerHTML).toContain('Hahaha'); expect(span.getAttribute('title')).toBe('Hahaha'); - expect(span.getAttribute('data-title')).toBe('state:Hahaha'); + expect(span.getAttribute('data-t')).toBe('state:Hahaha'); myComponent.data.set('text', 'Wuwuwu'); myComponent.nextTick(function () { expect(span.innerHTML).toContain('Wuwuwu'); expect(span.getAttribute('title')).toBe('Wuwuwu'); - expect(span.getAttribute('data-title')).toBe('state:Wuwuwu'); + expect(span.getAttribute('data-t')).toBe('state:Wuwuwu'); var innAttrs = innComponent.data.get('$attrs'); expect(innAttrs.title).toBe('Wuwuwu'); - expect(innAttrs['data-title']).toBe('state:Wuwuwu'); + expect(innAttrs['data-t']).toBe('state:Wuwuwu'); expect(innComponent.data.get('attrTitle')).toBeUndefined(); @@ -261,14 +313,14 @@ describe("Component Attribute Inherit", function () { var span = wrap.getElementsByTagName('span')[0]; expect(span.innerHTML).toContain('Hahaha'); expect(span.getAttribute('title')).toBe('Hahaha'); - expect(span.getAttribute('data-title')).toBe('state:Hahaha'); + expect(span.getAttribute('data-t')).toBe('state:Hahaha'); myComponent.data.set('text', 'Wuwuwu'); myComponent.nextTick(function () { expect(span.innerHTML).toContain('Wuwuwu'); expect(span.getAttribute('title')).toBe('Wuwuwu'); - expect(span.getAttribute('data-title')).toBe('state:Wuwuwu'); + expect(span.getAttribute('data-t')).toBe('state:Wuwuwu'); myComponent.dispose(); document.body.removeChild(wrap); From c34f2d89e338673c7032e5c3e4296c3395805453 Mon Sep 17 00:00:00 2001 From: errorrik Date: Mon, 4 Dec 2023 15:59:49 +0800 Subject: [PATCH 09/19] add #772 specs --- test/component-attr-inherit.spec.js | 56 ++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/test/component-attr-inherit.spec.js b/test/component-attr-inherit.spec.js index 889303565..d7a8e835c 100644 --- a/test/component-attr-inherit.spec.js +++ b/test/component-attr-inherit.spec.js @@ -42,13 +42,55 @@ describe("Component Attribute Inherit", function () { }); }); + it("bool value and bool attr", function (done) { + var Inner = san.defineComponent({ + template: '' + + }); + + var MyComponent = san.defineComponent({ + template: '
{{text}}
', + + components: { + 'ui-inner': Inner + } + }); + + var myComponent = new MyComponent({ + data: { + text: 'Hahaha', + ed: true + } + }); + + var wrap = document.createElement('div'); + document.body.appendChild(wrap); + myComponent.attach(wrap); + + var btn = wrap.getElementsByTagName('button')[0]; + expect(btn.getAttribute('title')).toBe('Hahaha'); + expect(btn.disabled).toBeTruthy(); + expect(btn.getAttribute('data-disabled')).toBe('true'); + + myComponent.data.set('ed', 'false'); + + myComponent.nextTick(function () { + expect(btn.disabled).toBeTruthy(); + expect(btn.getAttribute('data-disabled')).toBe('false'); + + myComponent.dispose(); + document.body.removeChild(wrap); + done(); + }); + }); + it("multi level", function (done) { var DeepInner = san.defineComponent({ template: '' }); var Inner = san.defineComponent({ - template: '', + template: '', components: { 'ui-inner': DeepInner @@ -56,7 +98,7 @@ describe("Component Attribute Inherit", function () { }); var MyComponent = san.defineComponent({ - template: '
{{text}}
', + template: '
{{text}}
', components: { 'ui-inner': Inner @@ -65,7 +107,8 @@ describe("Component Attribute Inherit", function () { var myComponent = new MyComponent({ data: { - text: 'Hahaha' + text: 'Hahaha', + clz: 'out-cls' } }); @@ -75,8 +118,10 @@ describe("Component Attribute Inherit", function () { var span = wrap.getElementsByTagName('span')[0]; expect(span.innerHTML).toContain('Hahaha'); + expect(span.className).toContain('out-cls'); expect(span.getAttribute('title')).toBe('Hahaha'); expect(span.getAttribute('data-t')).toBe('state:Hahaha'); + expect(span.getAttribute('data-c')).toBe('cover'); myComponent.data.set('text', 'Wuwuwu'); @@ -84,9 +129,10 @@ describe("Component Attribute Inherit", function () { expect(span.innerHTML).toContain('Wuwuwu'); expect(span.getAttribute('title')).toBe('Wuwuwu'); expect(span.getAttribute('data-t')).toBe('state:Wuwuwu'); + expect(span.getAttribute('data-c')).toBe('cover') - myComponent.dispose(); - document.body.removeChild(wrap); + // myComponent.dispose(); + // document.body.removeChild(wrap); done(); }); }); From 1678183214e03a90d11cdd69814c99defd15fed6 Mon Sep 17 00:00:00 2001 From: errorrik Date: Mon, 4 Dec 2023 16:00:02 +0800 Subject: [PATCH 10/19] #772 implement --- src/parser/integrate-attr.js | 16 +++-- src/view/component.js | 93 ++++++++++++++++++++++++++++ src/view/parse-component-template.js | 3 +- src/view/preheat-a-node.js | 7 +++ 4 files changed, 114 insertions(+), 5 deletions(-) diff --git a/src/parser/integrate-attr.js b/src/parser/integrate-attr.js index 9b147674b..e3c72578f 100644 --- a/src/parser/integrate-attr.js +++ b/src/parser/integrate-attr.js @@ -80,21 +80,29 @@ function integrateAttr(aNode, name, value, options) { aNode.vars = []; } - realName = kebab2camel(realName); aNode.vars.push({ - name: realName, + name: kebab2camel(realName), expr: parseExpr(value.replace(/(^\{\{|\}\}$)/g, '')) }); break; default: + var propsArray = aNode.props; if (prefix === 'prop') { name = realName; } + if (prefix === 'attr') { + name = realName; + if (!aNode.attrs) { + aNode.attrs = []; + } + propsArray = aNode.attrs; + } + // parse two way binding, e.g. value="{=ident=}" if (value && value.indexOf('{=') === 0 && value.slice(-2) === '=}') { - aNode.props.push({ + propsArray.push({ name: name, expr: parseExpr(value.slice(2, -2)), x: 1 @@ -170,7 +178,7 @@ function integrateAttr(aNode, name, value, options) { } - aNode.props.push( + propsArray.push( value != null ? {name: name, expr: expr} : {name: name, expr: expr, noValue: 1} diff --git a/src/view/component.js b/src/view/component.js index 7f4988ac3..8885154ac 100644 --- a/src/view/component.js +++ b/src/view/component.js @@ -84,6 +84,7 @@ function Component(options) { // eslint-disable-line var clazz = this.constructor; + this.inheritAttrs = !(this.inheritAttrs === false || clazz.inheritAttrs === false); this.filters = this.filters || clazz.filters || {}; this.computed = this.computed || clazz.computed || {}; this.messages = this.messages || clazz.messages || {}; @@ -228,6 +229,10 @@ function Component(options) { // eslint-disable-line this.tagName = this.tagName || this.source.tagName; this.binds = this.source._b; + if (this.inheritAttrs) { + this.attrs = this.source.attrs; + } + // init s-bind data this._srcSbindData = nodeSBindInit(this.source.directives.bind, this.scope, this.owner); } @@ -259,6 +264,19 @@ function Component(options) { // eslint-disable-line initData[bindInfo.name] = value; } } + + if (this.attrs) { + initData.$attrs = {}; + for (var i = 0, l = this.attrs.length; i < l; i++) { + var attrInfo = this.attrs[i]; + + var value = evalExpr(attrInfo.expr, this.scope, this.owner); + if (typeof value !== 'undefined') { + // See: https://github.com/ecomfe/san/issues/191 + initData.$attrs[attrInfo.name] = value; + } + } + } } this.data = new Data(initData); @@ -750,6 +768,16 @@ Component.prototype._update = function (changes) { } }); + each(me.attrs, function (attrItem) { + var updateExpr = attrItem.expr; + if (changeExprCompare(changeExpr, updateExpr, me.scope)) { + me.data.set( + '$attrs["' + attrItem.name + '"]', + evalExpr(updateExpr, me.scope, me.owner) + ); + } + }); + each(me.sourceSlotNameProps, function (bindItem) { needReloadForSlot = needReloadForSlot || changeExprCompare(changeExpr, bindItem.expr, me.scope); return !needReloadForSlot; @@ -833,6 +861,22 @@ Component.prototype._update = function (changes) { } } + if (this.attrs) { + var attrsData = this.data.get('$attrs'); + for (var i = 0; i < this.attrs.length; i++) { + var attr = this.attrs[i]; + if (this.aNode._pi[attr.name] == null) { + for (var j = 0; j < dataChanges.length; j++) { + var changePaths = dataChanges[j].expr.paths; + if (changePaths[0].value === '$attrs' && changePaths[1].value === attr.name) { + attr.handler(this.el, attrsData[attr.name], attr.name, this); + break; + } + } + } + } + } + for (var i = 0; i < this.children.length; i++) { this.children[i]._update(dataChanges); } @@ -1015,6 +1059,45 @@ Component.prototype.attach = function (parentEl, beforeEl) { // #[begin] devtool this._toPhase('beforeCreate'); // #[end] + + aNode = { + directives: aNode.directives, + props: aNode.props, + events: aNode.events, + children: aNode.children, + tagName: aNode.tagName, + attrs: aNode.attrs, + vars: aNode.vars, + _ht: aNode._ht, + _i: aNode._i, + _dp: aNode._dp, + _xp: aNode._xp, + _pi: aNode._pi, + _ai: aNode._ai, + _b: aNode._b, + _ce: aNode._ce + }; + + if (this.attrs) { + aNode.attrs = aNode.attrs || []; + for (var i = 0; i < this.attrs.length; i++) { + var attr = this.attrs[i]; + if (aNode._ai[attr.name] == null) { + aNode.attrs.push({ + name: attr.name, + expr: { + type: ExprType.ACCESSOR, + paths: [ + {type: ExprType.STRING, value: '$attrs'}, + {type: ExprType.STRING, value: attr.name} + ] + }, + handler: attr.handler + }); + } + } + } + debugger this._rootNode = this._rootNode || createNode(aNode, this, this.data, this); this._rootNode.attach(parentEl, beforeEl); this._rootNode._getElAsRootNode && (this.el = this._rootNode._getElAsRootNode()); @@ -1060,6 +1143,16 @@ Component.prototype.attach = function (parentEl, beforeEl) { prop.handler(this.el, value, prop.name, this); } } +debugger + if (this.attrs) { + var attrsData = this.data.get('$attrs'); + for (var i = 0; i < this.attrs.length; i++) { + var attr = this.attrs[i]; + if (this.aNode._pi[attr.name] == null) { + attr.handler(this.el, attrsData[attr.name], attr.name, this); + } + } + } this._toPhase('created'); } diff --git a/src/view/parse-component-template.js b/src/view/parse-component-template.js index b78c3dc0d..b1c2825d8 100644 --- a/src/view/parse-component-template.js +++ b/src/view/parse-component-template.js @@ -50,7 +50,8 @@ function parseComponentTemplate(ComponentClass) { delete aNode.tagName; } - if (proto.autoFillStyleAndId !== false && ComponentClass.autoFillStyleAndId !== false) { + if (proto.autoFillStyleAndId !== false && ComponentClass.autoFillStyleAndId !== false + && proto.inheritAttrs !== false && ComponentClass.inheritAttrs !== false) { fillStyleAndId(aNode.props); if (aNode.elses) { diff --git a/src/view/preheat-a-node.js b/src/view/preheat-a-node.js index c40f6df9b..ea1c6877b 100644 --- a/src/view/preheat-a-node.js +++ b/src/view/preheat-a-node.js @@ -66,6 +66,7 @@ function preheatANode(aNode, componentInstance) { aNode._dp = []; // hotspot: dynamic props aNode._xp = []; // hotspot: x props aNode._pi = {}; // hotspot: props index + aNode._ai = {}; // hotspot: attrs index aNode._b = []; // hotspot: binds aNode._ce = !aNode.directives.is // cache element && aNode.tagName && aNode.tagName.indexOf('-') < 0 @@ -77,6 +78,12 @@ function preheatANode(aNode, componentInstance) { recordHotspotData(varItem.expr); }); + each(aNode.attrs, function (attr, i) { + aNode._ai[attr.name] = i; + attr.handler = getPropHandler(aNode.tagName, attr.name); + recordHotspotData(attr.expr); + }); + var props = aNode.props; var propsLen = props.length; From 44faf6f323aa3817850c088ef036c2728d3576a0 Mon Sep 17 00:00:00 2001 From: errorrik Date: Mon, 4 Dec 2023 23:38:40 +0800 Subject: [PATCH 11/19] enhance attr inherit spec --- test/component-attr-inherit.spec.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/component-attr-inherit.spec.js b/test/component-attr-inherit.spec.js index d7a8e835c..439c4eb04 100644 --- a/test/component-attr-inherit.spec.js +++ b/test/component-attr-inherit.spec.js @@ -49,7 +49,7 @@ describe("Component Attribute Inherit", function () { }); var MyComponent = san.defineComponent({ - template: '
{{text}}
', + template: '
{{text}}
', components: { 'ui-inner': Inner @@ -69,6 +69,7 @@ describe("Component Attribute Inherit", function () { var btn = wrap.getElementsByTagName('button')[0]; expect(btn.getAttribute('title')).toBe('Hahaha'); + expect(btn.getAttribute('data-d')).toBe(''); expect(btn.disabled).toBeTruthy(); expect(btn.getAttribute('data-disabled')).toBe('true'); @@ -131,8 +132,8 @@ describe("Component Attribute Inherit", function () { expect(span.getAttribute('data-t')).toBe('state:Wuwuwu'); expect(span.getAttribute('data-c')).toBe('cover') - // myComponent.dispose(); - // document.body.removeChild(wrap); + myComponent.dispose(); + document.body.removeChild(wrap); done(); }); }); From f51c3afa307065bc15f2267e7a2c6e1346ffd4b3 Mon Sep 17 00:00:00 2001 From: errorrik Date: Mon, 4 Dec 2023 23:39:46 +0800 Subject: [PATCH 12/19] opti attr read --- src/parser/integrate-attr.js | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/parser/integrate-attr.js b/src/parser/integrate-attr.js index e3c72578f..afd43fb88 100644 --- a/src/parser/integrate-attr.js +++ b/src/parser/integrate-attr.js @@ -86,23 +86,30 @@ function integrateAttr(aNode, name, value, options) { }); break; - default: - var propsArray = aNode.props; - if (prefix === 'prop') { - name = realName; + case 'attr': + if (!aNode.attrs) { + aNode.attrs = []; } - if (prefix === 'attr') { + aNode.attrs.push({ + name: realName, + expr: value + ? parseText(value, options.delimiters) + : boolAttrs[realName] + ? {type: ExprType.BOOL, value: true} + : {type: ExprType.STRING, value: ''} + }); + + break; + + default: + if (prefix === 'prop') { name = realName; - if (!aNode.attrs) { - aNode.attrs = []; - } - propsArray = aNode.attrs; } // parse two way binding, e.g. value="{=ident=}" if (value && value.indexOf('{=') === 0 && value.slice(-2) === '=}') { - propsArray.push({ + aNode.props.push({ name: name, expr: parseExpr(value.slice(2, -2)), x: 1 @@ -175,10 +182,9 @@ function integrateAttr(aNode, name, value, options) { } } } - } - propsArray.push( + aNode.props.push( value != null ? {name: name, expr: expr} : {name: name, expr: expr, noValue: 1} From 625f8ebde0620d4e55ca14f758b5330592c2178a Mon Sep 17 00:00:00 2001 From: errorrik Date: Mon, 4 Dec 2023 23:41:33 +0800 Subject: [PATCH 13/19] fix: when rootNode is not a component, something error --- src/view/component.js | 76 ++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/src/view/component.js b/src/view/component.js index 8885154ac..2f7058d7d 100644 --- a/src/view/component.js +++ b/src/view/component.js @@ -1060,44 +1060,46 @@ Component.prototype.attach = function (parentEl, beforeEl) { this._toPhase('beforeCreate'); // #[end] - aNode = { - directives: aNode.directives, - props: aNode.props, - events: aNode.events, - children: aNode.children, - tagName: aNode.tagName, - attrs: aNode.attrs, - vars: aNode.vars, - _ht: aNode._ht, - _i: aNode._i, - _dp: aNode._dp, - _xp: aNode._xp, - _pi: aNode._pi, - _ai: aNode._ai, - _b: aNode._b, - _ce: aNode._ce - }; + if (this.components[aNode.tagName]) { + aNode = { + directives: aNode.directives, + props: aNode.props, + events: aNode.events, + children: aNode.children, + tagName: aNode.tagName, + attrs: aNode.attrs, + vars: aNode.vars, + _ht: aNode._ht, + _i: aNode._i, + _dp: aNode._dp, + _xp: aNode._xp, + _pi: aNode._pi, + _ai: aNode._ai, + _b: aNode._b, + _ce: aNode._ce + }; - if (this.attrs) { - aNode.attrs = aNode.attrs || []; - for (var i = 0; i < this.attrs.length; i++) { - var attr = this.attrs[i]; - if (aNode._ai[attr.name] == null) { - aNode.attrs.push({ - name: attr.name, - expr: { - type: ExprType.ACCESSOR, - paths: [ - {type: ExprType.STRING, value: '$attrs'}, - {type: ExprType.STRING, value: attr.name} - ] - }, - handler: attr.handler - }); + if (this.attrs) { + aNode.attrs = aNode.attrs || []; + for (var i = 0; i < this.attrs.length; i++) { + var attr = this.attrs[i]; + if (aNode._ai[attr.name] == null) { + aNode.attrs.push({ + name: attr.name, + expr: { + type: ExprType.ACCESSOR, + paths: [ + {type: ExprType.STRING, value: '$attrs'}, + {type: ExprType.STRING, value: attr.name} + ] + }, + handler: attr.handler + }); + } } - } - } - debugger + } + } + this._rootNode = this._rootNode || createNode(aNode, this, this.data, this); this._rootNode.attach(parentEl, beforeEl); this._rootNode._getElAsRootNode && (this.el = this._rootNode._getElAsRootNode()); @@ -1143,7 +1145,7 @@ Component.prototype.attach = function (parentEl, beforeEl) { prop.handler(this.el, value, prop.name, this); } } -debugger + if (this.attrs) { var attrsData = this.data.get('$attrs'); for (var i = 0; i < this.attrs.length; i++) { From a16ea1cfa5154d83d5f345f1e3007cf4d12dcd05 Mon Sep 17 00:00:00 2001 From: errorrik Date: Tue, 5 Dec 2023 09:47:44 +0800 Subject: [PATCH 14/19] attr --- doc/anode-pack.md | 48 +++++++++++++++++++++++++++++++++++++- src/parser/unpack-anode.js | 7 ++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/doc/anode-pack.md b/doc/anode-pack.md index b907ff4e2..d44b7884c 100644 --- a/doc/anode-pack.md +++ b/doc/anode-pack.md @@ -1170,4 +1170,50 @@ aPack = [1,"u",1,45,6,1,3,"cmpt"] "tagName": "u" } */ -``` \ No newline at end of file +``` + + +### var + +- head: 46 +- 编码序: `{string}name, {Node}expr` + +```js +aPack = [1,"my-ui",1,46,"title",9,,2,3,"Hello ",7,,6,1,3,"name",] +/* +{ + "directives": {}, + "props": [], + "events": [], + "children": [], + "tagName": "my-ui", + "attrs": [ + { + "name": "title", + "expr": { + "type": 7, + "segs": [ + { + "type": 1, + "value": "Hello " + }, + { + "type": 5, + "expr": { + "type": 4, + "paths": [ + { + "type": 1, + "value": "name" + } + ] + }, + "filters": [] + } + ] + } + } + ] +} +*/ +``` diff --git a/src/parser/unpack-anode.js b/src/parser/unpack-anode.js index 463477656..22a4861bd 100644 --- a/src/parser/unpack-anode.js +++ b/src/parser/unpack-anode.js @@ -293,6 +293,11 @@ function unpackANode(packed) { current.vars.push(node); break; + case 46: + current.attrs = current.attrs || []; + current.attrs.push(node); + break; + case 37: current.directives['for'] = node; break; @@ -390,12 +395,14 @@ function unpackANode(packed) { // Expr: UNARY // Prop // var + // attr // Object Spread Item, Array Item case 11: case 2: case 33: case 34: case 36: + case 46: case 15: case 17: case 18: From 87527917c66af363916bccb27f12fe9eaad87539 Mon Sep 17 00:00:00 2001 From: errorrik Date: Tue, 5 Dec 2023 10:40:47 +0800 Subject: [PATCH 15/19] fix attr unpack --- src/parser/unpack-anode.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/parser/unpack-anode.js b/src/parser/unpack-anode.js index 22a4861bd..fb1bad447 100644 --- a/src/parser/unpack-anode.js +++ b/src/parser/unpack-anode.js @@ -211,6 +211,7 @@ function unpackANode(packed) { break; case 36: + case 46: node = { name: packed[++i] }; From 87397bb5023002fae603d8d7ce4a8344b233d474 Mon Sep 17 00:00:00 2001 From: errorrik Date: Tue, 5 Dec 2023 12:23:53 +0800 Subject: [PATCH 16/19] fix mutil level attrs --- src/view/component.js | 102 ++++++++++++++++++++++++++---------------- 1 file changed, 63 insertions(+), 39 deletions(-) diff --git a/src/view/component.js b/src/view/component.js index 2f7058d7d..7377f79cf 100644 --- a/src/view/component.js +++ b/src/view/component.js @@ -325,6 +325,9 @@ function Component(options) { // eslint-disable-line } else { if (aNode.Clazz || this.components[aNode.tagName]) { + if (!aNode.Clazz && this.attrs) { + aNode = aNodeAttrsTransform(aNode, this.attrs); + } this._rootNode = createHydrateNode(aNode, this, this.data, this, hydrateWalker); this._rootNode._getElAsRootNode && (this.el = this._rootNode._getElAsRootNode()); } @@ -345,6 +348,9 @@ function Component(options) { // eslint-disable-line } else if (this.el) { if (aNode.Clazz || this.components[aNode.tagName]) { + if (!aNode.Clazz && this.attrs) { + aNode = aNodeAttrsTransform(aNode, this.attrs); + } hydrateWalker = new DOMChildrenWalker(this.el.parentNode, this.el); this._rootNode = createHydrateNode(aNode, this, this.data, this, hydrateWalker); this._rootNode._getElAsRootNode && (this.el = this._rootNode._getElAsRootNode()); @@ -1041,6 +1047,58 @@ Component.prototype._getElAsRootNode = function () { return this.el; }; +function aNodeAttrsTransform(aNode, attrs) { + var aNodeAttrs = aNode.attrs; + var aNodeAttrsIndex = aNode._ai; + var startIndex = 0; + + aNode = { + directives: aNode.directives, + props: aNode.props, + events: aNode.events, + children: aNode.children, + tagName: aNode.tagName, + attrs: aNodeAttrs ? aNodeAttrs.slice(0) : [], + vars: aNode.vars, + _ht: aNode._ht, + _i: aNode._i, + _dp: aNode._dp, + _xp: aNode._xp, + _pi: aNode._pi, + _ai: {}, + _b: aNode._b, + _ce: aNode._ce + }; + + if (aNodeAttrs) { + startIndex = aNodeAttrs.length; + for (var i = 0; i < startIndex; i++) { + aNode._ai[aNodeAttrs[i].name] = i; + } + } + + for (var i = 0; i < attrs.length; i++) { + var attr = attrs[i]; + + if (aNodeAttrsIndex[attr.name] == null) { + aNode._ai[attr.name] = startIndex++; + aNode.attrs.push({ + name: attr.name, + expr: { + type: ExprType.ACCESSOR, + paths: [ + {type: ExprType.STRING, value: '$attrs'}, + {type: ExprType.STRING, value: attr.name} + ] + }, + handler: attr.handler + }); + } + } + + return aNode; +} + /** * 将组件attach到页面 * @@ -1060,46 +1118,12 @@ Component.prototype.attach = function (parentEl, beforeEl) { this._toPhase('beforeCreate'); // #[end] - if (this.components[aNode.tagName]) { - aNode = { - directives: aNode.directives, - props: aNode.props, - events: aNode.events, - children: aNode.children, - tagName: aNode.tagName, - attrs: aNode.attrs, - vars: aNode.vars, - _ht: aNode._ht, - _i: aNode._i, - _dp: aNode._dp, - _xp: aNode._xp, - _pi: aNode._pi, - _ai: aNode._ai, - _b: aNode._b, - _ce: aNode._ce - }; - - if (this.attrs) { - aNode.attrs = aNode.attrs || []; - for (var i = 0; i < this.attrs.length; i++) { - var attr = this.attrs[i]; - if (aNode._ai[attr.name] == null) { - aNode.attrs.push({ - name: attr.name, - expr: { - type: ExprType.ACCESSOR, - paths: [ - {type: ExprType.STRING, value: '$attrs'}, - {type: ExprType.STRING, value: attr.name} - ] - }, - handler: attr.handler - }); - } - } - } + // aNode.Clazz 在 preheat 阶段为 if/else/for/fragment 等特殊标签或指令预热生成 + // 这里不能用 this.components[aNode.tagName] 判断,因为可能特殊指令和组件在同一个节点上并存 + if (!aNode.Clazz && this.attrs) { + aNode = aNodeAttrsTransform(aNode, this.attrs); } - + this._rootNode = this._rootNode || createNode(aNode, this, this.data, this); this._rootNode.attach(parentEl, beforeEl); this._rootNode._getElAsRootNode && (this.el = this._rootNode._getElAsRootNode()); From ca279810f32a01a8bf4c42108de582f3c2d48136 Mon Sep 17 00:00:00 2001 From: errorrik Date: Tue, 5 Dec 2023 21:11:16 +0800 Subject: [PATCH 17/19] fix $attrs when inheritAttrs option is false --- src/view/component.js | 15 ++++++--------- test/component-attr-inherit.spec.js | 25 ++++++++++++++++++------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/view/component.js b/src/view/component.js index 7377f79cf..abf8a55db 100644 --- a/src/view/component.js +++ b/src/view/component.js @@ -228,10 +228,7 @@ function Component(options) { // eslint-disable-line this.tagName = this.tagName || this.source.tagName; this.binds = this.source._b; - - if (this.inheritAttrs) { - this.attrs = this.source.attrs; - } + this.attrs = this.source.attrs; // init s-bind data this._srcSbindData = nodeSBindInit(this.source.directives.bind, this.scope, this.owner); @@ -325,7 +322,7 @@ function Component(options) { // eslint-disable-line } else { if (aNode.Clazz || this.components[aNode.tagName]) { - if (!aNode.Clazz && this.attrs) { + if (!aNode.Clazz && this.attrs && this.inheritAttrs) { aNode = aNodeAttrsTransform(aNode, this.attrs); } this._rootNode = createHydrateNode(aNode, this, this.data, this, hydrateWalker); @@ -348,7 +345,7 @@ function Component(options) { // eslint-disable-line } else if (this.el) { if (aNode.Clazz || this.components[aNode.tagName]) { - if (!aNode.Clazz && this.attrs) { + if (!aNode.Clazz && this.attrs && this.inheritAttrs) { aNode = aNodeAttrsTransform(aNode, this.attrs); } hydrateWalker = new DOMChildrenWalker(this.el.parentNode, this.el); @@ -867,7 +864,7 @@ Component.prototype._update = function (changes) { } } - if (this.attrs) { + if (this.attrs && this.inheritAttrs) { var attrsData = this.data.get('$attrs'); for (var i = 0; i < this.attrs.length; i++) { var attr = this.attrs[i]; @@ -1120,7 +1117,7 @@ Component.prototype.attach = function (parentEl, beforeEl) { // aNode.Clazz 在 preheat 阶段为 if/else/for/fragment 等特殊标签或指令预热生成 // 这里不能用 this.components[aNode.tagName] 判断,因为可能特殊指令和组件在同一个节点上并存 - if (!aNode.Clazz && this.attrs) { + if (!aNode.Clazz && this.attrs && this.inheritAttrs) { aNode = aNodeAttrsTransform(aNode, this.attrs); } @@ -1170,7 +1167,7 @@ Component.prototype.attach = function (parentEl, beforeEl) { } } - if (this.attrs) { + if (this.attrs && this.inheritAttrs) { var attrsData = this.data.get('$attrs'); for (var i = 0; i < this.attrs.length; i++) { var attr = this.attrs[i]; diff --git a/test/component-attr-inherit.spec.js b/test/component-attr-inherit.spec.js index 439c4eb04..f2899ed4e 100644 --- a/test/component-attr-inherit.spec.js +++ b/test/component-attr-inherit.spec.js @@ -163,7 +163,8 @@ describe("Component Attribute Inherit", function () { myComponent.attach(wrap); var innerComponent = myComponent.ref('inner'); - expect(innerComponent.data.get('$attrs')).toBeUndefined(); + expect(innerComponent.data.get('$attrs').title).toContain('Hahaha'); + expect(innerComponent.data.get('$attrs')['data-t']).toContain('state:Hahaha'); var span = wrap.getElementsByTagName('span')[0]; expect(span.innerHTML).toContain('Hahaha'); @@ -282,7 +283,7 @@ describe("Component Attribute Inherit", function () { it("inner component has $attrs data, not has attrXxx data", function (done) { var Inner = san.defineComponent({ - template: '

' + template: '

' }); var MyComponent = san.defineComponent({ @@ -309,17 +310,22 @@ describe("Component Attribute Inherit", function () { expect(innAttrs['data-t']).toBe('state:Hahaha'); expect(innComponent.data.get('attrTitle')).toBeUndefined(); + + var p = wrap.getElementsByTagName('p')[0]; + expect(p.getAttribute('title')).toBe('Hahaha'); + expect(p.getAttribute('data-t')).toBe('state:Hahaha'); + var span = wrap.getElementsByTagName('span')[0]; expect(span.innerHTML).toContain('Hahaha'); - expect(span.getAttribute('title')).toBe('Hahaha'); - expect(span.getAttribute('data-t')).toBe('state:Hahaha'); + expect(span.hasAttribute('title')).toBeFalsy(); + expect(span.hasAttribute('data-t')).toBeFalsy(); myComponent.data.set('text', 'Wuwuwu'); myComponent.nextTick(function () { expect(span.innerHTML).toContain('Wuwuwu'); - expect(span.getAttribute('title')).toBe('Wuwuwu'); - expect(span.getAttribute('data-t')).toBe('state:Wuwuwu'); + expect(p.getAttribute('title')).toBe('Wuwuwu'); + expect(p.getAttribute('data-t')).toBe('state:Wuwuwu'); var innAttrs = innComponent.data.get('$attrs'); @@ -336,7 +342,8 @@ describe("Component Attribute Inherit", function () { it("spread inherit attrs to other element", function (done) { var Inner = san.defineComponent({ - template: '

' + template: '

', + inheritAttrs: false }); var MyComponent = san.defineComponent({ @@ -362,6 +369,10 @@ describe("Component Attribute Inherit", function () { expect(span.getAttribute('title')).toBe('Hahaha'); expect(span.getAttribute('data-t')).toBe('state:Hahaha'); + var p = wrap.getElementsByTagName('p')[0]; + expect(p.hasAttribute('title')).toBeFalsy(); + expect(p.hasAttribute('data-t')).toBeFalsy(); + myComponent.data.set('text', 'Wuwuwu'); myComponent.nextTick(function () { From 1a6278cb7c430f800acd021c4b0cb1a5f03616f0 Mon Sep 17 00:00:00 2001 From: errorrik Date: Wed, 6 Dec 2023 23:46:45 +0800 Subject: [PATCH 18/19] opti attr inherit implement --- src/view/component.js | 81 +++++++++++++++++++------------------- src/view/preheat-a-node.js | 12 ++++-- 2 files changed, 50 insertions(+), 43 deletions(-) diff --git a/src/view/component.js b/src/view/component.js index abf8a55db..791869934 100644 --- a/src/view/component.js +++ b/src/view/component.js @@ -265,12 +265,12 @@ function Component(options) { // eslint-disable-line if (this.attrs) { initData.$attrs = {}; for (var i = 0, l = this.attrs.length; i < l; i++) { - var attrInfo = this.attrs[i]; + var attr = this.attrs[i]; - var value = evalExpr(attrInfo.expr, this.scope, this.owner); + var value = evalExpr(attr.expr, this.scope, this.owner); if (typeof value !== 'undefined') { // See: https://github.com/ecomfe/san/issues/191 - initData.$attrs[attrInfo.name] = value; + initData.$attrs[attr.name] = value; } } } @@ -323,7 +323,7 @@ function Component(options) { // eslint-disable-line else { if (aNode.Clazz || this.components[aNode.tagName]) { if (!aNode.Clazz && this.attrs && this.inheritAttrs) { - aNode = aNodeAttrsTransform(aNode, this.attrs); + aNode = aNodeMergeSourceAttrs(aNode, this.source); } this._rootNode = createHydrateNode(aNode, this, this.data, this, hydrateWalker); this._rootNode._getElAsRootNode && (this.el = this._rootNode._getElAsRootNode()); @@ -346,7 +346,7 @@ function Component(options) { // eslint-disable-line else if (this.el) { if (aNode.Clazz || this.components[aNode.tagName]) { if (!aNode.Clazz && this.attrs && this.inheritAttrs) { - aNode = aNodeAttrsTransform(aNode, this.attrs); + aNode = aNodeMergeSourceAttrs(aNode, this.source); } hydrateWalker = new DOMChildrenWalker(this.el.parentNode, this.el); this._rootNode = createHydrateNode(aNode, this, this.data, this, hydrateWalker); @@ -770,13 +770,12 @@ Component.prototype._update = function (changes) { } } }); - - each(me.attrs, function (attrItem) { - var updateExpr = attrItem.expr; - if (changeExprCompare(changeExpr, updateExpr, me.scope)) { +debugger + each(me.attrs, function (bindItem) { + if (changeExprCompare(changeExpr, bindItem.expr, me.scope)) { me.data.set( - '$attrs["' + attrItem.name + '"]', - evalExpr(updateExpr, me.scope, me.owner) + bindItem._data, + evalExpr(bindItem.expr, me.scope, me.owner) ); } }); @@ -866,13 +865,16 @@ Component.prototype._update = function (changes) { if (this.attrs && this.inheritAttrs) { var attrsData = this.data.get('$attrs'); + for (var i = 0; i < this.attrs.length; i++) { var attr = this.attrs[i]; + if (this.aNode._pi[attr.name] == null) { for (var j = 0; j < dataChanges.length; j++) { var changePaths = dataChanges[j].expr.paths; + if (changePaths[0].value === '$attrs' && changePaths[1].value === attr.name) { - attr.handler(this.el, attrsData[attr.name], attr.name, this); + getPropHandler(this.tagName, attr.name)(this.el, attrsData[attr.name], attr.name, this); break; } } @@ -957,7 +959,12 @@ Component.prototype._repaintChildren = function () { this._rootNode.dispose(0, 1); this.slotChildren = []; - this._rootNode = createNode(this.aNode, this, this.data, this); + var aNode = this.aNode; + if (!aNode.Clazz && this.attrs && this.inheritAttrs) { + aNode = aNodeMergeSourceAttrs(aNode, this.source); + } + + this._rootNode = createNode(aNode, this, this.data, this); this._rootNode.attach(parentEl, beforeEl); this._rootNode._getElAsRootNode && (this.el = this._rootNode._getElAsRootNode()); } @@ -1044,56 +1051,50 @@ Component.prototype._getElAsRootNode = function () { return this.el; }; -function aNodeAttrsTransform(aNode, attrs) { - var aNodeAttrs = aNode.attrs; - var aNodeAttrsIndex = aNode._ai; +function aNodeMergeSourceAttrs(aNode, source) { var startIndex = 0; - aNode = { + var mergedANode = { directives: aNode.directives, props: aNode.props, events: aNode.events, children: aNode.children, tagName: aNode.tagName, - attrs: aNodeAttrs ? aNodeAttrs.slice(0) : [], + attrs: [], vars: aNode.vars, _ht: aNode._ht, _i: aNode._i, _dp: aNode._dp, _xp: aNode._xp, _pi: aNode._pi, - _ai: {}, _b: aNode._b, _ce: aNode._ce }; - if (aNodeAttrs) { - startIndex = aNodeAttrs.length; + var aNodeAttrIndex = {}; + if (aNode.attrs) { + startIndex = aNode.attrs.length; for (var i = 0; i < startIndex; i++) { - aNode._ai[aNodeAttrs[i].name] = i; + var attr = aNode.attrs[i]; + aNodeAttrIndex[attr.name] = i; + mergedANode.attrs[i] = attr; } } - for (var i = 0; i < attrs.length; i++) { - var attr = attrs[i]; + for (var i = 0; i < source.attrs.length; i++) { + var attr = source.attrs[i]; - if (aNodeAttrsIndex[attr.name] == null) { - aNode._ai[attr.name] = startIndex++; - aNode.attrs.push({ + if (aNodeAttrIndex[attr.name] == null) { + mergedANode.attrs[startIndex] = { name: attr.name, - expr: { - type: ExprType.ACCESSOR, - paths: [ - {type: ExprType.STRING, value: '$attrs'}, - {type: ExprType.STRING, value: attr.name} - ] - }, - handler: attr.handler - }); + expr: attr._data, + _data: attr._data + }; + startIndex++; } } - return aNode; + return mergedANode; } /** @@ -1118,9 +1119,9 @@ Component.prototype.attach = function (parentEl, beforeEl) { // aNode.Clazz 在 preheat 阶段为 if/else/for/fragment 等特殊标签或指令预热生成 // 这里不能用 this.components[aNode.tagName] 判断,因为可能特殊指令和组件在同一个节点上并存 if (!aNode.Clazz && this.attrs && this.inheritAttrs) { - aNode = aNodeAttrsTransform(aNode, this.attrs); + aNode = aNodeMergeSourceAttrs(aNode, this.source); } - + this._rootNode = this._rootNode || createNode(aNode, this, this.data, this); this._rootNode.attach(parentEl, beforeEl); this._rootNode._getElAsRootNode && (this.el = this._rootNode._getElAsRootNode()); @@ -1172,7 +1173,7 @@ Component.prototype.attach = function (parentEl, beforeEl) { for (var i = 0; i < this.attrs.length; i++) { var attr = this.attrs[i]; if (this.aNode._pi[attr.name] == null) { - attr.handler(this.el, attrsData[attr.name], attr.name, this); + getPropHandler(this.tagName, attr.name)(this.el, attrsData[attr.name], attr.name, this); } } } diff --git a/src/view/preheat-a-node.js b/src/view/preheat-a-node.js index ea1c6877b..7c8033db6 100644 --- a/src/view/preheat-a-node.js +++ b/src/view/preheat-a-node.js @@ -66,8 +66,9 @@ function preheatANode(aNode, componentInstance) { aNode._dp = []; // hotspot: dynamic props aNode._xp = []; // hotspot: x props aNode._pi = {}; // hotspot: props index - aNode._ai = {}; // hotspot: attrs index aNode._b = []; // hotspot: binds + aNode._ab = []; // hotspot: attr binds + aNode._ap = []; // hotspot: attr props aNode._ce = !aNode.directives.is // cache element && aNode.tagName && aNode.tagName.indexOf('-') < 0 && !/^(template|select|input|option|button|video|audio|canvas|img|embed|object|iframe)$/i.test(aNode.tagName); @@ -79,8 +80,13 @@ function preheatANode(aNode, componentInstance) { }); each(aNode.attrs, function (attr, i) { - aNode._ai[attr.name] = i; - attr.handler = getPropHandler(aNode.tagName, attr.name); + attr._data = { + type: ExprType.ACCESSOR, + paths: [ + {type: ExprType.STRING, value: '$attrs'}, + {type: ExprType.STRING, value: attr.name} + ] + }; recordHotspotData(attr.expr); }); From f46d0a9940218347386bcb6cfc6fdfe5155d4071 Mon Sep 17 00:00:00 2001 From: errorrik Date: Thu, 7 Dec 2023 00:07:02 +0800 Subject: [PATCH 19/19] update cases dep --- package-lock.json | 32 ++++++++++++++++---------------- package.json | 4 ++-- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index dd721df6c..ff72a456d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "san", - "version": "3.13.1", + "version": "3.13.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "san", - "version": "3.13.1", + "version": "3.13.3", "license": "MIT", "devDependencies": { "@wdio/cli": "^6.6.6", @@ -29,8 +29,8 @@ "karma-sourcemap-loader": "^0.3.7", "mustache": "^3.0.1", "opener": "^1.5.1", - "san-anode-cases": "^3.10.1", - "san-html-cases": "^3.13.4", + "san-anode-cases": "^3.14.0", + "san-html-cases": "^3.14.0", "source-map": "^0.7.3", "swig-templates": "^2.0.3", "typescript": "^4.3.5", @@ -9774,15 +9774,15 @@ "dev": true }, "node_modules/san-anode-cases": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/san-anode-cases/-/san-anode-cases-3.10.1.tgz", - "integrity": "sha512-N3quamwUS2iZqxFOQWukElYBbrcB6y9dHn2TnBmrJ8aj1oDyltxqflXZtpT3B5Mdtqj9v4bwHMxnXvHj0MaTjA==", + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/san-anode-cases/-/san-anode-cases-3.14.0.tgz", + "integrity": "sha512-pwkbuTqtM7YzGF+67rMmTVrw1S2tYTr0D1fV9BkWABkTk0XkFx6kp8tBzxls3npyxOH0XWUeJ+9pmnWOeuLRNg==", "dev": true }, "node_modules/san-html-cases": { - "version": "3.13.4", - "resolved": "https://registry.npmjs.org/san-html-cases/-/san-html-cases-3.13.4.tgz", - "integrity": "sha512-ZYoRuaylnHu9zlajqp1cHvttfy/lHfr/cdfiG9IwcB3EOqTArCsrlB+8mYEVTuu17J+SpPFftHVO+WlGo/hiew==", + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/san-html-cases/-/san-html-cases-3.14.0.tgz", + "integrity": "sha512-+fV/ATLw5+891kh7vSmgfwUfaoc4Gpg6KqTImDMB8bS3D9a4Zt0yRGeLN6AmL5jbqaGVBa+8bkeWut1oRmvahw==", "dev": true }, "node_modules/saucelabs": { @@ -19617,15 +19617,15 @@ "dev": true }, "san-anode-cases": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/san-anode-cases/-/san-anode-cases-3.10.1.tgz", - "integrity": "sha512-N3quamwUS2iZqxFOQWukElYBbrcB6y9dHn2TnBmrJ8aj1oDyltxqflXZtpT3B5Mdtqj9v4bwHMxnXvHj0MaTjA==", + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/san-anode-cases/-/san-anode-cases-3.14.0.tgz", + "integrity": "sha512-pwkbuTqtM7YzGF+67rMmTVrw1S2tYTr0D1fV9BkWABkTk0XkFx6kp8tBzxls3npyxOH0XWUeJ+9pmnWOeuLRNg==", "dev": true }, "san-html-cases": { - "version": "3.13.4", - "resolved": "https://registry.npmjs.org/san-html-cases/-/san-html-cases-3.13.4.tgz", - "integrity": "sha512-ZYoRuaylnHu9zlajqp1cHvttfy/lHfr/cdfiG9IwcB3EOqTArCsrlB+8mYEVTuu17J+SpPFftHVO+WlGo/hiew==", + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/san-html-cases/-/san-html-cases-3.14.0.tgz", + "integrity": "sha512-+fV/ATLw5+891kh7vSmgfwUfaoc4Gpg6KqTImDMB8bS3D9a4Zt0yRGeLN6AmL5jbqaGVBa+8bkeWut1oRmvahw==", "dev": true }, "saucelabs": { diff --git a/package.json b/package.json index 9c5b1f9ee..65f3e4172 100644 --- a/package.json +++ b/package.json @@ -54,8 +54,8 @@ "karma-sourcemap-loader": "^0.3.7", "mustache": "^3.0.1", "opener": "^1.5.1", - "san-anode-cases": "^3.10.1", - "san-html-cases": "^3.13.4", + "san-anode-cases": "^3.14.0", + "san-html-cases": "^3.14.0", "source-map": "^0.7.3", "swig-templates": "^2.0.3", "typescript": "^4.3.5",