@esmx/router-vue 3.0.0-rc.27 → 3.0.0-rc.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/plugin.test.mjs +1 -1
- package/dist/router-link.d.ts +1 -1
- package/dist/use.mjs +10 -8
- package/dist/use.test.mjs +113 -415
- package/dist/util.d.ts +2 -0
- package/dist/util.mjs +8 -0
- package/dist/util.test.mjs +123 -97
- package/package.json +5 -5
- package/src/plugin.test.ts +1 -1
- package/src/use.test.ts +135 -534
- package/src/use.ts +13 -13
- package/src/util.test.ts +128 -111
- package/src/util.ts +13 -0
package/src/use.ts
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
provide,
|
|
9
9
|
ref
|
|
10
10
|
} from 'vue';
|
|
11
|
-
import { createSymbolProperty } from './util';
|
|
11
|
+
import { createDependentProxy, createSymbolProperty } from './util';
|
|
12
12
|
|
|
13
13
|
export interface VueInstance {
|
|
14
14
|
$parent?: VueInstance | null;
|
|
@@ -18,7 +18,7 @@ export interface VueInstance {
|
|
|
18
18
|
|
|
19
19
|
interface RouterContext {
|
|
20
20
|
router: Router;
|
|
21
|
-
route:
|
|
21
|
+
route: Route;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
const ROUTER_CONTEXT_KEY = Symbol('router-context');
|
|
@@ -136,7 +136,7 @@ export function getRouter(instance?: VueInstance): Router {
|
|
|
136
136
|
* ```
|
|
137
137
|
*/
|
|
138
138
|
export function getRoute(instance?: VueInstance): Route {
|
|
139
|
-
return findRouterContext(instance).route
|
|
139
|
+
return findRouterContext(instance).route;
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
/**
|
|
@@ -224,7 +224,7 @@ export function useRouter(): Router {
|
|
|
224
224
|
* ```
|
|
225
225
|
*/
|
|
226
226
|
export function useRoute(): Route {
|
|
227
|
-
return useRouterContext('useRoute').route
|
|
227
|
+
return useRouterContext('useRoute').route;
|
|
228
228
|
}
|
|
229
229
|
|
|
230
230
|
/**
|
|
@@ -259,20 +259,23 @@ export function useRoute(): Route {
|
|
|
259
259
|
export function useProvideRouter(router: Router): void {
|
|
260
260
|
const proxy = getCurrentProxy('useProvideRouter');
|
|
261
261
|
|
|
262
|
+
const dep = ref(false);
|
|
263
|
+
|
|
264
|
+
const proxiedRouter = createDependentProxy(router, dep);
|
|
265
|
+
const proxiedRoute = createDependentProxy(router.route, dep);
|
|
266
|
+
|
|
262
267
|
const context: RouterContext = {
|
|
263
|
-
router,
|
|
264
|
-
route:
|
|
268
|
+
router: proxiedRouter,
|
|
269
|
+
route: proxiedRoute
|
|
265
270
|
};
|
|
266
271
|
|
|
267
|
-
// Provide context via Vue 3's provide/inject (works in setup)
|
|
268
272
|
provide(ROUTER_INJECT_KEY, context);
|
|
269
|
-
|
|
270
|
-
// Also set on component instance for fallback (works after mount)
|
|
271
273
|
routerContextProperty.set(proxy, context);
|
|
272
274
|
|
|
273
275
|
const unwatch = router.afterEach((to: Route) => {
|
|
274
276
|
if (router.route === to) {
|
|
275
|
-
to.syncTo(
|
|
277
|
+
to.syncTo(proxiedRoute);
|
|
278
|
+
dep.value = !dep.value;
|
|
276
279
|
}
|
|
277
280
|
});
|
|
278
281
|
|
|
@@ -311,11 +314,8 @@ export function useProvideRouter(router: Router): void {
|
|
|
311
314
|
*/
|
|
312
315
|
export function useLink(props: RouterLinkProps) {
|
|
313
316
|
const router = useRouter();
|
|
314
|
-
const route = useRoute();
|
|
315
317
|
|
|
316
318
|
return computed(() => {
|
|
317
|
-
// vue will trigger a re-render when route.fullPath changes
|
|
318
|
-
route.fullPath;
|
|
319
319
|
return router.resolveLink(props);
|
|
320
320
|
});
|
|
321
321
|
}
|
package/src/util.test.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @vitest-environment happy-dom
|
|
3
3
|
*/
|
|
4
|
-
import { beforeEach, describe, expect, it } from 'vitest';
|
|
4
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
5
|
+
import { computed, nextTick, ref } from 'vue';
|
|
5
6
|
import { version } from 'vue';
|
|
6
7
|
import {
|
|
8
|
+
createDependentProxy,
|
|
7
9
|
createSymbolProperty,
|
|
8
10
|
isESModule,
|
|
9
11
|
isVue3,
|
|
@@ -251,168 +253,183 @@ describe('util.ts - Utility Functions', () => {
|
|
|
251
253
|
});
|
|
252
254
|
|
|
253
255
|
it('should return false for primitive values', () => {
|
|
254
|
-
const primitives = ['string',
|
|
256
|
+
const primitives = [42, 'string', true, false, Symbol('test')];
|
|
255
257
|
|
|
256
258
|
primitives.forEach((primitive) => {
|
|
257
259
|
expect(isESModule(primitive)).toBe(false);
|
|
258
260
|
});
|
|
259
261
|
});
|
|
260
|
-
});
|
|
261
|
-
});
|
|
262
262
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
263
|
+
it('should return false for functions and arrays', () => {
|
|
264
|
+
const nonObjects = [
|
|
265
|
+
() => {},
|
|
266
|
+
() => {},
|
|
267
|
+
[],
|
|
268
|
+
[1, 2, 3],
|
|
269
|
+
new Date()
|
|
270
|
+
];
|
|
266
271
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
expect(resolveComponent(value)).toBeNull();
|
|
272
|
+
nonObjects.forEach((nonObj) => {
|
|
273
|
+
expect(isESModule(nonObj)).toBe(false);
|
|
270
274
|
});
|
|
271
275
|
});
|
|
272
276
|
});
|
|
277
|
+
});
|
|
273
278
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
const result = resolveComponent(esModule);
|
|
284
|
-
|
|
285
|
-
expect(result).toBe(defaultComponent);
|
|
279
|
+
describe('resolveComponent', () => {
|
|
280
|
+
describe('should return default export or module itself', () => {
|
|
281
|
+
it('should return null for falsy values', () => {
|
|
282
|
+
expect(resolveComponent(null)).toBeNull();
|
|
283
|
+
expect(resolveComponent(undefined)).toBeNull();
|
|
284
|
+
expect(resolveComponent(false)).toBeNull();
|
|
285
|
+
expect(resolveComponent(0)).toBeNull();
|
|
286
|
+
expect(resolveComponent('')).toBeNull();
|
|
286
287
|
});
|
|
287
288
|
|
|
288
|
-
it('should return
|
|
289
|
-
const
|
|
289
|
+
it('should return default export if available', () => {
|
|
290
|
+
const defaultExport = { name: 'DefaultComponent' };
|
|
291
|
+
const module = {
|
|
290
292
|
__esModule: true,
|
|
291
|
-
|
|
293
|
+
default: defaultExport,
|
|
294
|
+
other: 'value'
|
|
292
295
|
};
|
|
293
296
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
expect(result).toBe(esModule);
|
|
297
|
+
expect(resolveComponent(module)).toBe(defaultExport);
|
|
297
298
|
});
|
|
298
299
|
|
|
299
|
-
it('should
|
|
300
|
-
const
|
|
301
|
-
const esModule = {
|
|
300
|
+
it('should return the module itself if no default export', () => {
|
|
301
|
+
const module = {
|
|
302
302
|
__esModule: true,
|
|
303
|
-
|
|
304
|
-
|
|
303
|
+
someExport: 'value',
|
|
304
|
+
otherExport: 42
|
|
305
305
|
};
|
|
306
306
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
expect(result).toBe(defaultComponent);
|
|
310
|
-
expect(result).not.toBe(esModule);
|
|
307
|
+
expect(resolveComponent(module)).toBe(module);
|
|
311
308
|
});
|
|
312
309
|
|
|
313
310
|
it('should handle modules with Symbol.toStringTag', () => {
|
|
314
|
-
const
|
|
315
|
-
const
|
|
311
|
+
const defaultExport = { name: 'SymbolDefaultComponent' };
|
|
312
|
+
const module = {
|
|
316
313
|
[Symbol.toStringTag]: 'Module',
|
|
317
|
-
default:
|
|
314
|
+
default: defaultExport
|
|
318
315
|
};
|
|
319
316
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
expect(result).toBe(defaultComponent);
|
|
317
|
+
expect(resolveComponent(module)).toBe(defaultExport);
|
|
323
318
|
});
|
|
324
319
|
|
|
325
|
-
it('should
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
const esModule = {
|
|
330
|
-
__esModule: true,
|
|
331
|
-
default: falsyDefault,
|
|
332
|
-
fallback: { name: 'FallbackComponent' }
|
|
333
|
-
};
|
|
320
|
+
it('should return non-module objects as is', () => {
|
|
321
|
+
const nonModule = { prop: 'value' };
|
|
322
|
+
expect(resolveComponent(nonModule)).toBe(nonModule);
|
|
323
|
+
});
|
|
334
324
|
|
|
335
|
-
|
|
325
|
+
it('should handle various component types', () => {
|
|
326
|
+
const functionComponent = () => ({ name: 'FunctionComponent' });
|
|
327
|
+
expect(resolveComponent(functionComponent)).toBe(
|
|
328
|
+
functionComponent
|
|
329
|
+
);
|
|
336
330
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
}
|
|
331
|
+
class ClassComponent {
|
|
332
|
+
name = 'ClassComponent';
|
|
333
|
+
}
|
|
334
|
+
const classInstance = new ClassComponent();
|
|
335
|
+
expect(resolveComponent(classInstance)).toBe(classInstance);
|
|
340
336
|
});
|
|
341
337
|
});
|
|
338
|
+
});
|
|
342
339
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
340
|
+
describe('createDependentProxy', () => {
|
|
341
|
+
it('should return original property values', () => {
|
|
342
|
+
const original = { foo: 'bar', count: 42 };
|
|
343
|
+
const dep = ref(false);
|
|
344
|
+
const proxy = createDependentProxy(original, dep);
|
|
348
345
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
it('should return function components directly', () => {
|
|
353
|
-
const functionComponent = () => ({ name: 'FunctionComponent' });
|
|
346
|
+
expect(proxy.foo).toBe('bar');
|
|
347
|
+
expect(proxy.count).toBe(42);
|
|
348
|
+
});
|
|
354
349
|
|
|
355
|
-
|
|
350
|
+
it('should handle method calls correctly', () => {
|
|
351
|
+
const original = {
|
|
352
|
+
items: [1, 2, 3],
|
|
353
|
+
getItems() {
|
|
354
|
+
return this.items;
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
const dep = ref(false);
|
|
358
|
+
const proxy = createDependentProxy(original, dep);
|
|
356
359
|
|
|
357
|
-
|
|
358
|
-
|
|
360
|
+
expect(proxy.getItems()).toEqual([1, 2, 3]);
|
|
361
|
+
expect(proxy.getItems()).toBe(original.items);
|
|
362
|
+
});
|
|
359
363
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
364
|
+
it('should handle nested property access', () => {
|
|
365
|
+
const original = {
|
|
366
|
+
nested: {
|
|
367
|
+
value: 'nested-value'
|
|
363
368
|
}
|
|
369
|
+
};
|
|
370
|
+
const dep = ref(false);
|
|
371
|
+
const proxy = createDependentProxy(original, dep);
|
|
364
372
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
expect(result).toBe(ClassComponent);
|
|
368
|
-
});
|
|
373
|
+
expect(proxy.nested.value).toBe('nested-value');
|
|
369
374
|
});
|
|
370
375
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
};
|
|
376
|
-
esModule.default = esModule; // Circular reference
|
|
376
|
+
it('should allow property modification', () => {
|
|
377
|
+
const original = { value: 'original' };
|
|
378
|
+
const dep = ref(false);
|
|
379
|
+
const proxy = createDependentProxy(original, dep);
|
|
377
380
|
|
|
378
|
-
|
|
381
|
+
proxy.value = 'modified';
|
|
382
|
+
expect(proxy.value).toBe('modified');
|
|
383
|
+
expect(original.value).toBe('modified');
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it('should trigger computed updates when dependency changes', async () => {
|
|
387
|
+
const original = { value: 'test' };
|
|
388
|
+
const dep = ref(false);
|
|
389
|
+
const proxy = createDependentProxy(original, dep);
|
|
379
390
|
|
|
380
|
-
|
|
391
|
+
const computedValue = computed(() => {
|
|
392
|
+
return proxy.value + '-' + String(dep.value);
|
|
381
393
|
});
|
|
382
394
|
|
|
383
|
-
|
|
384
|
-
const actualComponent = { name: 'DeepComponent' };
|
|
385
|
-
const esModule = {
|
|
386
|
-
__esModule: true,
|
|
387
|
-
default: {
|
|
388
|
-
default: {
|
|
389
|
-
default: actualComponent
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
};
|
|
395
|
+
expect(computedValue.value).toBe('test-false');
|
|
393
396
|
|
|
394
|
-
|
|
397
|
+
dep.value = true;
|
|
398
|
+
await nextTick();
|
|
399
|
+
expect(computedValue.value).toBe('test-true');
|
|
395
400
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
});
|
|
402
|
-
});
|
|
401
|
+
proxy.value = 'updated';
|
|
402
|
+
dep.value = false;
|
|
403
|
+
await nextTick();
|
|
404
|
+
expect(computedValue.value).toBe('updated-false');
|
|
405
|
+
});
|
|
403
406
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
407
|
+
it('should handle special properties', () => {
|
|
408
|
+
const symbol = Symbol('test');
|
|
409
|
+
const original = {
|
|
410
|
+
[symbol]: 'symbol-value'
|
|
411
|
+
};
|
|
412
|
+
const dep = ref(false);
|
|
413
|
+
const proxy = createDependentProxy(original, dep);
|
|
414
|
+
|
|
415
|
+
expect(proxy[symbol]).toBe('symbol-value');
|
|
416
|
+
});
|
|
411
417
|
|
|
412
|
-
|
|
418
|
+
it('should read the dependency on property access', () => {
|
|
419
|
+
const original = { value: 'test' };
|
|
420
|
+
const dep = ref(false);
|
|
413
421
|
|
|
414
|
-
|
|
422
|
+
const spy = vi.fn();
|
|
423
|
+
const depValue = dep.value; // 预先读取一次值
|
|
424
|
+
vi.spyOn(dep, 'value', 'get').mockImplementation(() => {
|
|
425
|
+
spy();
|
|
426
|
+
return depValue;
|
|
415
427
|
});
|
|
428
|
+
|
|
429
|
+
const proxy = createDependentProxy(original, dep);
|
|
430
|
+
|
|
431
|
+
proxy.value;
|
|
432
|
+
expect(spy).toHaveBeenCalled();
|
|
416
433
|
});
|
|
417
434
|
});
|
|
418
435
|
});
|
package/src/util.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { version } from 'vue';
|
|
2
|
+
import type { Ref } from 'vue';
|
|
2
3
|
|
|
3
4
|
export const isVue3 = version.startsWith('3.');
|
|
4
5
|
|
|
@@ -13,6 +14,18 @@ export function createSymbolProperty<T>(symbol: symbol) {
|
|
|
13
14
|
} as const;
|
|
14
15
|
}
|
|
15
16
|
|
|
17
|
+
export function createDependentProxy<T extends object>(
|
|
18
|
+
obj: T,
|
|
19
|
+
dep: Ref<boolean>
|
|
20
|
+
): T {
|
|
21
|
+
return new Proxy(obj, {
|
|
22
|
+
get(target, prop, receiver) {
|
|
23
|
+
dep.value;
|
|
24
|
+
return Reflect.get(target, prop, receiver);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
16
29
|
export function isESModule(obj: unknown): obj is Record<string | symbol, any> {
|
|
17
30
|
if (!obj || typeof obj !== 'object') return false;
|
|
18
31
|
const module = obj as Record<string | symbol, any>;
|