初始创建!
This commit is contained in:
132
src/components/DemiHelper.ts
Normal file
132
src/components/DemiHelper.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
import * as Vue3 from "vue";
|
||||
import { h, isVue2, isVue3, type VNode, type VNodeChildren, type VNodeData } from "vue-demi";
|
||||
import { InvokerItem } from "./InvokerItem";
|
||||
import { ComponentInternalInstance } from "vue-demi/lib/v2/index.js";
|
||||
|
||||
function splitAttrs(obj: object) : { attrs: Record<string, any>, on: Record<string, any> } {
|
||||
const attrs: Record<string, any> = {};
|
||||
const listeners: Record<string, any> = {};
|
||||
for (const key in obj) {
|
||||
if (key.indexOf('on') == 0) {
|
||||
const newKey = key[2].toLowerCase() + key.substring(3);
|
||||
listeners[newKey] = obj[key];
|
||||
} else {
|
||||
attrs[key] = obj[key];
|
||||
}
|
||||
}
|
||||
console.log("Listener", listeners)
|
||||
return { attrs, on: listeners };
|
||||
}
|
||||
|
||||
/**
|
||||
* 将扁平化的组件数据对象分离为Vue2形式的data对象
|
||||
* @param rawData
|
||||
* @returns
|
||||
*/
|
||||
function splitVue2Data(rawData?: Record<string, any> | null) {
|
||||
console.warn("Handling Vue2 data: ", rawData);
|
||||
if (!rawData) return {};
|
||||
|
||||
if (isVue3) throw new Error("Vue3 data object is not supported in Vue2");
|
||||
// Vue2分离式处理
|
||||
else {
|
||||
const on = {}; // 可能需要区分NativeOn
|
||||
const { class: cls, style, attrs = {}, props = {}, ...rest } = rawData;
|
||||
|
||||
// 处理事件
|
||||
for (const key in rest) {
|
||||
if (key.indexOf('on') == 0 && key != 'on') {
|
||||
const newKey = key[2].toLowerCase() + key.substring(3);
|
||||
on[newKey] = rest[key];
|
||||
}
|
||||
}
|
||||
const v2data = {
|
||||
class: cls,
|
||||
style,
|
||||
attrs: attrs,
|
||||
props: { ...props, ...rest }, // 未明确的属性放入props
|
||||
on,
|
||||
}
|
||||
return v2data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染具名插槽
|
||||
* @param slotFn
|
||||
* @param args
|
||||
* @returns
|
||||
*/
|
||||
function renderSlot(slotFn: Function, args: any, ctx?: ComponentInternalInstance) {
|
||||
let child: string | Array<InvokerItem> | InvokerItem = slotFn(args);
|
||||
// console.warn("Rendering Slot: ", child);
|
||||
// 字符串情况
|
||||
if (typeof child == 'string') {
|
||||
return child;
|
||||
}
|
||||
// Array<InvokerItem>情况
|
||||
else if (Array.isArray(child)) {
|
||||
return child.map((c: InvokerItem) => hh(c.tag, c.data, c.children, ctx));
|
||||
}
|
||||
// InvokerItem情况
|
||||
else {
|
||||
return hh(child.tag, child.data, child.children, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
export function hh(tag: string, data?: Record<string, any> | null, children?: string | Array<InvokerItem> | Record<string, Function>, ctx?: ComponentInternalInstance) {
|
||||
// 适配Vue2渲染函数结构
|
||||
// console.debug("Rendering", tag, data, children, `Vue Version: ${isVue2 ? 'Vue2' : 'Vue3'}`);
|
||||
|
||||
// 处理tag
|
||||
let processedTag: string | object = tag;
|
||||
if (isVue3 && typeof tag == 'string') {
|
||||
// Vue 3 需要解析全局组件
|
||||
// @ts-ignore - Vue 3 的 resolveComponent 需要特殊处理
|
||||
const resolved = Vue3?.resolveComponent?.(tag);
|
||||
processedTag = resolved || tag;
|
||||
}
|
||||
|
||||
// 处理data
|
||||
let processedData: any = null;
|
||||
if (isVue2) {
|
||||
processedData = splitVue2Data(data);
|
||||
}
|
||||
else processedData = data;
|
||||
|
||||
// 处理children
|
||||
let processedChildren: any = {};
|
||||
// 字符串则直接返回
|
||||
if (typeof children == 'string') {
|
||||
processedChildren = children
|
||||
}
|
||||
// 子节点列表则递归处理
|
||||
else if (Array.isArray(children)) {
|
||||
processedChildren = children.map(child => hh(child.tag, child.data, child.children, ctx));
|
||||
}
|
||||
// 处理具名插槽对象
|
||||
else if (children && typeof children == 'object') {
|
||||
if (isVue3) {
|
||||
processedChildren = Object.fromEntries(
|
||||
Object.entries(children).map(([name, slotFn]) => [
|
||||
name,
|
||||
(args) => renderSlot(slotFn, args, ctx)
|
||||
])
|
||||
)
|
||||
}
|
||||
else {
|
||||
// 兼容Vue2插槽
|
||||
processedData['scopedSlots'] = Object.fromEntries(
|
||||
Object.entries(children).map(([name, slotFn]) => [
|
||||
name,
|
||||
(args) => {
|
||||
return renderSlot(slotFn, args);
|
||||
}
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
console.debug("Processed", processedTag, processedData, processedChildren);
|
||||
return h(processedTag, processedData, processedChildren);
|
||||
}
|
Reference in New Issue
Block a user