pinia无法使用useRouter的原因,源码解析

0 阅读1分钟

问题诞生

在pinia等状态管理库当中,是无法使用useRouter获取router的,这样无法调用router.push进行路由跳转

import { defineStore } from "pinia";
import {useRouter} from 'vue'
export default defineStore("faultReport", () => {
  const router = useRouter() //为undefined
});

源码层面


function useRouter() {
    return inject(routerKey);
}

可以见,在useRouter本质是使用的vue3当中的inject与provide。但为什么获取不到,答案在createRouter里面

function createRouter(options) {
    ....
    const router = {
        install(app){
              app.provide(routerKey, router);
        }
    }
    
    ....
}

在日常配置vue-router当中,一般都会在main.js文件当中使用app.use(router),

而这个router又需要调用createRouter,此时,这个app是全局跟组件,既

import { createApp } from "vue";
import App from "./App.vue";

const app = createApp(App);//根组件
app.use(router)//调用router里的install方法

此方式相当于在跟组件注册了router,这样在各大子组件使用useRuter就获取到router了。

而pinia不在各大组件setup当中,自然就无法获取到router,这就是根本原因。

什么是各大组件的setup

setup就是组件的上下文,由一个vue的api可以获取,这又涉及到vue的源码

getCurrentInstance

image.png 这就是getCurrentInstance后获取的组件上下文,type里面就含有当前组件的信息,

而provide的值就存在CurrentInstance.provides当中,只不过vue-router使用Symbol类型值进行设置。 这样任何一个子组件就都可以通过useRouter获取到router.

 function provide(key, value) {
    if (!currentInstance) {
      {
        warn$1(`provide() can only be used inside setup().`);
      }
    } else {
      let provides = currentInstance.provides;
      const parentProvides = currentInstance.parent && currentInstance.parent.provides;
      if (parentProvides === provides) {
        provides = currentInstance.provides = Object.create(parentProvides);
      }
      provides[key] = value;
    }
  }
  function inject(key, defaultValue, treatDefaultAsFactory = false) {
    const instance = currentInstance || currentRenderingInstance;
    if (instance || currentApp) {
      const provides = instance ? instance.parent == null ? instance.vnode.appContext && instance.vnode.appContext.provides : instance.parent.provides : currentApp._context.provides;
      if (provides && key in provides) {
        return provides[key];
      } else if (arguments.length > 1) {
        return treatDefaultAsFactory && isFunction(defaultValue) ? defaultValue.call(instance && instance.proxy) : defaultValue;
      } else {
        warn$1(`injection "${String(key)}" not found.`);
      }
    } else {
      warn$1(`inject() can only be used inside setup() or functional components.`);
    }
  }

以上就是这俩歌api的源码部分。