@esmx/router-vue
Version:
Vue integration for @esmx/router - A universal router that works seamlessly with both Vue 2.7+ and Vue 3
115 lines (114 loc) • 3.2 kB
JavaScript
import {
computed,
getCurrentInstance,
inject,
onBeforeUnmount,
provide,
ref
} from "vue";
import { createDependentProxy, createSymbolProperty } from "./util.mjs";
const ROUTER_CONTEXT_KEY = Symbol("router-context");
const ROUTER_INJECT_KEY = Symbol("router-inject");
const ROUTER_VIEW_DEPTH_KEY = Symbol("router-view-depth");
const routerContextProperty = createSymbolProperty(ROUTER_CONTEXT_KEY);
const routerViewDepthProperty = createSymbolProperty(
ROUTER_VIEW_DEPTH_KEY
);
function getCurrentProxy() {
const instance = getCurrentInstance();
if (!instance || !instance.proxy) {
throw new Error(
"[@esmx/router-vue] Must be used within setup() or other composition functions"
);
}
return instance.proxy;
}
function findRouterContext(vm) {
if (!vm) {
vm = getCurrentProxy();
}
let context = routerContextProperty.get(vm);
if (context) {
return context;
}
let current = vm.$parent;
while (current) {
context = routerContextProperty.get(current);
if (context) {
routerContextProperty.set(vm, context);
return context;
}
current = current.$parent;
}
throw new Error(
"[@esmx/router-vue] Router context not found. Please ensure useProvideRouter() is called in a parent component."
);
}
export function getRouter(instance) {
return findRouterContext(instance).router;
}
export function getRoute(instance) {
return findRouterContext(instance).route;
}
function useRouterContext() {
const injectedContext = inject(ROUTER_INJECT_KEY);
if (injectedContext) {
return injectedContext;
}
const proxy = getCurrentProxy();
return findRouterContext(proxy);
}
export function useRouter() {
return useRouterContext().router;
}
export function useRoute() {
return useRouterContext().route;
}
export function useProvideRouter(router) {
const proxy = getCurrentProxy();
const dep = ref(0);
const proxiedRouter = createDependentProxy(router, dep);
const proxiedRoute = createDependentProxy(router.route, dep);
const context = {
router: proxiedRouter,
route: proxiedRoute
};
provide(ROUTER_INJECT_KEY, context);
routerContextProperty.set(proxy, context);
const unwatch = router.afterEach((to) => {
if (router.route === to) {
to.syncTo(proxiedRoute);
dep.value++;
}
});
onBeforeUnmount(unwatch);
}
export function _useRouterViewDepth(isRender) {
const depth = inject(ROUTER_VIEW_DEPTH_KEY, 0);
if (isRender) {
provide(ROUTER_VIEW_DEPTH_KEY, depth + 1);
const proxy = getCurrentProxy();
routerViewDepthProperty.set(proxy, depth + 1);
}
return depth;
}
export function useRouterViewDepth() {
return _useRouterViewDepth();
}
export function getRouterViewDepth(instance) {
let current = instance.$parent;
while (current) {
const value = routerViewDepthProperty.get(current);
if (typeof value === "number") return value;
current = current.$parent;
}
throw new Error(
"[@esmx/router-vue] RouterView depth not found. Please ensure a RouterView exists in ancestor components."
);
}
export function useLink(props) {
const router = useRouter();
return computed(() => {
return router.resolveLink(props);
});
}