@smart-portal/react-bridge
v1.0.1
Published
Use Vue and React each other
Downloads
13
Readme
SmartPortal react-bridge
门户vue-react bridge能力
重要功能
- 在react工程中可以直接使用 Vue3 组件
- 同项目中的vue以及react组件的context是共享的,可以实现数据无框架感阻碍通信
- 在react中可以使用vue的hooks,获取vue组件的上下文数据,比如vue-router、vuex,同时在vue组件也可以使用react的hooks,获取到react组件或者应用的上下文数据,比如react-router、provide、context等
- 支持高阶纯净模式使用,转换后的组件的子组件不再会有一个附加的dom元素容器
安装
$ npm i @smart-portal/react-bridge -S
# 一般搭配SmartPortal其他模块一起使用
$ npm i @smart-portal/components @smart-portal/hooks @smart-portal/utils @smart-portal/react-bridge -S用法
在React组件中使用Vue组件 - 基本用法
// SmartPortal组件
import { SmartPortalComponents } from "@smart-portal/utils/register/bridge";
export default function () {
const notifyOptions = {
schema: "General",
type: "Notify",
num: 1,
props: {
content:
"关于进一步加强能力中台储备区举办科技成果日(第33、34、35期)的通知",
},
};
// 组件所有事件回调
const emitsEvent = ({ eventName, data }) => {
console.log("事件回调:", eventName, data);
};
return (
<>
<SmartPortalComponents
options={notifyOptions}
onEmitsEvent={emitsEvent}
/>
</>
);
}在React组件中使用Vue组件 - 使用业务项目系统手写的Vue组件
// SmartPortal组件
import { ApplyVueInReact } from "@smart-portal/utils/register/bridge";
// 业务项目vue组件
import BasicVueComponent from "./Basic.vue";
const BasicWithNormal = ApplyVueInReact(BasicVueComponent);
export default function () {
return (
<>
<Basic />
</>
);
}在React组件中使用Vue组件 - 事件的用法
// SmartPortal组件
import { ApplyVueInReact } from "@smart-portal/utils/register/bridge";
// 业务项目vue组件
import BasicVueComponent from "./Basic.vue";
import { useState } from "react";
const Basic = ApplyVueInReact(BasicVueComponent);
export default function () {
function handleClickEvent() {
console.log("clicked!")
}
function handleEmitsEvent() {
console.log("事件回调")
}
return (
<div>
{/* 在Vue组件Basic中可以使用$emit('click')和$emit('emitsEvent')触发事件绑定的函数 */}
<Basic onClick={handleClickEvent} onEmitsEvent={handleEmitsEvent} />
</div>
);
}在React组件中使用Vue组件 - 插槽的用法
这个插槽的用法与Vue的jsx传递插槽的用法非常相似
// SmartPortal组件
import { ApplyVueInReact } from "@smart-portal/utils/register/bridge";
// 业务项目vue组件
import BasicVueComponent from "./Basic.vue";
const Basic = ApplyVueInReact(BasicVueComponent);
export default function () {
const vSlots = {
// 在Vue组件的template内使用'<slot name="slot1" />'进行渲染
slot1: <div>this is slot1(namedSlot)</div>,
// 在Vue组件的template内使用'<slot name="slot2" value="xxxxxx"/>'进行渲染
slot2: ({ value }) => (
<div>this is slot2(scopedSlot), and receive value: {value}</div>
),
// 在Vue组件的template内使用'<slot/>'进行渲染
default: <div>this is children</div>,
};
return (
<div>
{/* 只传递children */}
<Basic>
{/* 在Vue组件的template内使用'<slot/>'进行渲染 */}
<div>this is children</div>
</Basic>
{/* 传递 v-slots */}
<Basic v-slots={vSlots} />
{/* 另一种用法 */}
<Basic>{vSlots}</Basic>
</div>
);
}Context(上下文)
@smart-portal/react-bridge 会判断如果一个组件的外层有同一个框架的组件存在,那么就会使用 React 的 Portal 或者 Vue 的 Teleport创建被高阶组件包装的目标组件,而不是每次都创建一个新的应用实例。
React组件使用Vue组件 - Provider / useContext 的用法
// SmartPortal组件
import { ApplyVueInReact } from "@smart-portal/utils/register/bridge";
// 业务项目vue组件
import BasicVueComponent from "./Basic.vue";
import { createContext, useContext } from "react";
const Basic = ApplyVueInReact(BasicVueComponent);
// 创建 React context 对象
const Context = createContext({});
// React子组件
function SubReactComponent() {
// 获取 context 值
const { name } = useContext(Context);
return <div className="slot">bossName from Context: {name}</div>;
}
export default function () {
// 设置 context 值
return (
<Context.Provider value={{ name: "God" }}>
{/* Vue组件Basic */}
<Basic>
{/* 在Vue组件的children里, React子组件可以获得从外层Provider传入的context的值 */}
<SubReactComponent />
</Basic>
</Context.Provider>
);
}在React组件中使用Vue组件, v-model / v-models 的用法
'v-model' 的用法与Vue的jsx中的'v-model'用法相似
在React jsx中使用 v-model 属性, 可以有如下格式:[ modelValue, modelSetter, argumentKey, argumentModifiers ][ modelValue, modelSetter, argumentModifiers ][ modelValue, modelSetter ]
'argumentKey'代表了v-model的自定义参数名, 默认情况下, v-model的参数名时modelValue, 也可以将'argumentKey'设置在v-model属性之后的附加后缀上, 比如 v-model-god={[godValue, setGodValue]} = v-model={[godValue, setGodValue, 'god']}
// types
type modelValue = any;
type modelSetter = (newValue) => void;
type argumentKey = string;
type argumentModifiers = string[];// SmartPortal组件
import { ApplyVueInReact } from "@smart-portal/utils/register/bridge";
// 业务项目vue组件
import BasicVue from "./Basic.vue";
import Basic1Vue from "./Basic1.vue";
import { useState } from "react";
const Basic = ApplyVueInReact(BasicVue);
const Basic1 = ApplyVueInReact(Basic1Vue);
export default function () {
const [foo, setFoo] = useState(Math.random());
const [bar, setBar] = useState(Math.random());
const [zoo, setZoo] = useState(Math.random());
return (
<div>
<Basic v-model={[foo, setFoo]} v-model-bar={[bar, setBar]} />
{/*<Basic1 v-model={[zoo, setZoo, 'zoo']}/>*/}
{/*<Basic1 v-model={[zoo, setZoo, 'zoo', ['number']]}/>*/}
{/*<Basic1 v-model-zoo={[zoo, setZoo, ['number']]}/>*/}
<Basic1
v-models={{
// v-models对象中的key设置为'modelValue'时, 等同于默认的v-model属性
modelValue: [zoo, setZoo],
//...可以设置其他的自定义v-model的key
}}
/>
</div>
);
}在React组件中使用Vue组件, 使用异步的Vue组件
import { lazyVueInReact, lazyPureVueInReact } from "@smart-portal/react-bridge";
const AsyncBasicWithNormal = lazyVueInReact(() => import("./Basic"));
const AsyncBasicWithPure = lazyPureVueInReact(() => import("./Basic"));
export default function () {
return (
<>
<AsyncBasicWithNormal />
<AsyncBasicWithPure />
</>
);
}// types
type lazyReactInVue = (asyncImport: Promise<any>, options?: options) => any;在React组件中使用Vue组件, 获取ref实例的用法
// SmartPortal组件
import { ApplyVueInReact } from "@smart-portal/utils/register/bridge";
import BasicVue from "./Basic.vue";
import React, { createRef, useEffect } from "react";
const Basic = ApplyVueInReact(BasicVue);
export default function () {
const basicInstance = createRef(null);
useEffect(() => {
// Get the real vue instance through `__veauryVueRef__`
console.log(basicInstance.current.__veauryVueRef__);
}, []);
return <Basic ref={basicInstance} />;
}Vue 和 React共存时会引发JSX的TS类型错误.
如果您可以忽略 IDE 中的 TS 错误警告,则可以跳过本章。
Vue(@vue/runtime-dom) 和 React(@types/react) 都在全局命名空间 JSX 中扩展了类型接口,这会导致类型冲突。
例如,JSX.Element 不能同时扩展 ReactElement 和 VNode。
所以如果项目中同时安装了Vue和React,会在IDE(如vscode或webstorm)的JSX中引起TS错误警告,但这不会影响开发环境和生产环境的编译。
一个可行的解决方案是使用 patch-package 来修改 @vue/runtime-dom/dist/runtime-dom.d.ts 和 @types/react/index.d.ts,并且确保在tsconfig.json中设置compilerOptions.jsx为preserve。
比如这两个文件的改动如下。
node_modules/@types/react/index.d.ts(@types/[email protected])
diff --git a/node_modules/@types/react/index.d.ts b/node_modules/@types/react/index.d.ts
index 5c5d343..a850f38 100644
--- a/node_modules/@types/react/index.d.ts
+++ b/node_modules/@types/react/index.d.ts
@@ -3118,7 +3118,9 @@ type ReactManagedAttributes<C, P> = C extends { propTypes: infer T; defaultProps
declare global {
namespace JSX {
- interface Element extends React.ReactElement<any, any> { }
+ interface Element extends React.ReactElement<any, any> {
+ [k: string]: any
+ }
interface ElementClass extends React.Component<any> {
render(): React.ReactNode;
}
@@ -3133,8 +3135,12 @@ declare global {
: ReactManagedAttributes<T, P>
: ReactManagedAttributes<C, P>;
- interface IntrinsicAttributes extends React.Attributes { }
- interface IntrinsicClassAttributes<T> extends React.ClassAttributes<T> { }
+ interface IntrinsicAttributes extends React.Attributes {
+ [k: string]: any
+ }
+ interface IntrinsicClassAttributes<T> extends React.ClassAttributes<T> {
+ [k: string]: any
+ }
interface IntrinsicElements {
// HTML
node_modules/@vue/runtime-dom/dist/runtime-dom.d.ts(@vue/[email protected])
diff --git a/node_modules/@vue/runtime-dom/dist/runtime-dom.d.ts b/node_modules/@vue/runtime-dom/dist/runtime-dom.d.ts
index 3366f5a..b9eacc6 100644
--- a/node_modules/@vue/runtime-dom/dist/runtime-dom.d.ts
+++ b/node_modules/@vue/runtime-dom/dist/runtime-dom.d.ts
@@ -1493,7 +1493,7 @@ type NativeElements = {
declare global {
namespace JSX {
- interface Element extends VNode {}
+ // interface Element extends VNode {}
interface ElementClass {
$props: {}
}
