背景
hel框架中,用于加载和渲染vue2和vue3组件 将vue2或者vue3组件转换为DOM。
vue3ToVue2
实现了一个加载远程vue3组件的vue2应用程序,Vue2应用依赖于vue3应用公开的组件。
vue3 提供方
export { default as Button } Button from './Button.vue'
export { default as vueVersion } from '../../node_modules/vue'
utils/transform.js
- 集成element-plus
import { createApp, h } from 'vue';
import ElementPlus from 'element-plus'
export const vue3ToVue2 = async (WrapperComponent, wrapperId) => {
let vm
return {
async mounted () {
await import('element-plus/dist/index.css')
vm = createApp({
render: () => {
return h(
WrapperComponent,
{
...this.$props
}
)
}
})
vm.use(ElementPlus)
vm.mount(`#${wrapperId}`)
},
props: WrapperComponent.props,
render () {
vm && vm.$forceUpdate()
}
}
}
export default {
vue3ToVue2,
};
vue2 使用方
vue组件
<template>
<div>render
<div id="vue2Button" />
<!-- <vue2Button /> -->
<RemoteCompAsyncBetter />
</div>
</template>
<script>
import { preFetchLib } from 'hel-micro'
// 本地联调
const enableCustom = !!window.location.port
const fetchOptions = {
custom: {
host: 'http://localhost:7002',
enable: enableCustom,
skipFetchHelMeta: true
}
}
export default {
components: {
// 可能因为编译器的关系,此写法无效
// vue2Button: vue3ToVue2('Button', 'vue2Button')
RemoteCompAsyncBetter: () => ({
component: new Promise(async(r, j) => {
try {
const name = 'hel-tpl-remote-vue3-comps-ts'
const mod = await preFetchLib(name, fetchOptions)
const { vue3ToVue2 } = mod.Transform
const WrapperComponent = mod['Button']
r(vue3ToVue2(WrapperComponent, 'vue2Button'))
} catch (err) {
j(err)
}
})
})
}
}
</script>
vue2ToVue3
实现了一个加载远程vue2组件的vue3应用程序,Vue3应用依赖于vue2应用公开的组件。
vue2 提供方
export { default as Vue2Test } from '@/views/vue2Test'
export { default as Vue2 } from '../../node_modules/vue/dist/vue'
vue3 使用方
vue组件
<template>
<div>
<h3>Vue3 App</h3>
<Content :count="count" />
<div id="vue2Button"></div>
<vue2-button @btnClick="inc"/>
<RemoteCompAsyncBetter />
</div>
</template>
<script>
import * as Vue from 'vue'
import { ref } from 'vue';
import { preFetchLib, bindVueRuntime } from 'hel-micro';
import { vue2ToVue3 } from '../utils/transform';
export default {
components: {
vue2Button: vue2ToVue3(Button, 'vue2Button'),
// 可能因为编译器的关系,此写法无效
// RemoteCompAsyncOld: async () => {
// const name = 'ib-zhangbb-component';
// const mod = await preFetchLib(name, fetchOptions);
// console.log("mod===>",mod)
// // const mod = await preFetchLib("remote-selection-point", "0.0.1-alpha.3");
// // const mod = await preFetchLib("hel-tpl-remote-vue-comps");
// return mod.Vue2Test;
// },
// 可能因为编译器的关系,此写法无效
// RemoteCompAsyncBetter: () => ({
// component: new Promise(async (r, j) => {
// try {
// const mod = await preFetchLib('lib-zhangbb-component', fetchOptions);
// console.log('mod===>', mod)
// const Vue2 = mod.Vue2
// const components = vue2ToVue3(mod.Vue2Test,vue2Button,Vue2)
// r(components);
// // r(mod.Vue2Test);
// } catch (err) {
// j(err);
// }
// }),
// }),
},
setup () {
const count = ref(0);
const inc = () => {
count.value++;
};
return {
count,
inc,
};
},
};
</script>
<style scoped>
h1 {
font-family: Arial, Helvetica, sans-serif;
}
</style>
utils.js
import { preFetchLib } from 'hel-micro'
// 本地联调
const enableCustom = !!window.location.port
const fetchOptions = {
custom: {
host: 'http://localhost:7002',
enable: enableCustom,
/**
* defaut: false
* 是否跳过获取 hel-meta.json 的获取步骤,true:跳过,false:不跳过
* 当用户设定 custom.host 配置时,hel-micro 采取总是相信该 host 存在一个 hel-meta.json 文件并尝试去获取
* 如获取失败时再去解析该 host 对应的首页并现场解析出 hel-meta.json 数据
* 因此获取动作可能会报一个 404 not found 符合预期的行为,用户可设定 skipFetchHelMeta 为 true 跳过此步骤
* 但建议加载线上模块时(非本地联调时),保持 skipFetchHelMeta 为 false 比较好,有利于提高模块加载速度( 无html解析hel-meta.json过程 )
*/
skipFetchHelMeta: true
}
}
function bindSlotContext(target = {}, context) {
return Object.keys(target).map((key) => {
const vnode = target[key]
vnode.context = context
return vnode
})
}
/**
* Transform vue2 components to DOM.
* @param {*} WrapperComponentName
* @param {*} wrapperId
* @returns
*/
export const vue2ToVue3 = async(WrapperComponentName, wrapperId) => {
let vm
const name = 'hel-tpl-remote-vue3-comps-ts'
const mod = await preFetchLib(name, fetchOptions)
const Vue = mod.vueVersion
const WrapperComponent = mod[WrapperComponentName]
return {
mounted() {
const slots = bindSlotContext(this.$slots, this.__self)
vm = new Vue({
render: (createElement) => {
return createElement(
WrapperComponent,
{
on: this.$attrs,
attrs: this.$attrs,
props: this.$props,
scopedSlots: this.$scopedSlots
},
slots
)
}
})
vm.$mount(`#${wrapperId}`)
},
props: WrapperComponent.props,
render() {
vm && vm.$forceUpdate()
}
}
}
实现demo vue2模块: 地址:github.com/zhangbinzhb… 对应分支dev_vue2_in_vue3_test
vue3模块: 地址:github.com/zhangbinzhb… 对应分支dev_vue3_in_vue2_test
特点
- 双向共享、同时引入多个依赖