React jsx 仅仅只是 React.createElement(component, props, ...children) 函数的语法糖。所有的jsx最终都会被转换成React.createElement的函数调用。
Vue jsx也仅仅是createElement函数(vue2) / h函数(vue3) 的语法糖。
Vue jsx转换(要用@vue/babel-preset-jsx解析为h函数) 和 React jsx转换(要用@babel/preset-react解析为React.createElement)有所区别。 而且 render函数生成的Vnode结构也有所不同。 编译时的处理也有所区别。
1.使用第三方库 vuera(vue2与react) veaury(vue3 与 react)
vuera: https://github.com/akxcv/vuera
veaury: https://github.com/gloriasoft/veaury
这里以vuera为例:
(1).Vue中使用react:
<template>
<div>
<my-react-component :message="message" />
</div>
</template>
<script>
import { ReactInVue } from 'vuera'
import MyReactComponent from './MyReactComponent'
export default {
data () {
message: 'Hello from React!',
},
components: { 'my-react-component': ReactInVue(MyReactComponent) } //局部注册为vue component
}
</script>
ReactInVue底层会使用到 封装的ReactWrapper 核心处理代码即
ReactDOM.render(React.createElement(Component, _extends({}, this.$props.passedProps, this.$attrs, this.$listeners, children, { ref: function ref(_ref) { return _this2.reactComponentRef = _ref; }})), this.$refs.react);
(2)React中使用Vue:
import React from 'react'
import { VueInReact } from 'vuera'
import MyVueComponent from './MyVueComponent.vue'
export default () => {
const Component = VueInReact(MyVueComponent)
return (
<div>
<Component message='Hello from Vue!' />
</div>
)
}
VueInReact底层会使用到封装的VueContainer,核心处理代码即
reactThisBinding.vueInstance = new Vue(_extends({
el: targetElement,
data: props
}, config.vueInstanceOptions, {
render: function render(createElement) {
return createElement(VUE_COMPONENT_NAME, {
props: this.$data,
on: on
}, [wrapReactChildren(createElement, this.children)]);
},
components: (_components = {}, defineProperty(_components, VUE_COMPONENT_NAME, component), defineProperty(_components, 'vuera-internal-react-wrapper', ReactWrapper), _components)
}));
vuera的babel插件 "plugins": ["vuera/babel"]
本质上是编译为__vueraReactResolver函数处理: return /*#__PURE__*/__vueraReactResolver(Calendar, { fullscreen: fullscreen || false });
exports.__vueraReactResolver = babelReactResolver$$1;
function babelReactResolver$$1(component, props, children) {
return isReactComponent(component) ? React.createElement(component, props, children) : React.createElement(VueContainer, Object.assign({ component: component }, props), children);
}
2.自行通过new Vue/ReactDOM 进行渲染
2-1.vue中使用react: 主要问题是vue无法识别react的 jsx语法。
配置babel 解决jsx语法,webpack.config.js中:
module.exports = {
module: {
rules: [
{
test: /.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options:{
"presets": ["@babel/preset-react", "@babel/preset-env"],
"plugins": ["@babel/plugin-transform-runtime"]
}
}
}
]
}
}
渲染React组件:
import React from 'react';
import * as ReactDOM from 'react-dom/client';
import Test from "./test.jsx"
<div id="reactApp"></div>
mounted () {
const container = document.getElementById('reactApp');
const root = ReactDOM.createRoot(container);
root.render(<Test />); //这个写法是 react-dom的18版本的写法
// 18版本之前的话,这样处理: ReactDOM.render( React.createElement(Test, null), document.getElementById('reactApp') );
}
2-2.react中使用vue: 主要问题是react无法解析.vue文件(组件)
配置vue-loader解决.vue文件解析问题。
// 在 webpack.config.js 内
const { VueLoaderPlugin } = require('vue-loader')
module.exports = {
mode: 'development',
module: { rules: [ { test: /.vue$/, loader: 'vue-loader' } ] },
plugins: [ new VueLoaderPlugin() ]
}
渲染Vue组件:
import Vue from 'vue'
import Test from "./test.vue"; //需要vue-loader解析.vue文件
export default class App extends Component {
componentDidMount() {
new Vue({ render: h => h(Test) }).$mount('#vueApp');
}
render() { return <div id="vueApp" /> }
}
//直接渲染vue的组件选项对象的话 无需配置vue-loader解析,直接引入全量Vue库处理即可,如下:
const Test = {
template: ` <div> ....... </div> `,
data () { return { } }
}