mirror of https://github.com/sveltejs/svelte
perf: avoid re-traversing the effect tree after `$:` assignments (#17848)
If an assignment happens in a `$:` statement, any affected effects are rescheduled while the traversal is ongoing. But this is wasteful — it results in the `flush_effects` loop running another time, even though the affected effects are guaranteed to be visited _later_ in the traversal (unless the thing being updated is a store). This PR fixes it: inside a `legacy_pre_effect`, we temporarily pretend that the branch _containing_ the component with the `$:` statement is the `active_effect`, such that Svelte understands that any marked effects are about to be visited and thus don't need to be scheduled. We deal with the store case by temporarily pretending that there _is_ no `active_effect`. I will be delighted when we can rip all this legacy stuff out of the codebase. ### Before submitting the PR, please make sure you do the following - [x] It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs - [x] Prefix your PR title with `feat:`, `fix:`, `chore:`, or `docs:`. - [x] This message body should clearly illustrate what problems it solves. - [ ] Ideally, include a test that fails without this PR but passes with it. - [x] If this PR changes code within `packages/svelte/src`, add a changeset (`npx changeset`). ### Tests and linting - [x] Run the tests with `pnpm test` and lint the project with `pnpm lint`eager-bind-this-teardown
parent
7dc864d941
commit
2a1f5ada13
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': patch
|
||||
---
|
||||
|
||||
perf: avoid re-traversing the effect tree after `$:` assignments
|
||||
@ -0,0 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { count } from './stores';
|
||||
|
||||
let n = 0;
|
||||
|
||||
$: $count = n;
|
||||
</script>
|
||||
|
||||
<button onclick={() => n += 1}>{$count}</button>
|
||||
@ -0,0 +1,22 @@
|
||||
import { flushSync } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
html: `<button>0</button><button>0</button>`,
|
||||
|
||||
test({ assert, target }) {
|
||||
const [button1, button2] = target.querySelectorAll('button');
|
||||
|
||||
flushSync(() => button1.click());
|
||||
assert.htmlEqual(target.innerHTML, `<button>1</button><button>1</button>`);
|
||||
|
||||
flushSync(() => button1.click());
|
||||
assert.htmlEqual(target.innerHTML, `<button>2</button><button>2</button>`);
|
||||
|
||||
flushSync(() => button2.click());
|
||||
assert.htmlEqual(target.innerHTML, `<button>1</button><button>1</button>`);
|
||||
|
||||
flushSync(() => button2.click());
|
||||
assert.htmlEqual(target.innerHTML, `<button>2</button><button>2</button>`);
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,6 @@
|
||||
<script>
|
||||
import Child from './Child.svelte';
|
||||
</script>
|
||||
|
||||
<Child />
|
||||
<Child />
|
||||
@ -0,0 +1,3 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export const count = writable(0);
|
||||
Loading…
Reference in new issue