在 Vue3 应用中集成用 Svelte 创建的 Web 组件

两年前做过一个实验,文章链接在这里:《在 React 或 Vue 应用中集成用 Svelte 创建的 Web 组件》(https://www.worldlink.com.cn/post/integrating-web-components-created-with-svelte-in-react-or-vue-apps.html)。当时的实现包括 React 和 Vue 2 版本的。期间有人说计数器的“自定义按钮”在 Vue 3 下工作不正常,经测试问题确实存在,多方查找也没能找到个解决方案,因此就搁置了。前一阵子在官网再次看到一段话,终于把它给看明白了,那现在就把 Vue 3 版的实现补上。gitHub repo 地址还是:https://github.com/vulcangz/svelte-webcomponent-in-react-vue

简单来说,需求是这样的:用 Svelte 实现一个计数器 web 组件,然后在 Vue 3 应用里调用这个组件。这个组件包括一个内部计数器和一个自定义按钮,组件内部的 "+"、"-" 按钮控制 web 组件本身的计数器加减;自定义按钮控制宿主应用(本例中是 Vue 3 应用)的计数器加 1。

支持 Vue 2 的 web 组件为什么到了 Vue 3 环境就不能正常工作呢?难道是 Vue 3 中对自定义事件的处理机制改变了?!在当时 Vue 3 由于一些突破性改变而被屡遭质疑的大环境下,我也是这么认为的。直到前一阵子看到 Vue 官网文档——《跳过组件解析》(https://cn.vuejs.org/guide/extras/web-components.html#using-custom-elements-in-vue)这段话,它是这么说的:

“默认情况下,Vue 会将任何非原生的 HTML 标签优先当作 Vue 组件处理,而将“渲染一个自定义元素”作为后备选项。这会在开发时导致 Vue 抛出一个“解析组件失败”的警告。要让 Vue 知晓特定元素应该被视为自定义元素并跳过组件解析,我们可以指定 compilerOptions.isCustomElement 这个选项。

如果在开发 Vue 应用时进行了构建配置,则应该在构建配置中传递该选项,因为它是一个编译时选项。”

问题的关键点就在这里了,名为 my-counter 的 web 组件在 Vue 3 应用中被当作了一个并不存在的 Vue 组件处理了,因此我们需要“跳过组件解析”,以让 Vue 3 应用把 my-counter 当作一定自定义元素。具体来说就是做如下编译配置:

https://github.com/vulcangz/svelte-webcomponent-in-react-vue/blob/lerna/packages/vue3-with-webcomponent/vite.config.js

import { fileURLToPath, URL } from 'node:url'

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue({
      template: {
        compilerOptions: {
          // 将所有带短横线的标签名都视为自定义元素
          isCustomElement: (tag) => tag.includes('-')
        }
      }
    })
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  }
})

项目原来采用的项目管理工具是 bolt(https://github.com/boltpkg/bolt),bolt 是在项目的根目录下维护所有包的依赖,很多情况下这能节省空间。但现在要把 Vue 2 和 Vue 3 同时交给 bolt 管理是在给 bolt 出难题了:)。所以现在把项目管理工具换做了 lerna,当然,为了服从 lerna 的管理,几个 app 的依赖也相应地做了个别调整。详细请参看 commit。

说老实话,上面官网的这句话当初我也看到过,只是当时混淆了这句话当中的 “Vue 组件” 和 web 组件的含义。而且还有,我估计动手实验过的人也许会问:在未配置 “isCustomElement” 编译选项的时候,既然 my-counter 未被正常解析,那为什么内部计数器的工作又是正常的呢?我认为从构建后的目标代码中能找到答案,具体就不再延申了。对此有兴趣的看官可以动手研究下。

喜歡:
2
標簽:
去到頂部