组件库中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 方式引入了:

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

相关推荐
Nan_Shu_6142 小时前
Web前端面试题(2)
前端
知识分享小能手2 小时前
React学习教程,从入门到精通,React 组件核心语法知识点详解(类组件体系)(19)
前端·javascript·vue.js·学习·react.js·react·anti-design-vue
蚂蚁RichLab前端团队3 小时前
🚀🚀🚀 RichLab - 花呗前端团队招贤纳士 - 【转岗/内推/社招】
前端·javascript·人工智能
孩子 你要相信光3 小时前
css之一个元素可以同时应用多个动画效果
前端·css
萌萌哒草头将军3 小时前
Oxc 和 Rolldown Q4 更新计划速览!🚀🚀🚀
javascript·vue.js·vite
huangql5203 小时前
npm 发布流程——从创建组件到发布到 npm 仓库
前端·npm·node.js
Qlittleboy3 小时前
uniapp如何使用本身的字体图标
javascript·vue.js·uni-app
Days20503 小时前
LeaferJS好用的 Canvas 引擎
前端·开源
小白菜学前端4 小时前
vue2 常用内置指令总结
前端·vue.js
林_深时见鹿4 小时前
Vue + ElementPlus 自定义指令控制输入框只可以输入数字
前端·javascript·vue.js