Created
February 7, 2018 22:02
-
-
Save mrkishi/6f2e5946dd430eed08a9a951c63b8295 to your computer and use it in GitHub Desktop.
Experiments on spread attributes for Svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// App.html | |
<Child a="{{a}}" ...{{b_props}} c="{{c}}" d="{{d}}" ...{{e_props}} /> | |
// app.output.js | |
function create_main_fragment(state, component) { | |
var child = new Child({ | |
root: component.root, | |
// Even though not allowed by the standard, browsers | |
// simply ignore duplicate attributes | |
// So let's also use that precedence: a > a_props > b > b_props > c | |
data: assign( | |
state.e_props, | |
{ c: state.b, d: state.d }, | |
state.b_props, | |
{ a: state.a } | |
) | |
}); | |
// Note: disregard unmangled names -- assume they'd have | |
// unique prefixes and suffixes | |
// Cache all attributes by precedence level | |
// We know the number of spread objects used, so we can calculate | |
// the number of levels at compile-time and pre-instantiate these maps | |
// Naively: 1 precedence per source attribute (a + b + c + d + e; 5) | |
// Small optimization: | |
// 1 + 1 precedence per spread boundary (1 + a-b + b-c + d-e; 4) | |
var attr_by_precedence = [{}, {}, {}, {}, {}]; | |
// Given an attribute, get active precedence level | |
var current_precedence_for_attr = {}; | |
// Cache the attribute names for each spread object | |
var active_b_props = {}; | |
var active_e_props = {}; | |
return { | |
c: function create() { | |
child._fragment.c(); | |
}, | |
m: function mount(target, anchor) { | |
child._mount(target, anchor); | |
}, | |
p: function update(changed, state) { | |
var child_changes = {}; | |
// We start with the max precedence = 4 | |
if (changed.a) { | |
attr_by_precedence[4].a = state.a; | |
current_precedence_for_attr.a = 4; | |
child_changes.a = state.a; | |
} | |
// Precedence = 3 | |
if (changed.b_props) { | |
var leftover_attributes = active_b_props; | |
active_b_props = {}; | |
for (var key in state.b_props) { | |
active_b_props[key] = true; | |
delete leftover_attributes[key]; | |
// Always save value in appropriate cache... | |
attr_by_precedence[3][key] = state.b_props[key]; | |
// ...and only propagate changes if we have | |
// a higher precedence than what's already there | |
if ((current_precedence_for_attr[key] || 0) <= 3) { | |
current_precedence_for_attr[key] = 3; | |
child_changes[key] = state.b_props[key]; | |
} | |
} | |
for (var key in leftover_attributes) { | |
delete attr_by_precedence[3][key]; | |
// Bail if we weren't active in the first place... | |
if (current_precedence_for_attr[key] !== 3) continue; | |
// ...otherwise, find a lower precedence value | |
var next_in_line = undefined; | |
for (var i = 2; i >= 0; ++i) { | |
if (key in attr_by_precedence[i]) { | |
current_precedence_for_attr[key] = i; | |
next_in_line = attr_by_precedence[i][key]; | |
break; | |
} | |
} | |
child_changes[key] = next_in_line; | |
} | |
} | |
// Precedence = 2 | |
if (changed.c) { | |
attr_by_precedence[2].c = state.c; | |
// Only propagate changes if we have | |
// a higher precedence than what's there | |
if (current_precedence_for_attr.c >= 2) { | |
current_precedence_for_attr.c = 2; | |
child_changes.c = state.c; | |
} | |
} | |
// Precedence = 1 | |
if (changed.d) { | |
// ... | |
} | |
// Precedence = 0 | |
if (changed.e_props) { | |
// ... | |
} | |
child._set(nested_changes); | |
}, | |
u: function unmount() { | |
nested._unmount(); | |
}, | |
d: function destroy() { | |
child.destroy(false); | |
} | |
}; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// App.html | |
<Child ...{{props_b}} a="{{a}}" c="{{c}}" d="{{d}}" /> | |
// app.output.js | |
function create_main_fragment(state, component) { | |
var child = new Child({ | |
root: component.root, | |
// Always give preference to statically set attributes | |
data: assign( | |
state.b_props, | |
{ a: state.a, c: state.c, d: state.d } | |
) | |
}); | |
// Note: disregard unmangled names -- assume they'd have | |
// unique prefixes and suffixes | |
// Attributes set statically on the component | |
var child_attributes = ['a', 'c', 'd']; | |
// Cache the attribute names for the spread object | |
var b_props = {}; | |
return { | |
c: function create() { | |
child._fragment.c(); | |
}, | |
m: function mount(target, anchor) { | |
child._mount(target, anchor); | |
}, | |
p: function update(changed, state) { | |
var child_changes = {}; | |
if (changed.a) child_changes.a = state.a; | |
if (changed.b_props) { | |
var leftover_attributes = b_props; | |
b_props = {}; | |
for (var key in state.b_props) { | |
if (child_attributes.indexOf(key) !== -1) continue; | |
b_props[key] = true; | |
delete leftover_attributes[key]; | |
child_changes[key] = state.b_props[key]; | |
} | |
for (var key in leftover_attributes) { | |
child_changes[key] = undefined; | |
} | |
} | |
if (changed.c) child_changes.c = state.c; | |
if (changed.d) child_changes.d = state.d; | |
child._set(nested_changes); | |
}, | |
u: function unmount() { | |
nested._unmount(); | |
}, | |
d: function destroy() { | |
child.destroy(false); | |
} | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment