轉自團隊原文:https://juejin.cn/post/6898498514813190158
說在前面
上篇文章討論完怎么在 React 和 Vue 互相調用之后,我們發現了新的需求。
升級 Vue3 之后,因為底層的 render 函數被重寫,導致 Vue2 現有豐富並完善的組件不能直接在 Vue3 上使用。因此 EMP 針對這個問題,提出了 Vue3 調用 Vue2 的方案,並且付諸實踐證實可行。
所以我們實現了 emp-vuett,一個跨越 Vue 版本的 HOC。
先看 demo
demo 鏈接:
git clone emp ,然后 yarn global add lerna && yarn && rm -rf node_modules/vue && lerna bootstrap && cd projects && dev:vue23 將啟動以下項目:
Vue3-project
Vue2-base
在 Vue3 下調用 Vue2 組件

Vue2 組件自身

使用
安裝 emp-vuett
yarn add @efox/emp-vuett or npm i @efox/emp-vuett
Vue3 調用 Vue2 組件
- 使用
npx @efox/emp-cli init新建一個 Vue3 項目 - 選擇 Vue3
3. 在項目里引入 @efox/emp-vuett 4. 引入一個 Vue2 的組件 4. 新建一個空 dom ,且賦予一個 id 5. 把 Vue2 組件和空dom id傳入 @efox/emp-vuett
完整代碼用例:
<template>
<div> <h1>VUE 3 Project</h1> <v3b-button /> <!-- 為了保持加載順序需要掛載一個空的 dom ,將 id 傳給 emp-vuett--> <div id="content"></div> <conent-in-vue3 :dataProps="num" :methodProps="propsFunc" @myEvent="emitFunc" /> <v3b-content /> </div> </template> <script> import { defineAsyncComponent, render } from "vue"; // Vue2 組件 import Content from "@v2b/Content"; // Vue2 在 Vue3 運行的 HOC import { Vue2InVue3 } from "@efox/emp-vuett"; // 讓 Vue2 組件通過 emp-vuett 可以在 Vue3 上運行 // 傳入 Vue2 組件和當前模板一個空div的id const ContentInVue3 = Vue2InVue3(Content, "content"); export default { components: { "conent-in-vue3": ContentInVue3, }, data() { return { num: 0, }; }, methods: { // 正常傳遞 props 到 Vue2 組件 propsFunc() { console.log("Vue3 to Vue2 Method Props"); }, // 可以正常在 Vue2 組件上使用 emit emitFunc() { this.num++; }, }, setup() {}, mounted() {}, name: "App", }; </script>
on,props,attrs,slot 等等都可以正常使用
注意:由於 Vue3 把 $listeners 移除里,被 Vue3 調用的 Vue2 組件需要做以下調整
// Vue2 使用 Vue3 傳過來的自定義事件需要把函數名 kebab-case 改為 camelCase 再加前綴 on // 例如:調用 @myEvent 需要寫成 onMyEvent // 被 Vue3 調用 this.$emit("onMyEvent"); // 被 Vue2 調用 this.$emit("myEvent");
emp-vuett 原理

EMP 遠程調用 Vue2 組件
上述被調用的 Vue2 組件都是通過 EMP 微前端遠程調用的。
想要遠程調用組件,只需要引用項目和被引用項目進行以下兩步:
npm i -g @efox/emp-cli or yarn global add @efox/emp-cli
2.新建 emp-config.js,並配置(了解更多 EMP 使用,請看 EMP 系列教程)
/vue2-base/emp-config.js
const withVue2 = require('@efox/emp-vue2') module.exports = withVue2(({config}) => { const projectName = 'vue2Base' const port = 8009 config.output.publicPath(`http://localhost:${port}/`) config.devServer.port(port) config.plugin('mf').tap(args => { args[0] = { ...args[0], ...{ name: projectName, library: {type: 'var', name: projectName}, filename: 'emp.js', exposes: { './Content': './src/components/Content', }, shared: ['vue/dist/vue.esm.js'], }, } return args }) config.plugin('html').tap(args => { args[0] = { ...args[0], ...{ title: 'EMP Vue2 Base', }, } return args }) })
/vue3-project/emp-config.js
const withFrameWork = require('@efox/emp-vue3') module.exports = withFrameWork(({config}) => { const projectName = 'vue3Project' config.output.publicPath('http://localhost:8006/') config.devServer.port(8006) config.plugin('mf').tap(args => { args[0] = { ...args[0], ...{ name: projectName, library: {type: 'var', name: projectName}, filename: 'emp.js', remotes: { '@v2b': 'vue2Base', }, exposes: {}, /* shared: { vue: {eager: true, singleton: true, requiredVersion: '^3.0.2'}, }, */ }, } return args }) config.plugin('html').tap(args => { args[0] = { ...args[0], ...{ title: 'EMP Vue3 Project', files: { js: ['http://localhost:8009/emp.js'], }, }, } return args }) })
總結
- 通過 EMP 的加持,遠程調用 Vue2 組件,極大方便了組件的復用。
- 通過利用 Vue3 的生命周期和 Vue2 的 Runtime 實現了在 Vue3 上無縫使用 Vue2 組件,極大豐富了 Vue3 的生態。
作者
![]() Benny Shi |
![]() Ken.Xu |


