@@ -1150,4 +1150,168 @@ describe('reactivity/computed', () => {
11501150 const t2 = performance . now ( )
11511151 expect ( t2 - t1 ) . toBeLessThan ( process . env . CI ? 100 : 30 )
11521152 } )
1153+
1154+ describe ( 'dev mode optimization' , ( ) => {
1155+ // Mock __DEV__ for testing
1156+ const originalDev = ( globalThis as any ) . __DEV__
1157+ beforeEach ( ( ) => {
1158+ ; ( globalThis as any ) . __DEV__ = true
1159+ } )
1160+ afterEach ( ( ) => {
1161+ ; ( globalThis as any ) . __DEV__ = originalDev
1162+ } )
1163+
1164+ test ( 'should prevent unnecessary recomputations when dependencies have not actually changed' , ( ) => {
1165+ const getter = vi . fn ( )
1166+ const base = ref ( 1 )
1167+ const comp = computed ( ( ) => {
1168+ getter ( )
1169+ return base . value
1170+ } )
1171+
1172+ // Initial computation
1173+ expect ( comp . value ) . toBe ( 1 )
1174+ expect ( getter ) . toHaveBeenCalledTimes ( 1 )
1175+
1176+ // Trigger dependency tracking but without actual value change
1177+ // This simulates the scenario where globalVersion changes but actual dep values don't
1178+ const impl = comp as any as ComputedRefImpl
1179+ impl . globalVersion = - 1 // Force a version mismatch
1180+
1181+ // Access computed again - should not recompute due to dev mode optimization
1182+ expect ( comp . value ) . toBe ( 1 )
1183+ expect ( getter ) . toHaveBeenCalledTimes ( 1 ) // Should not have called getter again
1184+
1185+ // Now actually change the value
1186+ base . value = 2
1187+ expect ( comp . value ) . toBe ( 2 )
1188+ expect ( getter ) . toHaveBeenCalledTimes ( 2 ) // Should recompute when value actually changes
1189+ } )
1190+
1191+ test ( 'should refresh nested computed dependencies correctly' , ( ) => {
1192+ const getterA = vi . fn ( )
1193+ const getterB = vi . fn ( )
1194+ const getterC = vi . fn ( )
1195+
1196+ const base = ref ( 1 )
1197+ const compA = computed ( ( ) => {
1198+ getterA ( )
1199+ return base . value * 2
1200+ } )
1201+ const compB = computed ( ( ) => {
1202+ getterB ( )
1203+ return compA . value + 1
1204+ } )
1205+ const compC = computed ( ( ) => {
1206+ getterC ( )
1207+ return compB . value * 3
1208+ } )
1209+
1210+ // Initial computation
1211+ expect ( compC . value ) . toBe ( 9 ) // (1 * 2 + 1) * 3 = 9
1212+ expect ( getterA ) . toHaveBeenCalledTimes ( 1 )
1213+ expect ( getterB ) . toHaveBeenCalledTimes ( 1 )
1214+ expect ( getterC ) . toHaveBeenCalledTimes ( 1 )
1215+
1216+ // Force version mismatch to trigger dev mode optimization path
1217+ const implA = compA as any as ComputedRefImpl
1218+ const implB = compB as any as ComputedRefImpl
1219+ const implC = compC as any as ComputedRefImpl
1220+ implA . globalVersion = - 1
1221+ implB . globalVersion = - 1
1222+ implC . globalVersion = - 1
1223+
1224+ // Access computed again - should not recompute any level
1225+ expect ( compC . value ) . toBe ( 9 )
1226+ expect ( getterA ) . toHaveBeenCalledTimes ( 1 )
1227+ expect ( getterB ) . toHaveBeenCalledTimes ( 1 )
1228+ expect ( getterC ) . toHaveBeenCalledTimes ( 1 )
1229+
1230+ // Change base value
1231+ base . value = 2
1232+ expect ( compC . value ) . toBe ( 15 ) // (2 * 2 + 1) * 3 = 15
1233+ expect ( getterA ) . toHaveBeenCalledTimes ( 2 )
1234+ expect ( getterB ) . toHaveBeenCalledTimes ( 2 )
1235+ expect ( getterC ) . toHaveBeenCalledTimes ( 2 )
1236+ } )
1237+
1238+ test ( 'should recompute when at least one dependency actually changes' , ( ) => {
1239+ const getter = vi . fn ( )
1240+ const base1 = ref ( 1 )
1241+ const base2 = ref ( 2 )
1242+ const comp = computed ( ( ) => {
1243+ getter ( )
1244+ return base1 . value + base2 . value
1245+ } )
1246+
1247+ // Initial computation
1248+ expect ( comp . value ) . toBe ( 3 )
1249+ expect ( getter ) . toHaveBeenCalledTimes ( 1 )
1250+
1251+ // Force version mismatch
1252+ const impl = comp as any as ComputedRefImpl
1253+ impl . globalVersion = - 1
1254+
1255+ // Change one dependency
1256+ base1 . value = 5
1257+
1258+ // Should recompute because at least one dependency changed
1259+ expect ( comp . value ) . toBe ( 7 )
1260+ expect ( getter ) . toHaveBeenCalledTimes ( 2 )
1261+ } )
1262+
1263+ test ( 'should handle mixed changed and unchanged dependencies' , ( ) => {
1264+ const getter = vi . fn ( )
1265+ const unchanged = ref ( 1 )
1266+ const changed = ref ( 2 )
1267+ const comp = computed ( ( ) => {
1268+ getter ( )
1269+ return unchanged . value + changed . value
1270+ } )
1271+
1272+ // Initial computation
1273+ expect ( comp . value ) . toBe ( 3 )
1274+ expect ( getter ) . toHaveBeenCalledTimes ( 1 )
1275+
1276+ // Access unchanged ref to establish dependency tracking
1277+ unchanged . value
1278+
1279+ // Force version mismatch
1280+ const impl = comp as any as ComputedRefImpl
1281+ impl . globalVersion = - 1
1282+
1283+ // Change only one dependency
1284+ changed . value = 5
1285+
1286+ // Should recompute because at least one dependency changed
1287+ expect ( comp . value ) . toBe ( 6 ) // 1 + 5
1288+ expect ( getter ) . toHaveBeenCalledTimes ( 2 )
1289+ } )
1290+
1291+ test ( 'should not affect production mode behavior' , ( ) => {
1292+ // Set to production mode
1293+ ; ( globalThis as any ) . __DEV__ = false
1294+
1295+ const getter = vi . fn ( )
1296+ const base = ref ( 1 )
1297+ const comp = computed ( ( ) => {
1298+ getter ( )
1299+ return base . value
1300+ } )
1301+
1302+ // Initial computation
1303+ expect ( comp . value ) . toBe ( 1 )
1304+ expect ( getter ) . toHaveBeenCalledTimes ( 1 )
1305+
1306+ // Force version mismatch - in production this should still follow normal path
1307+ const impl = comp as any as ComputedRefImpl
1308+ impl . globalVersion = - 1
1309+
1310+ // In production mode, the optimization should not apply
1311+ // The behavior should be determined by the normal computed logic
1312+ expect ( comp . value ) . toBe ( 1 )
1313+ // In production, without the dev optimization, the call count behavior
1314+ // depends on the normal computed implementation
1315+ } )
1316+ } )
11531317} )
0 commit comments