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

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

相关推荐
spencer_tseng21 分钟前
I have something to say about Vue Node.js
vue.js·node.js
敲代码的彭于晏24 分钟前
前端上传与下载基础:Blob、File与ArrayBuffer详解
前端
緑水長流29 分钟前
什么是Promise?什么是async和await?
前端·javascript·vue.js
Mintopia29 分钟前
Three.js 相机(Camera)的使用详解
前端·javascript·three.js
wordbaby32 分钟前
PC 屏幕自适应的流行方案解析
前端
Mintopia33 分钟前
Node.js 中path模块的深度解析与实战应用
前端·javascript·node.js
程序员小续34 分钟前
git rebase 和git merge使用及区别
前端·git·后端
静待一世花开34 分钟前
React基础学习
前端
华科云商xiao徐35 分钟前
使用Scrapy编写图像下载程序示例
前端
WillaWang35 分钟前
html结构中图片下方的间隙
前端