0.2.0 • Published 21 days ago
transform-component v0.2.0
transform-component - 转换不同类型的渲染组件
前言
背景
在一些时候,当前项目技术栈可能是React,但是要引入一个vue3的组件,或者不同运行时的react组件
所以,一个跨技术栈、跨运行时,且支持插槽的组件转换器就来了
说明
支持转换的组件类型: vue@2, vue@3, react, none(无框架,类微前端) 支持在支持类型间任意转换
- shimer - 组件垫片方法;用于创建一个宿主组件来接收节点、参数
- renderer - 组件渲染方法;用于通过接收的参数在指定节点下渲染组件
- transformer - 组件转换方法; 类似 shimer(wrapEl => renderer(Component, wrapEl))
用法
import transformComponent from 'transform-component'
/**
* @desc 转换渲染组件
* @param {string} sourceType - 来源组件类型 - ['vue2', 'vue3', 'react', 'none']
* @param {string} targetType - 目标组件类型 - ['vue2', 'vue3', 'react', 'none']
* @param {object|function} Component - 要转换的组件
* @param {object} sourceOptions - 来源组件的渲染参数
* @param {object} targetOptions - 目标组件渲染参数
* @return {object|function}
*/
const TargetCOmponent = transformComponent(sourceType, targetType, SourceComponent, sourceOptions, targetOptions)
console.log(TargetCOmponent)
查看所有的支持类型
import { transformers, shimers, renderers } from 'transform-component'
// 默认支持的组件类型转换器对象 - transformComponent的快捷形式
console.log(transformers)
// [
// "react2react",
// "react2vue3",
// "react2vue2",
// "react2none",
// "vue32react",
// "vue32vue3",
// "vue32vue2",
// "vue32none",
// "vue22react",
// "vue22vue3",
// "vue22vue2",
// "vue22none",
// "none2react",
// "none2vue3",
// "none2vue2",
// "none2none"
// ]
// 默认支持的垫片组件类型对象
console.log(shimers)
// [
// "react",
// "vue@3",
// "vue@2",
// "none"
// ]
// 默认支持的渲染组件类型对象
console.log(renderers)
// [
// "react",
// "vue@3",
// "vue@2",
// "none"
// ]
示例
react -> vue3
import React from 'react'
import ReactDOM from 'react-dom'
import Vue from 'vue'
import ReactComponent from 'some-react-component'
// 方案一
import transformComponent from 'transform-component'
const Vue3Component = transformComponent('react', 'vue@3', ReactComponent, { React, ReactDOM }, { Vue })
// 方案二
import { transformers } from 'transform-component'
const Vue3Component = transformers.react2vue3(ReactComponent, { React, ReactDOM }, { Vue })
transformers.react2vue3 是 transformComponent.bind(null, 'react', 'vue@3') 的快捷形式
react -> none - 直接渲染组件到dom
import React from 'react' import ReactDOM from 'react-dom' import ReactComponent from 'some-react-component'
// 方案一 import transformComponent from 'transform-component' const renderTo = transformComponent('react', 'none', ReactComponent, { React, ReactDOM })
// 方案二 import { transformers } from 'transform-component' const renderTo = transformers.react2none(ReactComponent, { React, ReactDOM })
// 渲染方法 const { dispose, setProps} = renderTo(document.querySelector('#app'))
> transformers.react2none 约等于 renderer.react
> 即直接渲染该组件
### vue3 -> react
```js
import React from 'react'
import ReactDOM from 'react-dom'
import Vue from 'vue'
import Vue3Component from 'some-vue3-component'
// 方案一
import transformComponent from 'transform-component'
const ReactComponent = transformComponent('vue@3', 'react', Vue3Component, { Vue }, { React, ReactDOM })
// 方案二
import { transformers } from 'transform-component'
const ReactComponent = transformers.vue32react(Vue3Component, { Vue }, { React, ReactDOM })
vue3 -> vue2
import Vue2 from 'vue@2'
import Vue3 from 'vue@3'
import Vue3Component from 'some-vue3-component'
// 方案一
import transformComponent from 'transform-component'
const Vue2Component = transformComponent('vue@3', 'vue@2', Vue3Component, { Vue: Vue3 }, { Vue: Vue2 })
// 方案二
import { transformers } from 'transform-component'
const Vue2Component = transformers.vue32vue2(Vue3Component, { Vue: Vue3 }, { Vue: Vue2 })
其余未列出的类型转换,与上面示例同理
扩展
扩展渲染方法 - renderer
import { renderers } from 'transform-component'
// 声明渲染器
const renderSomeTypeComponent = (SomeTypeComponent: Function | Object, wrapEl: HTMLElement, rendererOptions: Object) => {
const {
// 组件依赖包
SomeTypeRender,
// 默认的props; 变更时通过 setProps 接收
props,
// 默认的插槽; 变更时通过 setSlots 接收
slots,
} = rendererOptions
// 在指定节点下渲染该组件 - 伪代码
SomeTypeRender.renderComponent(SomeTypeComponent, wrapEl)
// 实现下列方法
// 释放组件时调用方法
const dispose = () => void
// 组件props变更时调用方法
const setProps = (newProps: Object) => void
// 抽象的插槽变更时调用方法
const setSlots = (newSlots: SlotsObject) => void
interface SlotsObject {
[slotKey: string]: (slotArgs: Array<any>, slotWrapEl: HTMLElement) => void;
}
// 返回渲染控制方法
return {
dispose,
setProps,
setSlots,
}
}
// 声明渲染器的依赖
renderSomeTypeComponent.deps = {
// 默认的变量名: 默认的包名
SomeTypeRender: 'some-type-render',
}
// 接入渲染器
renderers.someType = renderSomeTypeComponent
扩展垫片方法 - shimer
import { shimers } from 'transform-component'
// 声明渲染器
const createSomeTypeComponent = (renderToElement: Function, shimerOptions: Object) => {
const {
// 组件依赖包
SomeTypeRender,
} = shimerOptions
// 声明一个新组件 - 伪代码
const SomeTypeComponent = (props) => {
// 组件挂载时触发 dispose
let rendererHandle
onMount(() => {
const shimEl = shimRef.current
rendererHandle = renderToElement(shimEl, {
props,
})
})
// 组件卸载时触发 dispose
onUnMount(() => {
rendererHandle.dispose()
})
// 组件props变更时触发 setProps
onPropsChange(() => {
rendererHandle.setProps(props)
})
// 组件slots变更时触发 setSlots
const slotsParams = {}
onSlotsChange(() => {
const slots = {}
someTypeSlotKeys.forEach(key => {
slots[key] = (slotArgs, slotWrapEl) => {
slotsParams[key] = [slotArgs, slotWrapEl]
}
})
rendererHandle.setSlots(slots)
})
// 接收到slots参数回调时 生成构造插槽节点
const fakeSlotsNodes = {}
someTypeSlotKeys.forEach((key, i) => {
const slotParams = slotsParams[key]
if (slotParams && slotParams[1]) {
const [slotArgs, slotWrapEl] = slotParams
// const vnodes = someTypeSlotValues[i](...slotArgs)
fakeSlotsNodes[key] = <Teleport to={slotWrapEl}>{vnodes}</Teleport>
}
})
// 创建shim节点及构造的插槽节点
return <div component-shim ref={shimRef}>{fakeSlotsNodes}</div>
}
// 返回生成的组件
return SomeTypeComponent
}
// 声明渲染器的依赖
createSomeTypeComponent.deps = {
// 默认的变量名: 默认的包名
SomeTypeRender: 'some-type-render',
}
// 接入渲染器
shimers.someType = createSomeTypeComponent