Skip to content

Commit 22e0df1

Browse files
committed
♻️ (blaze/attrs): optimize attribute updates by caching last values to avoid
redundant DOM updates and ensure handlers are cleaned up properly
1 parent dcf40e3 commit 22e0df1

File tree

5 files changed

+97
-95
lines changed

5 files changed

+97
-95
lines changed

packages/blaze/attrs.js

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -352,18 +352,18 @@ ElementAttributesUpdater.prototype.update = function(newAttrs) {
352352
const elem = this.elem;
353353
const handlers = this.handlers;
354354

355+
// Cache for last set values to avoid redundant DOM updates
356+
if (!this._lastValues) this._lastValues = {};
357+
const lastValues = this._lastValues;
358+
355359
Object.getOwnPropertyNames(handlers).forEach((k) => {
356360
if (!has(newAttrs, k)) {
357-
// remove attributes (and handlers) for attribute names
358-
// that don't exist as keys of `newAttrs` and so won't
359-
// be visited when traversing it. (Attributes that
360-
// exist in the `newAttrs` object but are `null`
361-
// are handled later.)
362361
const handler = handlers[k];
363362
const oldValue = handler.value;
364363
handler.value = null;
365364
handler.update(elem, oldValue, null);
366365
delete handlers[k];
366+
delete lastValues[k];
367367
}
368368
})
369369

@@ -373,19 +373,24 @@ ElementAttributesUpdater.prototype.update = function(newAttrs) {
373373
const value = newAttrs[k];
374374
if (!has(handlers, k)) {
375375
if (value !== null) {
376-
// make new handler
377376
handler = Blaze._makeAttributeHandler(elem, k, value);
378377
handlers[k] = handler;
379378
}
380379
} else {
381380
handler = handlers[k];
382381
oldValue = handler.value;
383382
}
384-
if (oldValue !== value) {
383+
// Only update if value has changed (shallow equality or string compare)
384+
const last = lastValues[k];
385+
const shouldUpdate = last !== value && String(last) !== String(value);
386+
if (shouldUpdate) {
385387
handler.value = value;
386388
handler.update(elem, oldValue, value);
387-
if (value === null)
389+
lastValues[k] = value;
390+
if (value === null) {
388391
delete handlers[k];
392+
delete lastValues[k];
393+
}
389394
}
390395
})
391396
};

test-app/.meteor/packages

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@
44
# 'meteor add' and 'meteor remove' will edit this file for you,
55
# but you can also edit it by hand.
66

7-
[email protected]-rc300.2 # Packages every Meteor app needs to have
8-
[email protected]-rc300.2 # Packages for a great mobile UX
9-
mongo@2.0.0-rc300.2 # The database Meteor supports right now
10-
static-html@1.3.3-rc300.2 # Define static page content in .html files
11-
[email protected]-rc300.2 # Reactive variable for tracker
12-
[email protected]-rc300.2 # Meteor's client-side reactive programming library
7+
[email protected] # Packages every Meteor app needs to have
8+
[email protected] # Packages for a great mobile UX
9+
mongo@2.1.2-beta330.1 # The database Meteor supports right now
10+
static-html@1.4.0 # Define static page content in .html files
11+
[email protected] # Reactive variable for tracker
12+
[email protected] # Meteor's client-side reactive programming library
1313

14-
[email protected]-rc300.2 # CSS minifier run for production mode
15-
standard-minifier-js@3.0.0-rc300.2 # JS minifier run for production mode
16-
[email protected]-rc300.2 # ECMAScript 5 compatibility for older browsers
17-
[email protected].9-rc300.2 # Enable ECMAScript2015+ syntax in app code
18-
typescript@5.4.3-rc300.2 # Enable TypeScript syntax in .ts and .tsx modules
19-
[email protected].0-rc300.2 # Server-side component of the `meteor shell` command
14+
[email protected] # CSS minifier run for production mode
15+
standard-minifier-js@3.1.0-beta330.1 # JS minifier run for production mode
16+
[email protected] # ECMAScript 5 compatibility for older browsers
17+
[email protected].11-beta330.1 # Enable ECMAScript2015+ syntax in app code
18+
typescript@5.6.3 # Enable TypeScript syntax in .ts and .tsx modules
19+
[email protected].1 # Server-side component of the `meteor shell` command

test-app/.meteor/release

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
METEOR@3.0-rc.2
1+
METEOR@3.3-beta.1

test-app/.meteor/versions

Lines changed: 64 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,64 @@
1-
2-
3-
4-
5-
6-
7-
8-
9-
10-
11-
12-
13-
14-
15-
16-
17-
18-
19-
20-
21-
22-
23-
24-
25-
26-
27-
28-
29-
30-
31-
32-
33-
34-
35-
36-
37-
38-
39-
40-
41-
42-
43-
44-
45-
46-
47-
48-
49-
50-
51-
52-
53-
54-
55-
56-
57-
58-
59-
60-
61-
62-
63-
64-
65-
66-
67-
68-
69-
70-
1+
2+
3+
4+
5+
6+
7+
8+
9+
10+
11+
12+
13+
14+
15+
16+
17+
18+
19+
20+
21+
22+
23+
24+
25+
26+
27+
28+
29+
30+
31+
32+
33+
34+
35+
36+
37+
38+
39+
40+
41+
42+
43+
44+
45+
46+
47+
48+
49+
50+
51+
52+
53+
54+
55+
56+
57+
58+
59+
60+
61+
62+
63+
64+

test-app/package.json

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,16 @@
1515
},
1616
"license": "MIT",
1717
"dependencies": {
18-
"@babel/runtime": "^7.23.5",
18+
"@babel/runtime": "^7.27.1",
1919
"jquery": "^3.7.1",
20-
"meteor-node-stubs": "^1.2.7",
21-
"puppeteer": "^10.4.0"
20+
"meteor-node-stubs": "^1.2.17",
21+
"puppeteer": "^24.8.2"
22+
},
23+
"meteor": {
24+
"modern": true
2225
},
2326
"devDependencies": {
24-
"@quave/eslint-config-quave": "^1.0.7"
27+
"@quave/eslint-config-quave": "^3.0.0"
2528
},
2629
"eslintConfig": {
2730
"extends": [

0 commit comments

Comments
 (0)