Skip to content

Commit 319db6e

Browse files
committed
perf(runtime-vapor): remove reliance on onScopeDispose in useSelector
1 parent 9311832 commit 319db6e

File tree

4 files changed

+92
-94
lines changed

4 files changed

+92
-94
lines changed

packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap

+9-9
Original file line numberDiff line numberDiff line change
@@ -137,36 +137,36 @@ export function render(_ctx) {
137137
`;
138138

139139
exports[`compiler: v-for > selector pattern 1`] = `
140-
"import { useSelectorPattern as _useSelectorPattern, child as _child, toDisplayString as _toDisplayString, setText as _setText, createFor as _createFor, template as _template } from 'vue';
140+
"import { child as _child, toDisplayString as _toDisplayString, setText as _setText, createFor as _createFor, template as _template } from 'vue';
141141
const t0 = _template("<tr> </tr>", true)
142142
143143
export function render(_ctx) {
144-
const _selector0_0 = _useSelectorPattern(() => _ctx.selected, () => (_ctx.rows))
145144
const n0 = _createFor(() => (_ctx.rows), (_for_item0) => {
146145
const n2 = t0()
147146
const x2 = _child(n2)
148-
_selector0_0.register(_for_item0.value.id, () => {
147+
_selector0_0.register(() => {
149148
_setText(x2, _toDisplayString(_ctx.selected === _for_item0.value.id ? 'danger' : ''))
150149
})
151150
return n2
152151
}, (row) => (row.id))
152+
const _selector0_0 = n0.useSelector(() => _ctx.selected)
153153
return n0
154154
}"
155155
`;
156156

157157
exports[`compiler: v-for > selector pattern 2`] = `
158-
"import { useSelectorPattern as _useSelectorPattern, setClass as _setClass, createFor as _createFor, template as _template } from 'vue';
158+
"import { setClass as _setClass, createFor as _createFor, template as _template } from 'vue';
159159
const t0 = _template("<tr></tr>", true)
160160
161161
export function render(_ctx) {
162-
const _selector0_0 = _useSelectorPattern(() => _ctx.selected, () => (_ctx.rows))
163162
const n0 = _createFor(() => (_ctx.rows), (_for_item0) => {
164163
const n2 = t0()
165-
_selector0_0.register(_for_item0.value.id, () => {
164+
_selector0_0.register(() => {
166165
_setClass(n2, _ctx.selected === _for_item0.value.id ? 'danger' : '')
167166
})
168167
return n2
169168
}, (row) => (row.id))
169+
const _selector0_0 = n0.useSelector(() => _ctx.selected)
170170
return n0
171171
}"
172172
`;
@@ -189,18 +189,18 @@ export function render(_ctx) {
189189
`;
190190

191191
exports[`compiler: v-for > selector pattern 4`] = `
192-
"import { useSelectorPattern as _useSelectorPattern, setClass as _setClass, createFor as _createFor, template as _template } from 'vue';
192+
"import { setClass as _setClass, createFor as _createFor, template as _template } from 'vue';
193193
const t0 = _template("<tr></tr>", true)
194194
195195
export function render(_ctx) {
196-
const _selector0_0 = _useSelectorPattern(() => _ctx.selected, () => (_ctx.rows))
197196
const n0 = _createFor(() => (_ctx.rows), (_for_item0) => {
198197
const n2 = t0()
199-
_selector0_0.register(_for_item0.value.id, () => {
198+
_selector0_0.register(() => {
200199
_setClass(n2, { danger: _for_item0.value.id === _ctx.selected })
201200
})
202201
return n2
203202
}, (row) => (row.id))
203+
const _selector0_0 = n0.useSelector(() => _ctx.selected)
204204
return n0
205205
}"
206206
`;

packages/compiler-vapor/src/generators/for.ts

+3-10
Original file line numberDiff line numberDiff line change
@@ -107,11 +107,9 @@ export function genFor(
107107
patternFrag.push(
108108
NEWLINE,
109109
`const ${selectorName} = `,
110-
...genCall(helper('useSelectorPattern'), [
110+
...genCall(`n${id}.useSelector`, [
111111
`() => `,
112112
...genExpression(selector, context),
113-
`, `,
114-
...sourceExpr,
115113
]),
116114
)
117115
}
@@ -120,9 +118,6 @@ export function genFor(
120118
const frag: CodeFragment[] = []
121119
frag.push('(', ...args, ') => {', INDENT_START)
122120
if (selectorPatterns.length || keyOnlyBindingPatterns.length) {
123-
const keyExpr =
124-
render.expressions.find(expr => expr.content === keyProp!.content) ??
125-
keyProp!
126121
frag.push(
127122
...genBlockContent(render, context, false, () => {
128123
const patternFrag: CodeFragment[] = []
@@ -131,9 +126,7 @@ export function genFor(
131126
const { effect } = selectorPatterns[i]
132127
patternFrag.push(
133128
NEWLINE,
134-
`_selector${id}_${i}.register(`,
135-
...genExpression(keyExpr, context),
136-
`, () => {`,
129+
`_selector${id}_${i}.register(() => {`,
137130
INDENT_START,
138131
)
139132
for (const oper of effect.operations) {
@@ -171,7 +164,6 @@ export function genFor(
171164
}
172165

173166
return [
174-
...patternFrag,
175167
NEWLINE,
176168
`const n${id} = `,
177169
...genCall(
@@ -182,6 +174,7 @@ export function genFor(
182174
flags ? String(flags) : undefined,
183175
// todo: hydrationNode
184176
),
177+
...patternFrag,
185178
]
186179

187180
// construct a id -> accessor path map.

packages/runtime-vapor/src/apiCreateFor.ts

+80-74
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import {
33
type ShallowRef,
44
isReactive,
55
isShallow,
6-
onScopeDispose,
76
pauseTracking,
87
resetTracking,
98
shallowReadArray,
@@ -80,12 +79,15 @@ export const createFor = (
8079
let oldBlocks: ForBlock[] = []
8180
let newBlocks: ForBlock[]
8281
let parent: ParentNode | undefined | null
82+
// useSelector only
83+
let currentKey: any
8384
// TODO handle this in hydration
8485
const parentAnchor = __DEV__ ? createComment('for') : createTextNode()
8586
const frag = new VaporFragment(oldBlocks)
8687
const instance = currentInstance!
8788
const canUseFastRemove = flags & VaporVForFlags.FAST_REMOVE
8889
const isComponent = flags & VaporVForFlags.IS_COMPONENT
90+
const selectors: ReturnType<typeof useSelector>[] = []
8991

9092
if (__DEV__ && !instance) {
9193
warn('createFor() can only be used inside setup()')
@@ -113,9 +115,12 @@ export const createFor = (
113115
}
114116
} else if (!newLength) {
115117
// fast path for clearing all
118+
for (const selector of selectors) {
119+
selector.cleanup()
120+
}
116121
const doRemove = !canUseFastRemove
117122
for (let i = 0; i < oldLength; i++) {
118-
unmount(oldBlocks[i], doRemove)
123+
unmount(oldBlocks[i], doRemove, false)
119124
}
120125
if (canUseFastRemove) {
121126
parent!.textContent = ''
@@ -230,6 +235,7 @@ export const createFor = (
230235
const keyRef = needKey ? shallowRef(key) : undefined
231236
const indexRef = needIndex ? shallowRef(index) : undefined
232237

238+
currentKey = key2
233239
let nodes: Block
234240
let scope: EffectScope | undefined
235241
if (isComponent) {
@@ -292,9 +298,18 @@ export const createFor = (
292298
}
293299
}
294300

295-
const unmount = ({ nodes, scope }: ForBlock, doRemove = true) => {
301+
const unmount = (
302+
{ nodes, scope, key }: ForBlock,
303+
doRemove = true,
304+
doDeregister = true,
305+
) => {
296306
scope && scope.stop()
297307
doRemove && removeBlock(nodes, parent!)
308+
if (doDeregister) {
309+
for (const selector of selectors) {
310+
selector.deregister(key)
311+
}
312+
}
298313
}
299314

300315
if (flags & VaporVForFlags.ONCE) {
@@ -307,7 +322,69 @@ export const createFor = (
307322
insert(frag, _insertionParent, _insertionAnchor)
308323
}
309324

325+
// @ts-expect-error
326+
frag.useSelector = useSelector
327+
310328
return frag
329+
330+
function useSelector(getActiveKey: () => any): {
331+
register: (key: any, cb: () => void) => void
332+
deregister: (key: any) => void
333+
cleanup: () => void
334+
} {
335+
let operMap = new Map<any, (() => void) | (() => void)[]>()
336+
let activeKey: any
337+
338+
watch(getActiveKey, newValue => {
339+
do {
340+
if (activeKey !== undefined) {
341+
const opers = operMap.get(activeKey)
342+
if (Array.isArray(opers)) {
343+
for (const oper of opers) {
344+
oper()
345+
}
346+
} else if (opers !== undefined) {
347+
opers()
348+
}
349+
}
350+
if (activeKey !== newValue) {
351+
activeKey = newValue
352+
continue
353+
}
354+
break
355+
} while (true)
356+
})
357+
358+
const res = { register, deregister, cleanup }
359+
selectors.push(res)
360+
return res
361+
362+
function cleanup() {
363+
operMap = new Map()
364+
}
365+
366+
function register(oper: () => void) {
367+
oper()
368+
let opers = operMap.get(currentKey)
369+
if (opers !== undefined) {
370+
if (Array.isArray(opers)) {
371+
opers.push(oper)
372+
} else {
373+
opers = [opers, oper]
374+
operMap.set(currentKey, opers)
375+
}
376+
} else {
377+
operMap.set(currentKey, oper)
378+
if (activeKey === undefined) {
379+
activeKey = getActiveKey()
380+
}
381+
}
382+
}
383+
384+
function deregister(key: any) {
385+
operMap.delete(key)
386+
}
387+
}
311388
}
312389

313390
export function createForSlots(
@@ -390,74 +467,3 @@ export function getRestElement(val: any, keys: string[]): any {
390467
export function getDefaultValue(val: any, defaultVal: any): any {
391468
return val === undefined ? defaultVal : val
392469
}
393-
394-
export function useSelectorPattern(
395-
getActiveKey: () => any,
396-
source: () => any[],
397-
): {
398-
register: (key: any, cb: () => void) => void
399-
} {
400-
let mapVersion = 1
401-
let operMap = new Map<any, (() => void)[]>()
402-
let activeOpers: (() => void)[] | undefined
403-
404-
watch(source, newValue => {
405-
if (Array.isArray(newValue) && !newValue.length) {
406-
operMap = new Map()
407-
activeOpers = undefined
408-
mapVersion++
409-
}
410-
})
411-
watch(getActiveKey, newValue => {
412-
if (activeOpers !== undefined) {
413-
for (const oper of activeOpers) {
414-
oper()
415-
}
416-
}
417-
activeOpers = operMap.get(newValue)
418-
if (activeOpers !== undefined) {
419-
for (const oper of activeOpers) {
420-
oper()
421-
}
422-
}
423-
})
424-
425-
return { register }
426-
427-
function register(key: any, oper: () => void) {
428-
oper()
429-
let opers = operMap.get(key)
430-
if (opers !== undefined) {
431-
opers.push(oper)
432-
} else {
433-
opers = [oper]
434-
operMap.set(key, opers)
435-
if (getActiveKey() === key) {
436-
activeOpers = opers
437-
}
438-
}
439-
const currentMapVersion = mapVersion
440-
onScopeDispose(() => {
441-
if (currentMapVersion === mapVersion) {
442-
deregister(key, oper)
443-
}
444-
})
445-
}
446-
447-
function deregister(key: any, oper: () => void) {
448-
const opers = operMap.get(key)
449-
if (opers !== undefined) {
450-
if (opers.length === 1) {
451-
operMap.delete(key)
452-
if (activeOpers === opers) {
453-
activeOpers = undefined
454-
}
455-
} else {
456-
const index = opers.indexOf(oper)
457-
if (index >= 0) {
458-
opers.splice(index, 1)
459-
}
460-
}
461-
}
462-
}
463-
}

packages/runtime-vapor/src/index.ts

-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ export {
3737
createForSlots,
3838
getRestElement,
3939
getDefaultValue,
40-
useSelectorPattern,
4140
} from './apiCreateFor'
4241
export { createTemplateRefSetter } from './apiTemplateRef'
4342
export { createDynamicComponent } from './apiCreateDynamicComponent'

0 commit comments

Comments
 (0)