diff --git a/src/compile/render-dom/index.ts b/src/compile/render-dom/index.ts index afc6985c47..16a9f8f5d7 100644 --- a/src/compile/render-dom/index.ts +++ b/src/compile/render-dom/index.ts @@ -145,33 +145,10 @@ export default function dom( ` : `@noop`; - const body = [ - deindent` - $$init($$make_dirty) { - ${component.init_uses_self && `const $$self = this;`} - - ${should_add_css && - `if (!document.getElementById("${component.stylesheet.id}-style")) @add_css();`} - - ${component.javascript || component.exports.map(x => `let ${x.name};`)} - - ${component.partly_hoisted.length > 0 && component.partly_hoisted.join('\n\n')} - - return [ - // TODO only what's needed by the template - () => ({ ${component.declarations.join(', ')} }), - ${inject_props}, - ${inject_refs} - ]; - } - - $$create_fragment(${component.alias('component')}, ctx) { - ${block.getContents()} - } - ` - ]; + const body = []; const debug_name = `<${component.customElement ? component.tag : name}>`; + const not_equal = component.options.immutable ? `@not_equal` : `@safe_not_equal`; if (component.options.dev) { // TODO check no uunexpected props were passed, as well as @@ -219,11 +196,35 @@ export default function dom( }); builder.addBlock(deindent` + function $$create_fragment(${component.alias('component')}, ctx) { + ${block.getContents()} + } + ${component.module_javascript} ${component.fully_hoisted.length > 0 && component.fully_hoisted.join('\n\n')} + function $$init($$self, $$make_dirty) { + ${should_add_css && + `if (!document.getElementById("${component.stylesheet.id}-style")) @add_css();`} + + ${component.javascript || component.exports.map(x => `let ${x.name};`)} + + ${component.partly_hoisted.length > 0 && component.partly_hoisted.join('\n\n')} + + return [ + // TODO only what's needed by the template + () => ({ ${component.declarations.join(', ')} }), + ${inject_props}, + ${inject_refs} + ]; + } + class ${name} extends ${superclass} { + constructor(options) { + super(options, $$init, $$create_fragment, ${not_equal}); + } + ${body.join('\n\n')} } `); diff --git a/src/internal/Component.js b/src/internal/Component.js index 0cbd4d3226..9107276e16 100644 --- a/src/internal/Component.js +++ b/src/internal/Component.js @@ -5,7 +5,7 @@ import { blankObject } from './utils.js'; import { children } from './dom.js'; export class $$Component { - constructor(options) { + constructor(options, init, create_fragment, not_equal) { this.$$beforeRender = []; this.$$onMount = []; this.$$afterRender = []; @@ -16,14 +16,15 @@ export class $$Component { this.$$slotted = options.slots || {}; set_current_component(this); - const [get_state, inject_props, inject_refs] = this.$$init( + const [get_state, inject_props, inject_refs] = init( + this, key => { this.$$make_dirty(key); if (this.$$bindings[key]) this.$$bindings[key](get_state()[key]); } ); - this.$$ = { get_state, inject_props, inject_refs }; + this.$$ = { get_state, inject_props, inject_refs, not_equal }; this.$$refs = {}; @@ -35,7 +36,7 @@ export class $$Component { } run_all(this.$$beforeRender); - this.$$fragment = this.$$create_fragment(this, this.$$.get_state()); + this.$$fragment = create_fragment(this, this.$$.get_state()); if (options.target) { intro.enabled = !!options.intro; @@ -63,8 +64,11 @@ export class $$Component { $set(values) { if (this.$$) { + const state = this.$$.get_state(); this.$$.inject_props(values); - for (const key in values) this.$$make_dirty(key); + for (const key in values) { + if (this.$$.not_equal(state[key], values[key])) this.$$make_dirty(key); + } } } @@ -137,7 +141,7 @@ export class $$ComponentDev extends $$Component { throw new Error(`'target' is a required option`); } - super(options); + super(...arguments); this.$$checkProps(); } diff --git a/src/internal/utils.js b/src/internal/utils.js index 8443e1f7de..d50d2b3385 100644 --- a/src/internal/utils.js +++ b/src/internal/utils.js @@ -47,4 +47,12 @@ export function run_all(fns) { export function is_function(thing) { return typeof thing === 'function'; +} + +export function safe_not_equal(a, b) { + return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function'); +} + +export function not_equal(a, b) { + return a != a ? b == b : a !== b; } \ No newline at end of file