背景
最近的项目有一个加载远程组件的需求。基于此我对 Vue 加载远程组件的方案进行了研究,并且整理了两个可行的解决方案。
HTML 文件 + umd 组件
这个方案是最简单、最容易实现的。组件以 umd 的格式进行打包,然后在 HTML 文件中直接使用。
<div id="app"> <test-input></test-input> </div> <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> <script> // 将组件 URL 挂载到 script 标签上,然后通过 window 获取这个组件 await lodaScript('http://localhost/component/input/0.1.0/bundle.js') app.component('TestInput', window.TestInput) </script>
但是这个方案不适合在大型项目中使用,效率比较低。
Vue 工程项目 + esm /umd 组件
Vue 工程项目 + esm /umd 组件是我目前在使用的方案,但是在研究的过程中遇到了两个问题,逐一解决后,才把这个方案趟通了。
第一个问题 Relative references must start with either "/", "./", or "../"
由于我们的项目不需要兼容 IE,所以打包组件采用的是 esm 格式。打包后的组件源码如下:
import { reactive } from 'vue' // other code...
然后在主项目中进行引用:
const { default: TestInput } = await import('http://localhost/component/input/0.1.0/bundle.mjs')
在动态导入远程组件到项目时,提示报错 Relative references must start with either "/", "./", or "../"
。这是因为在浏览器中不支持以 import { reactive } from 'vue'
的方式进行导入,得把 'vue'
改成 https://..../vue.js
或者 './vue.js'
的形式才可以。平时我们这样用没问题是因为有 vite、webpack 等构建工具帮忙解决了这个问题。
第二个问题 Vue 上下文环境不同
产生上面的问题是因为要引入依赖,如果打包组件时把相关依赖都打在一起,那不就没有 import 语句了。结果试了一下还是不行,因为当前的 Vue 主项目和打包好的 Vue 组件存在两个不同的 Vue 上下文。导致在加载组件时报错,比如提示 xxx 变量找不到
这种问题。
虽然主项目和远程组件使用的 Vue 方法都是一样的,但由于各自的 Vue 上下文不一样,导致主项目无法正常使用远程组件。
以上两个问题困扰了我一天的时间,但是睡醒一觉后,终于想到了如何解决这两个问题。首先在浏览器上不能直接使用 import { reactive } from 'vue'
这种语句,那把它改成 const { reactive } = Vue
就能解决这个问题了。至于第二个问题,打包时不把依赖打在一起,而是在 main.js
文件中直接把整个 Vue 引进来:
import * as Vue from 'vue' window.Vue = Vue
这样就能确保主项目和远程组件使用的是同一个 Vue 上下文。
为了解决代码转换问题,我写了一个 rollup-plugin-import-to-const 插件(支持 rollup、vite),打包 esm 组件时,它会自动的把 import { reactive } from 'vue'
转换成 const { reactive } = Vue
。
至此,就可以在主项目中加载远程 esm 组件了:
const { default: TestInput } = await import('http://localhost/component/input/0.1.0/bundle.mjs')
其实只要能解决上面的两个问题,不管是 esm 还是 umd、cjs 等格式,都能够实现加载远程组件的方案。比如换成 umd 的格式来打包组件,就不需要引入 rollup 插件去转换代码了,并且还能支持 webpack。唯一要做的只是在 main.js
上把 Vue 全引进来挂到 window 下。
import * as Vue from 'vue' window.Vue = Vue
总结
远程组件的方案其实不止上面两种,比如还有直接加载 .vue
文件的方案,有个现成的 vue3-sfc-loader 插件能用。 一般来说,加载远程组件的应用场景比较少,所以网上能搜到的讨论也比较少。目前比较常见的应用场景应该就是在低代码平台中加载远程组件了。