From 53de73d08cdf2fb62dca7949767f120f3fac9629 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Thu, 23 Feb 2023 03:02:42 +0800 Subject: [PATCH] feat: simpler output for reactive vars if dependencies are all static (#7942) --- src/compiler/compile/Component.ts | 19 +++++- src/compiler/compile/render_dom/index.ts | 17 ++++-- src/compiler/compile/render_dom/invalidate.ts | 1 + src/compiler/interfaces.ts | 1 + .../reactive-class-optimized/expected.js | 11 +--- test/js/samples/reactive-values/expected.js | 60 +++++++++++++++++++ test/js/samples/reactive-values/input.svelte | 7 +++ 7 files changed, 101 insertions(+), 15 deletions(-) create mode 100644 test/js/samples/reactive-values/expected.js create mode 100644 test/js/samples/reactive-values/input.svelte diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index c0d703892b..ffac6e1f07 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -38,6 +38,7 @@ import compiler_warnings from './compiler_warnings'; import compiler_errors from './compiler_errors'; import { extract_ignores_above_position, extract_svelte_ignore_from_comments } from '../utils/extract_svelte_ignore'; import check_enable_sourcemap from './utils/check_enable_sourcemap'; +import is_dynamic from './render_dom/wrappers/shared/is_dynamic'; interface ComponentOptions { namespace?: string; @@ -1380,12 +1381,11 @@ export default class Component { module_dependencies.add(name); } } - const is_writable_or_mutated = - variable && (variable.writable || variable.mutated); + if ( should_add_as_dependency && (!owner || owner === component.instance_scope) && - (name[0] === '$' || is_writable_or_mutated) + (name[0] === '$' || variable) ) { dependencies.add(name); } @@ -1409,6 +1409,19 @@ export default class Component { const { expression } = node.body as ExpressionStatement; const declaration = expression && (expression as AssignmentExpression).left; + const is_dependency_static = Array.from(dependencies).every( + dependency => dependency !== '$$props' && dependency !== '$$restProps' && !is_dynamic(this.var_lookup.get(dependency)) + ); + + if (is_dependency_static) { + assignees.forEach(assignee => { + const variable = component.var_lookup.get(assignee); + if (variable) { + variable.is_reactive_static = true; + } + }); + } + unsorted_reactive_declarations.push({ assignees, dependencies, diff --git a/src/compiler/compile/render_dom/index.ts b/src/compiler/compile/render_dom/index.ts index 58b7a8317b..bc56f2eb27 100644 --- a/src/compiler/compile/render_dom/index.ts +++ b/src/compiler/compile/render_dom/index.ts @@ -390,13 +390,13 @@ export default function dom( const resubscribable_reactive_store_unsubscribers = reactive_stores .filter(store => { const variable = component.var_lookup.get(store.name.slice(1)); - return variable && (variable.reassigned || variable.export_name); + return variable && (variable.reassigned || variable.export_name) && !variable.is_reactive_static; }) .map(({ name }) => b`$$self.$$.on_destroy.push(() => ${`$$unsubscribe_${name.slice(1)}`}());`); if (has_definition) { - const reactive_declarations: (Node | Node[]) = []; - const fixed_reactive_declarations: Node[] = []; // not really 'reactive' but whatever + const reactive_declarations: Node[] = []; + const fixed_reactive_declarations: Array = []; // not really 'reactive' but whatever component.reactive_declarations.forEach(d => { const dependencies = Array.from(d.dependencies); @@ -417,6 +417,15 @@ export default function dom( reactive_declarations.push(statement); } else { fixed_reactive_declarations.push(statement); + for (const assignee of d.assignees) { + const variable = component.var_lookup.get(assignee); + if (variable && variable.subscribable) { + fixed_reactive_declarations.push(b` + ${component.compile_options.dev && b`@validate_store(${assignee}, '${assignee}');`} + @component_subscribe($$self, ${assignee}, $$value => $$invalidate(${renderer.context_lookup.get('$' + assignee).index}, ${'$' + assignee} = $$value)); + `); + } + } } }); @@ -430,7 +439,7 @@ export default function dom( const name = $name.slice(1); const store = component.var_lookup.get(name); - if (store && (store.reassigned || store.export_name)) { + if (store && (store.reassigned || store.export_name) && !store.is_reactive_static) { const unsubscribe = `$$unsubscribe_${name}`; const subscribe = `$$subscribe_${name}`; const i = renderer.context_lookup.get($name).index; diff --git a/src/compiler/compile/render_dom/invalidate.ts b/src/compiler/compile/render_dom/invalidate.ts index ffc5b6b42e..d9efdb5142 100644 --- a/src/compiler/compile/render_dom/invalidate.ts +++ b/src/compiler/compile/render_dom/invalidate.ts @@ -19,6 +19,7 @@ export function invalidate(renderer: Renderer, scope: Scope, node: Node, names: !variable.hoistable && !variable.global && !variable.module && + !variable.is_reactive_static && ( variable.referenced || variable.subscribable || diff --git a/src/compiler/interfaces.ts b/src/compiler/interfaces.ts index eb2cc336ed..402f9ff5e1 100644 --- a/src/compiler/interfaces.ts +++ b/src/compiler/interfaces.ts @@ -223,6 +223,7 @@ export interface Var { subscribable?: boolean; is_reactive_dependency?: boolean; imported?: boolean; + is_reactive_static?: boolean; } export interface CssResult { diff --git a/test/js/samples/reactive-class-optimized/expected.js b/test/js/samples/reactive-class-optimized/expected.js index 1d0606ad60..f75a4015b0 100644 --- a/test/js/samples/reactive-class-optimized/expected.js +++ b/test/js/samples/reactive-class-optimized/expected.js @@ -9,7 +9,6 @@ import { noop, safe_not_equal, space, - subscribe, toggle_class } from "svelte/internal"; @@ -133,13 +132,8 @@ let reactiveModuleVar = Math.random(); function instance($$self, $$props, $$invalidate) { let reactiveDeclaration; let $reactiveStoreVal; - - let $reactiveDeclaration, - $$unsubscribe_reactiveDeclaration = noop, - $$subscribe_reactiveDeclaration = () => ($$unsubscribe_reactiveDeclaration(), $$unsubscribe_reactiveDeclaration = subscribe(reactiveDeclaration, $$value => $$invalidate(3, $reactiveDeclaration = $$value)), reactiveDeclaration); - + let $reactiveDeclaration; component_subscribe($$self, reactiveStoreVal, $$value => $$invalidate(2, $reactiveStoreVal = $$value)); - $$self.$$.on_destroy.push(() => $$unsubscribe_reactiveDeclaration()); nonReactiveGlobal = Math.random(); const reactiveConst = { x: Math.random() }; reactiveModuleVar += 1; @@ -148,7 +142,8 @@ function instance($$self, $$props, $$invalidate) { reactiveConst.x += 1; } - $: $$subscribe_reactiveDeclaration($$invalidate(1, reactiveDeclaration = reactiveModuleVar * 2)); + $: reactiveDeclaration = reactiveModuleVar * 2; + component_subscribe($$self, reactiveDeclaration, $$value => $$invalidate(3, $reactiveDeclaration = $$value)); return [reactiveConst, reactiveDeclaration, $reactiveStoreVal, $reactiveDeclaration]; } diff --git a/test/js/samples/reactive-values/expected.js b/test/js/samples/reactive-values/expected.js new file mode 100644 index 0000000000..7ed435d6ad --- /dev/null +++ b/test/js/samples/reactive-values/expected.js @@ -0,0 +1,60 @@ +/* generated by Svelte vX.Y.Z */ +import { + SvelteComponent, + detach, + element, + init, + insert, + noop, + safe_not_equal, + set_data, + space, + text +} from "svelte/internal"; + +function create_fragment(ctx) { + let h1; + let t3; + let t4; + + return { + c() { + h1 = element("h1"); + h1.textContent = `Hello ${name}!`; + t3 = space(); + t4 = text(/*foo*/ ctx[0]); + }, + m(target, anchor) { + insert(target, h1, anchor); + insert(target, t3, anchor); + insert(target, t4, anchor); + }, + p(ctx, [dirty]) { + if (dirty & /*foo*/ 1) set_data(t4, /*foo*/ ctx[0]); + }, + i: noop, + o: noop, + d(detaching) { + if (detaching) detach(h1); + if (detaching) detach(t3); + if (detaching) detach(t4); + } + }; +} + +let name = 'world'; + +function instance($$self) { + let foo; + $: foo = name + name; + return [foo]; +} + +class Component extends SvelteComponent { + constructor(options) { + super(); + init(this, options, instance, create_fragment, safe_not_equal, {}); + } +} + +export default Component; \ No newline at end of file diff --git a/test/js/samples/reactive-values/input.svelte b/test/js/samples/reactive-values/input.svelte new file mode 100644 index 0000000000..d713217771 --- /dev/null +++ b/test/js/samples/reactive-values/input.svelte @@ -0,0 +1,7 @@ + + +

Hello {name}!

+{foo} \ No newline at end of file