mirror of https://github.com/sveltejs/svelte
fix: coarse reactivity, alternative approach (#16100)
Make sure we track statically visible dependencies and untrack indirect dependencies Fixes #14351 --------- Co-authored-by: 7nik <kifiranet@gmail.com> Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>pull/16181/head
parent
d99d872519
commit
b224c3fb4b
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': patch
|
||||
---
|
||||
|
||||
fix: use compiler-driven reactivity in legacy mode template expressions
|
@ -1,21 +1,14 @@
|
||||
/** @import { Expression } from 'estree' */
|
||||
/** @import { AST } from '#compiler' */
|
||||
/** @import { ComponentContext } from '../types' */
|
||||
import * as b from '../../../../utils/builders.js';
|
||||
import { build_expression } from './shared/utils.js';
|
||||
|
||||
/**
|
||||
* @param {AST.AttachTag} node
|
||||
* @param {ComponentContext} context
|
||||
*/
|
||||
export function AttachTag(node, context) {
|
||||
context.state.init.push(
|
||||
b.stmt(
|
||||
b.call(
|
||||
'$.attach',
|
||||
context.state.node,
|
||||
b.thunk(/** @type {Expression} */ (context.visit(node.expression)))
|
||||
)
|
||||
)
|
||||
);
|
||||
const expression = build_expression(context, node.expression, node.metadata.expression);
|
||||
context.state.init.push(b.stmt(b.call('$.attach', context.state.node, b.thunk(expression))));
|
||||
context.next();
|
||||
}
|
||||
|
@ -0,0 +1,12 @@
|
||||
import { flushSync } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
test({ assert, target }) {
|
||||
const button = target.querySelector('button');
|
||||
|
||||
assert.htmlEqual(target.innerHTML, `<div></div><button>inc</button> [0,0,0,0,0,0,0,0,0]`);
|
||||
flushSync(() => button?.click());
|
||||
assert.htmlEqual(target.innerHTML, `<div></div><button>inc</button> [0,0,0,0,0,0,0,0,0]`);
|
||||
}
|
||||
});
|
@ -0,0 +1,45 @@
|
||||
<script>
|
||||
let a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0, h = 0, i = 0;
|
||||
function inc() {
|
||||
a++;
|
||||
b++;
|
||||
c++;
|
||||
d++;
|
||||
e++;
|
||||
f++;
|
||||
g++;
|
||||
h++;
|
||||
i++;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if a = 0}{/if}
|
||||
|
||||
{#each [b = 0] as x}{x,''}{/each}
|
||||
|
||||
{#key c = 0}{/key}
|
||||
|
||||
{#await d = 0}{/await}
|
||||
|
||||
{#snippet snip()}{/snippet}
|
||||
|
||||
{@render (e = 0, snip)()}
|
||||
|
||||
{@html f = 0, ''}
|
||||
|
||||
<div {@attach !!(g = 0)}></div>
|
||||
|
||||
{#key 1}
|
||||
{@const x = (h = 0)}
|
||||
{x, ''}
|
||||
{/key}
|
||||
|
||||
{#if 1}
|
||||
{@const x = (i = 0)}
|
||||
{x, ''}
|
||||
{/if}
|
||||
|
||||
<button on:click={inc}>inc</button>
|
||||
[{a},{b},{c},{d},{e},{f},{g},{h},{i}]
|
||||
|
||||
|
@ -0,0 +1,12 @@
|
||||
import { flushSync } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
test({ assert, target }) {
|
||||
const button = target.querySelector('button');
|
||||
|
||||
assert.htmlEqual(target.innerHTML, `<div></div><button data-foo="true">inc</button> 12 - 12`);
|
||||
flushSync(() => button?.click());
|
||||
assert.htmlEqual(target.innerHTML, `<div></div><button data-foo="true">inc</button> 13 - 12`);
|
||||
}
|
||||
});
|
@ -0,0 +1,36 @@
|
||||
<script>
|
||||
let count1 = 1;
|
||||
let count2 = 1;
|
||||
function fn(ret) {
|
||||
if (count1 > 100) return ret;
|
||||
count1++;
|
||||
count2++;
|
||||
return ret;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if fn(false)}{:else if fn(true)}{/if}
|
||||
|
||||
{#each fn([]) as x}{x, ''}{/each}
|
||||
|
||||
{#key fn(1)}{/key}
|
||||
|
||||
{#await fn(Promise.resolve())}{/await}
|
||||
|
||||
{#snippet snip()}{/snippet}
|
||||
|
||||
{@render fn(snip)()}
|
||||
|
||||
{@html fn('')}
|
||||
|
||||
<div {@attach fn(() => {})}></div>
|
||||
|
||||
{#key 1}
|
||||
{@const x = fn('')}
|
||||
{x}
|
||||
{/key}
|
||||
|
||||
<button data-foo={fn(true)} on:click={() => count1++}>{fn('inc')}</button>
|
||||
{count1} - {count2}
|
||||
|
||||
|
@ -0,0 +1,12 @@
|
||||
import { flushSync } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
test({ assert, target }) {
|
||||
const button = target.querySelector('button');
|
||||
|
||||
assert.htmlEqual(target.innerHTML, `<div></div><button>inc</button> 10 - 10`);
|
||||
flushSync(() => button?.click());
|
||||
assert.htmlEqual(target.innerHTML, `<div></div><button>inc</button> 11 - 10`);
|
||||
}
|
||||
});
|
@ -0,0 +1,46 @@
|
||||
<script>
|
||||
let count1 = 1;
|
||||
let count2 = 1;
|
||||
function fn(ret) {
|
||||
if (count1 > 100) return ret;
|
||||
count1++;
|
||||
count2++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
const obj = {
|
||||
get true() { return fn(true) },
|
||||
get false() { return fn(false) },
|
||||
get array() { return fn([]) },
|
||||
get string() { return fn('') },
|
||||
get promise() { return fn(Promise.resolve()) },
|
||||
get snippet() { return fn(snip) },
|
||||
get attachment() { return fn(() => {}) },
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if obj.false}{:else if obj.true}{/if}
|
||||
|
||||
{#each obj.array as x}{x, ''}{/each}
|
||||
|
||||
{#key obj.string}{/key}
|
||||
|
||||
{#await obj.promise}{/await}
|
||||
|
||||
{#snippet snip()}{/snippet}
|
||||
|
||||
{@render obj.snippet()}
|
||||
|
||||
{@html obj.string}
|
||||
|
||||
<div {@attach obj.attachment}></div>
|
||||
|
||||
{#key 1}
|
||||
{@const x = obj.string}
|
||||
{x}
|
||||
{/key}
|
||||
|
||||
<button on:click={() => count1++}>inc</button>
|
||||
{count1} - {count2}
|
||||
|
||||
|
Loading…
Reference in new issue