@@ -4,6 +4,7 @@ import isPlainObject from 'is-plain-obj'
44export const IS_PROXY = Symbol ( 'IS_PROXY' )
55export const PATH = Symbol ( 'PATH' )
66export const VALUE = Symbol ( 'VALUE' )
7+ export const CACHED_PROXY = Symbol ( 'CACHED_PROXY' )
78
89const arrayMutations = new Set ( [
910 'push' ,
@@ -16,22 +17,77 @@ const arrayMutations = new Set([
1617 'copyWithin' ,
1718] )
1819
19- const removeProxyMutations = [ 'set' , 'splice' , 'shift' , 'unshift' ]
20-
2120export class Proxifier {
2221 private proxyCache = { }
2322 constructor ( private tree : TTree ) {
24- tree . master . onMutation ( this . removeProxies )
23+ tree . master . onRemoveProxy ( this . removeProxies )
2524 }
2625 private concat ( path , prop ) {
2726 return path ? path + '.' + prop : prop
2827 }
29-
3028 addProxyToCache ( path : string , proxy : any ) {
31- return ( this . proxyCache [ path ] = proxy )
29+ const pathArray = path . split ( '.' )
30+ let currentCache = this . proxyCache
31+ const length = pathArray . length
32+ const keyIndex = length - 1
33+
34+ for ( let x = 0 ; x < length ; x ++ ) {
35+ const key = pathArray [ x ]
36+
37+ if ( ! currentCache [ key ] ) {
38+ currentCache [ key ] = { }
39+ }
40+
41+ if ( x === keyIndex ) {
42+ currentCache [ key ] [ CACHED_PROXY ] = proxy
43+ } else {
44+ currentCache = currentCache [ key ]
45+ }
46+ }
47+
48+ return proxy
3249 }
50+
3351 getProxyFromCache ( path : string ) {
34- return this . proxyCache [ path ]
52+ const pathArray = path . split ( '.' )
53+ let currentCache = this . proxyCache
54+ const length = pathArray . length
55+ const keyIndex = length - 1
56+
57+ for ( let x = 0 ; x < length ; x ++ ) {
58+ const key = pathArray [ x ]
59+
60+ if ( ! currentCache [ key ] ) {
61+ return null
62+ }
63+
64+ if ( x === keyIndex ) {
65+ return currentCache [ key ] [ CACHED_PROXY ]
66+ } else {
67+ currentCache = currentCache [ key ]
68+ }
69+ }
70+ }
71+
72+ removeProxies = ( path : string ) => {
73+ const pathArray = path . split ( '.' )
74+ let currentCache = this . proxyCache
75+ const length = pathArray . length
76+ const keyIndex = length - 1
77+
78+ for ( let x = 0 ; x < length ; x ++ ) {
79+ const key = pathArray [ x ]
80+
81+ if ( ! currentCache [ key ] ) {
82+ return null
83+ }
84+
85+ if ( x === keyIndex ) {
86+ delete currentCache [ key ]
87+ } else {
88+ currentCache = currentCache [ key ]
89+ }
90+ }
3591 }
3692
3793 shouldTrackMutations ( path ) {
@@ -41,16 +97,6 @@ export class Proxifier {
4197 )
4298 }
4399
44- removeProxies = ( mutation : IMutation ) => {
45- if ( removeProxyMutations . includes ( mutation . method ) ) {
46- for ( let path in this . proxyCache ) {
47- if ( path . indexOf ( mutation . path ) === 0 ) {
48- delete this . proxyCache [ path ]
49- }
50- }
51- }
52- }
53-
54100 ensureMutationTrackingIsEnabled ( path ) {
55101 if ( this . tree . master . options . devmode && ! this . tree . canMutate ( ) ) {
56102 throw new Error (
@@ -142,9 +188,19 @@ export class Proxifier {
142188 proxifier . ensureMutationTrackingIsEnabled ( nestedPath )
143189 return ( ...args ) => {
144190 const mutationTree = proxifier . getMutationTree ( )
191+ const method = String ( prop )
192+
193+ // On POP we can optimally remove cached proxy by removing the specific one
194+ // that was removed. If it is a PUSH, we do not have to remove anything, as
195+ // existing proxies stays the same
196+ if ( method === 'pop' ) {
197+ proxifier . tree . master . removeProxy ( nestedPath )
198+ } else if ( method !== 'push' ) {
199+ proxifier . tree . master . removeProxy ( path )
200+ }
145201
146202 mutationTree . addMutation ( {
147- method : String ( prop ) ,
203+ method,
148204 path : path ,
149205 args : args ,
150206 } )
@@ -167,6 +223,10 @@ export class Proxifier {
167223
168224 const mutationTree = proxifier . getMutationTree ( )
169225
226+ if ( isPlainObject ( target [ prop ] ) ) {
227+ proxifier . tree . master . removeProxy ( nestedPath )
228+ }
229+
170230 mutationTree . addMutation ( {
171231 method : 'set' ,
172232 path : nestedPath ,
@@ -248,6 +308,10 @@ export class Proxifier {
248308
249309 const mutationTree = proxifier . getMutationTree ( )
250310
311+ if ( isPlainObject ( target [ prop ] ) || Array . isArray ( target [ prop ] ) ) {
312+ proxifier . tree . master . removeProxy ( nestedPath )
313+ }
314+
251315 mutationTree . addMutation (
252316 {
253317 method : 'set' ,
@@ -277,6 +341,10 @@ export class Proxifier {
277341
278342 const mutationTree = proxifier . getMutationTree ( )
279343
344+ if ( isPlainObject ( target [ prop ] ) || Array . isArray ( target [ prop ] ) ) {
345+ proxifier . tree . master . removeProxy ( nestedPath )
346+ }
347+
280348 mutationTree . addMutation (
281349 {
282350 method : 'unset' ,
@@ -295,9 +363,9 @@ export class Proxifier {
295363 )
296364 )
297365 }
298- proxify ( value , path ) {
366+ proxify ( value : any , path : string ) {
299367 if ( value ) {
300- if ( value [ IS_PROXY ] && value [ PATH ] !== path ) {
368+ if ( value [ IS_PROXY ] && String ( value [ PATH ] ) !== String ( path ) ) {
301369 return this . proxify ( value [ VALUE ] , path )
302370 } else if ( value [ IS_PROXY ] ) {
303371 return value
0 commit comments