原文链接: github.com/yinxin630/b…
技术交流: fiora.suisuijiang.com/
前言
是不是看标题就感觉很蛋疼?
最近项目在基于 react 重构, 但是依赖的一个公共业务组件是 vue 的, 并且暂时没有使用 react 重构的计划, 但是总不能一直等下去, 遂尝试在 react 里渲染 vue 组件
vuera
需要借助 vuera(github.com/akxcv/vuera) 来做这个事情
同时, webpack里需要同时配置 react 和 vue 的 loader
首先, 写一个典型的 vue 组件
// Module.vue
<template>
<div>
<span>click times: {{count}}</span>
<br />
<button @click="handleClick">+1s</button>
</div>
</template>
<script>
export default {
name: "Module",
data() {
return {
count: 0
}
},
methods: {
handleClick() {
this.count++;
}
}
}
</script>
然后, 在 react 里面渲染 vue 组件
// index.tsx
import React from 'react';
import { render } from 'react-dom';
import { VueWrapper } from 'vuera';
import Module from './Module.vue';
render(
<div>
<h2>下面是vue组件</h2>
<VueWrapper component={Module} />
</div>,
document.getElementById('app'),
);
渲染效果:
向 vue 组件传参
react 里就像正常 react 组件一样传参即可
// index.tsx
<VueWrapper component={Module} msg="message pass by props" />
vue 里也是和正常 vue 组件一样, 定义 props
// Module.vue
<template>
<div>
<h3>{{msg}}</h3>
...
</div>
</template>
<script>
export default {
...
props: {
msg: {
type: String,
}
},
...
}
</script>
接收 vue 组件抛出的事件
在 vue 里面可以调用 this.$emit('xxx')
抛出事件
但是, react 组件里, 不能直接用 @xxx
或者 v-on:xxx
绑定事件, 但是可以通过 props 传递 function, 在 vue 组件里调用 props function
vue 组件对外抛出事件
// Module.vue
<script>
export default {
...
props: {
...
onCountChange: {
type: Function
}
},
methods: {
handleClick() {
this.count++;
this.onCountChange(this.count);
}
}
}
</script>
如果 vue 组件不受自己控制, 不能修改源码的话, 也可以自己封装一层, 将 props function 转为 @xxx
使用 this.$emit()
的 vue 组件
// Module.vue
<script>
export default {
...
methods: {
handleClick() {
this.count++;
this.$emit('count-change', this.count);
}
}
}
</script>
创建中间适配层组件 MoudleWrapper
// ModuleWrapper.vue
<template>
<div>
<Module :msg="msg" @count-change="onCountChange"></Module>
</div>
</template>
<script>
import Module from './Module.vue';
export default {
name: "ModuleWrapper",
components: { Module },
props: {
msg: {
type: String,
},
onCountChange: {
type: Function
}
},
}
</script>
修改入口文件, 改为渲染 MoudleWrapper 组件
// index.tsx
import React from 'react';
import { render } from 'react-dom';
import { VueWrapper } from 'vuera';
import MoudleWrapper from './MoudleWrapper.vue';
render(
<div>
<h2>下面是vue组件</h2>
<VueWrapper component={MoudleWrapper} msg="message pass by props" onCountChange={console.log} />
</div>,
document.getElementById('app'),
);
打包
使用 vue 组件, 会把完整的 vue.js 打包进去
如果该 vue 组件并不是初始状态就需要展示的话, 可以做按需加载打成一个单独的 chunk, 使其不影响页面初始渲染时间
下面是一个按需加载的例子, 新增 App 组件来做动态展示和按需加载的逻辑
// App.tsx
import React, { useState, useEffect } from 'react';
import { VueWrapper } from 'vuera';
let MoudleWrapper: any = null;
export default function App() {
const [showVueModule, toggleVueModule] = useState(false);
function showHideVueModule() {
if (MoudleWrapper) {
toggleVueModule(!showVueModule);
} else {
import(/* webpackChunkName: "vue-module" */ './MoudleWrapper.vue').then(module => {
MoudleWrapper = module.default;
toggleVueModule(!showVueModule);
});
}
}
return (
<div>
<h2>下面是vue组件</h2>
{
showVueModule && MoudleWrapper
? <div><VueWrapper component={MoudleWrapper} msg="message pass by props" onCountChange={console.log} /></div>
: null
}
<button onClick={showHideVueModule}>展示/隐藏vue组件</button>
</div>
);
}
运行效果如下: