-
-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Description
I've brought this up in chat previously, figured it'd be worth opening an issue.
One of the ways in which Svelte avoids unnecessary work is by tracking which properties are 'dirty' — thus <h1>Hello {name}!</h1>
yields the following update code:
if (changed.name) {
setData(text1, ctx.name);
}
This is all well and good, but it can result in unwieldy code if the expression (or, in the case of e.g. an each
block, any of the expressions within) depends on multiple values:
if (changed.foo || changed.bar || changed.baz) {
setData(text, ctx.foo * ctx.bar * ctx.baz);
}
We could instead use a bitmask. Take all the values that are referenced in the template (or in reactive declarations), sort them by frequency, and assign them a value that is a power of 2. In the case above, foo
might be 1
, bar
might be 2
, baz
might be 4
and qux
might be 8
.
function handle_click() {
- foo += 1; $$invalidate('foo', foo);
- bar += 1; $$invalidate('bar', bar);
+ foo += 1; $$invalidate('foo', foo, 1);
+ bar += 1; $$invalidate('bar', bar, 2);
}
Then, instead of changed
being an object that gets recreated after each update, it's just a number:
if (changed & (/*foo, bar or baz*/ 7)) {
setData(text, ctx.foo * ctx.bar * ctx.baz);
}
If changed
was equal to 8
(i.e. qux
changed) nothing would happen. This minifies much better:
-if(a.foo||a.bar||a.baz)b(c,d.foo*d.bar*d.baz)
+if(a&7)b(c,d.foo*d.bar*d.baz)
Drawbacks
It's slightly harder to understand what's going on by looking at the code. Also, without adding more complexity (e.g. making changed
an array of numbers, or only doing the bitmask thing where possible) it places a constraint on components: you can only reference 53 separate top-level variables (beyond that, you're in unsafe integer territory). For the vast majority of components that wouldn't be an issue, but it's not impossible to imagine someone hitting that wall. For that reason, we might want to consider doing this for v3, since it would be a breaking change.
Mangled context
While we're here, perhaps we should consider mangling context properties:
function handle_click() {
- foo += 1; $$invalidate('foo', foo, 1);
- bar += 1; $$invalidate('bar', bar, 2);
+ foo += 1; $$invalidate(1, foo);
+ bar += 1; $$invalidate(2, bar);
}
if (changed & (/*foo, bar or baz*/ 7)) {
- setData(text, ctx.foo * ctx.bar * ctx.baz);
+ setData(text, ctx[1] * ctx[2] * ctx[3]);
}
// minified
-if(a&7)b(c,d.foo*d.bar*d.baz)
+if(a&7)b(c,d[1]*d[2]*d[3])
Nested properties
Another thing to consider: we might want to take better advantage of information at our disposal when mutating objects and arrays:
function toggleTodo(i) {
todos[i].done = !todos[i].done;
}
In this situation, we could theoretically output code that didn't loop over todos
(as currently happens) but instead went straight to the affected iteration. (I'm glossing over some stuff here.) I don't know if that's something we need to consider if we go down this alternative path.