组件库中vueRouter实例如何从外部注入

背景

当我们开发的 VUE 组件中需要用到 vue-router,但是这个 vue-router 实例是需要外部传递进来的。比如我们开发了一个类似 admin 模板的组件,里面用到了 vue-router 来实现页面路由的功能,但是这个路由的配置以及有哪些路由页面都是由使用我这个模板组件的人来控制的,所以 vue-router 实例也就只能由使用者创建了传递给我。

使用属性传入

最容易想到的方案就是通过 props 来将 vue-router 实力传递进来,然后再通过 provide 传递给所有子组件。但是使用 vue-router 的时候一般需要用到 routerouter 两个对象,这就需要将这两个都作为属性来传递。而且使用时我们也不能正常通过 useRoute 或者 useRouter 来获取,而是要用 inject,使用起来有点不爽。

通过注册注入

我们组件作为插件导出时是需要定义 install 方法来给使用者来注册我们组件的。

ts 复制代码
import { type App } from 'vue'
import Admin from './admin.vue'

Admin.install = (app: App) => {
  app.component('Admin', Admin)
}

export default Admin

那我们是不是可以通过这个 install 方法来从外部注入 vue-router 实例。install 方法第二个参数 options 正好可以用来传递 vue-router 实例。

ts 复制代码
import { type App } from 'vue'
import type { Router } from 'vue-router'
import Admin from './admin.vue'

Admin.install = (app: App, options: {
  router: Router
}) => {
  app.use(options.router)
  app.component('Admin', Admin)
}

export default Admin

上面代码打包发布后,发现怎么不起作用?

经过吭哧吭哧调试发现原来是 app.use(options.router) 这行代码的问题。在使用者注册我们插件的时候是如下调用的:

ts 复制代码
import { createApp } from 'vue'
import App from './App.vue'
import Admin from 'admin'

const app = createApp(App)

app.use(Admin, {
  router
});
app.mount('#app');

而 vue 的 use 方法定义如下:

调用插件的 install 方法传入的 app 是当前上面 const app = createApp(App) 创建的实例,而我们是需要在我们开发的 Admin 组件实例上注册。所以需要将代码修改成如下:

ts 复制代码
import { type App, createApp } from 'vue'
import type { Router } from 'vue-router'
import Admin from './admin.vue'

const adminApp = createApp(Admin);

Admin.install = (app: App, options: {
  router: Router
}) => {
  // 应该向 admin 实例注册
  adminApp.use(options.router)
  app.component('Admin', Admin)
}

export default Admin

打包发布后,发现,唉,怎么还是不起作用?

又经过吭哧吭哧调试发现原来是我们打包的时候,将 vue-router 的代码一起打包进组件代码里了。

可以看到打包出来的组件代码里把组件里用到的 useRouteuseRouter 方法一起打包进来了。

当我们执行 useRouter 方法去获取 router 对象的时候,就是执行的上图的代码

ts 复制代码
function useRouter() {
  return inject(routerKey);
}

通过 inject 来注入前面通过 adminApp.use(options.router) 注册的 router 对象。我们调试进入 inject 方法:

可以看到我们的 router 已经注册进来了(provides 中有 symbol(router) 对象),并且我们要取的 key 的值也是 symbol(router),但是 key in provides 却是 false

这是怎么回事呢?

我们再仔细看打包出来的代码

routerKey 是在打包代码里生成的一个 Symbol 值,而我们注册时的 key 是外部生成的。虽然两个值都是 symbol(router),但是由于 Symbol 值是独一无二的特性,只要它们不是同一个变量,它们就是不相等的。

所以我们打包时不能将 vue-router 的代码打包进我们的组件代码里。

我们打包是用的 vite。在 vite 中我们可以通过 build.rollupOptions.external 配置来排除不需要打包的模块:

ts 复制代码
export default defineConfig({
  plugins: [
    vue()
  ],
  build: {
    outDir: path.resolve(__dirname, './dist/'),
    lib: {
      entry: path.resolve(__dirname, './index.ts'),
    },
    rollupOptions: {
      external: ['vue', 'vue-router'],
      output: {
        // 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
        globals: {
          'vue': 'Vue',
          'vue-router': 'vueRouter'
        },
      },
    }
  },
})

我们再来看打包生成出来的组件代码,可以看到 vue-router 相关的代码变成 import 方式引入了:

我们打包发布后再来运行一下,终于成功了!

相关推荐
inksci14 分钟前
Vue 3 打开 el-dialog 时使 el-input 获取焦点
前端·javascript·vue.js
若愚67921 小时前
前端取经路——量子UI:响应式交互新范式
前端·ui·交互
PHASELESS4111 小时前
HTML常用标签用法全解析:构建语义化网页的核心指南
前端·html
粉末的沉淀1 小时前
css:倒影倾斜效果
前端·css
zandy10112 小时前
如何快速入门-衡石科技分析平台
服务器·前端·科技·数据库管理员
邝邝邝邝丹2 小时前
React学习———React Router
前端·学习·react.js
Yvonne爱编码3 小时前
CSS- 2.1 实战之图文混排、表格、表单
前端·css·html·github·状态模式·html5·hbuilder
前端小巷子3 小时前
CSS面试题汇总
前端·css·面试
绝美焦栖3 小时前
vue复杂数据类型多层嵌套的监听
前端·javascript·vue.js
GoodStudyAndDayDayUp3 小时前
gitlab+portainer 实现Ruoyi Vue后端CI/CD
vue.js·ci/cd·gitlab