import * as Vue3 from "vue"; // @ts-ignore import { ComponentInternalInstance, h, isVue2, isVue3, type VNode, type VNodeChildren, type VNodeData } from "vue-demi"; import { InvokerItem } from "./InvokerItem"; function splitAttrs(obj: object) : { attrs: Record, on: Record } { const attrs: Record = {}; const listeners: Record = {}; 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 | 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, renderFunction?: Function) { let child: string | Array | InvokerItem = slotFn(args); // console.warn("Rendering Slot: ", child); // 字符串情况 if (typeof child == 'string') { return child; } // Array情况 else if (Array.isArray(child)) { return child.map((c: InvokerItem) => hh(c.tag, c.data, c.children, renderFunction)); } // InvokerItem情况 else { return hh(child.tag, child.data, child.children, renderFunction); } } export function hh(tag: string | object, data?: Record | null, children?: string | Array | Record, renderFunction?: Function) { // 适配Vue2渲染函数结构 // console.debug("Rendering", tag, data, children, `Vue Version: ${isVue2 ? 'Vue2' : 'Vue3'}`); const render = renderFunction || h; // 处理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, renderFunction)); // console.warn("Processed Children: ", processedChildren); } // 处理具名插槽对象 else if (children && typeof children == 'object') { if (isVue3) { processedChildren = Object.fromEntries( Object.entries(children).map(([name, slotFn]) => [ name, (args) => renderSlot(slotFn, args, renderFunction) ]) ) } else { // 兼容Vue2插槽 processedData['scopedSlots'] = Object.fromEntries( Object.entries(children).map(([name, slotFn]) => [ name, (args) => { return renderSlot(slotFn, args, renderFunction); } ]) ); } } // console.debug("Processed", { // processedTag, processedData, processedChildren // }); return render(processedTag, processedData, processedChildren); }