更新配置项,更新Receiver入参,修复渲染错误

This commit is contained in:
2025-05-13 16:15:11 +08:00
parent 5d2af32d68
commit 0c75dca9c3
7 changed files with 708 additions and 730 deletions

1235
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
<template> <template>
<div> <div>
vue-{{ vueVer }} vue-{{ vueVer }}
<Invoker url="/child.html#?id={id}" style="border:1px solid red;height: 400px;overflow-y: scroll;" :items="items" /> <Invoker style="border:1px solid red;height: 400px;overflow-y: scroll;" :items="items" />
<component :is="dom" /> <component :is="dom" />
</div> </div>
</template> </template>

View File

@@ -1,8 +1,7 @@
import * as Vue3 from "vue"; import * as Vue3 from "vue";
// @ts-ignore // @ts-ignore
import { h, isVue2, isVue3, type VNode, type VNodeChildren, type VNodeData } from "vue-demi"; import { ComponentInternalInstance, h, isVue2, isVue3, type VNode, type VNodeChildren, type VNodeData } from "vue-demi";
import { InvokerItem } from "./InvokerItem"; 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> } { function splitAttrs(obj: object) : { attrs: Record<string, any>, on: Record<string, any> } {
const attrs: Record<string, any> = {}; const attrs: Record<string, any> = {};
@@ -58,7 +57,7 @@ function splitVue2Data(rawData?: Record<string, any> | null) {
* @param args * @param args
* @returns * @returns
*/ */
function renderSlot(slotFn: Function, args: any, ctx?: ComponentInternalInstance) { function renderSlot(slotFn: Function, args: any, renderFunction?: Function) {
let child: string | Array<InvokerItem> | InvokerItem = slotFn(args); let child: string | Array<InvokerItem> | InvokerItem = slotFn(args);
// console.warn("Rendering Slot: ", child); // console.warn("Rendering Slot: ", child);
// 字符串情况 // 字符串情况
@@ -67,17 +66,18 @@ function renderSlot(slotFn: Function, args: any, ctx?: ComponentInternalInstance
} }
// Array<InvokerItem>情况 // Array<InvokerItem>情况
else if (Array.isArray(child)) { else if (Array.isArray(child)) {
return child.map((c: InvokerItem) => hh(c.tag, c.data, c.children, ctx)); return child.map((c: InvokerItem) => hh(c.tag, c.data, c.children, renderFunction));
} }
// InvokerItem情况 // InvokerItem情况
else { else {
return hh(child.tag, child.data, child.children, ctx); return hh(child.tag, child.data, child.children, renderFunction);
} }
} }
export function hh(tag: string, data?: Record<string, any> | null, children?: string | Array<InvokerItem> | Record<string, Function>, ctx?: ComponentInternalInstance) { export function hh(tag: string | object, data?: Record<string, any> | null, children?: string | Array<InvokerItem> | Record<string, Function>, renderFunction?: Function) {
// 适配Vue2渲染函数结构 // 适配Vue2渲染函数结构
// console.debug("Rendering", tag, data, children, `Vue Version: ${isVue2 ? 'Vue2' : 'Vue3'}`); // console.debug("Rendering", tag, data, children, `Vue Version: ${isVue2 ? 'Vue2' : 'Vue3'}`);
const render = renderFunction || h;
// 处理tag // 处理tag
let processedTag: string | object = tag; let processedTag: string | object = tag;
@@ -103,7 +103,8 @@ export function hh(tag: string, data?: Record<string, any> | null, children?: st
} }
// 子节点列表则递归处理 // 子节点列表则递归处理
else if (Array.isArray(children)) { else if (Array.isArray(children)) {
processedChildren = children.map(child => hh(child.tag, child.data, child.children, ctx)); processedChildren = children.map(child => hh(child.tag, child.data, child.children, renderFunction));
console.warn("Processed Children: ", processedChildren);
} }
// 处理具名插槽对象 // 处理具名插槽对象
else if (children && typeof children == 'object') { else if (children && typeof children == 'object') {
@@ -111,7 +112,7 @@ export function hh(tag: string, data?: Record<string, any> | null, children?: st
processedChildren = Object.fromEntries( processedChildren = Object.fromEntries(
Object.entries(children).map(([name, slotFn]) => [ Object.entries(children).map(([name, slotFn]) => [
name, name,
(args) => renderSlot(slotFn, args, ctx) (args) => renderSlot(slotFn, args, renderFunction)
]) ])
) )
} }
@@ -121,13 +122,15 @@ export function hh(tag: string, data?: Record<string, any> | null, children?: st
Object.entries(children).map(([name, slotFn]) => [ Object.entries(children).map(([name, slotFn]) => [
name, name,
(args) => { (args) => {
return renderSlot(slotFn, args); return renderSlot(slotFn, args, renderFunction);
} }
]) ])
); );
} }
} }
console.debug("Processed", processedTag, processedData, processedChildren); console.debug("Processed", {
return h(processedTag, processedData, processedChildren); processedTag, processedData, processedChildren
});
return render(processedTag, processedData, processedChildren);
} }

View File

@@ -1,12 +1,14 @@
import { import {
defineComponent, defineComponent,
onBeforeUpdate, onMounted,
onUnmounted, onUnmounted,
ref,
watch, watch,
type PropType type PropType
} from "vue-demi"; } from "vue-demi";
import { ModContext } from "../types/ModContext"; import { ModContext } from "../types/ModContext";
import { InvokerItem } from "./InvokerItem"; import { InvokerItem } from "./InvokerItem";
import { modPageConfig } from "../modPageConfig";
let idCount = 0; let idCount = 0;
export type InvokerModName = string; export type InvokerModName = string;
@@ -37,9 +39,13 @@ export default defineComponent({
const id = ++idCount; const id = ++idCount;
/** vue实例上下文此处获取的是Receiver的vue组件实例 */ /** vue实例上下文此处获取的是Receiver的vue组件实例 */
let receiver: ModContext | null = null; let receiver: ModContext | null = null;
const invokerKey = modPageConfig.Invoker.invokerKey;
const url = ref<string | undefined>('');
window["MES_MOD_INVOKERS"] ||= {}; console.log(`[Mod-Invoker] setting up invoker, id: ${id}, name: ${props.name}, invokerKey: ${invokerKey}`);
window["MES_MOD_INVOKERS"][id] = {
window[invokerKey] ||= {};
window[invokerKey][id] = {
getRenderContext, getRenderContext,
initFinish, initFinish,
receiver, receiver,
@@ -64,7 +70,7 @@ export default defineComponent({
/** 当Receiver初始化完毕后触发 */ /** 当Receiver初始化完毕后触发 */
function initFinish(context: ModContext) { function initFinish(context: ModContext) {
receiver = context; receiver = context;
console.log('[Mod-Invoker]initFinish') console.log('[Mod-Invoker] initFinish')
} }
function getRefs() { function getRefs() {
@@ -92,11 +98,11 @@ export default defineComponent({
} }
); );
onBeforeUpdate(() => { onMounted(async () => {
url.value = await modPageConfig.Invoker.GetModUrl?.(props.name ?? '', id.toString(), invokerKey);
})
});
onUnmounted(() => { onUnmounted(() => {
console.log("[Mod-Invoker] invoker-destroyed");
emit('destroyed'); emit('destroyed');
}); });
@@ -107,7 +113,7 @@ export default defineComponent({
return ( return (
<iframe <iframe
src={props.url.replace('{id}',id.toString())} src={url.value}
scrolling={props.scroll ? "yes" : "no"} scrolling={props.scroll ? "yes" : "no"}
style={`border: none; width: 100%; height: ${props.height}; `} style={`border: none; width: 100%; height: ${props.height}; `}
></iframe> ></iframe>

View File

@@ -1,94 +1,7 @@
import { defineComponent, nextTick, onMounted, ref, getCurrentInstance, isVue3 } from 'vue-demi'; import { defineComponent, nextTick, onMounted, ref, getCurrentInstance, isVue3, h } from 'vue-demi';
import { hh } from './DemiHelper'; import { hh } from './DemiHelper';
import { InvokerContext } from '../types/InvokerContext'; import { InvokerContext } from '../types/InvokerContext';
console.log('receiver-loaded');
function renderContext(item: object | Array<any>, toSlot: boolean = false) {
/**
* 对象为字符串的情况
* # { tag: 'div', attrs: undefined, children: 'hhh' } => h('span', undefined, 'hhh')
*/
if (typeof item === 'string')
return item;
/**
* 对象为函数的情况
* # { tag: 'div', attrs: undefined, children: () => 'hhh' } => h('span', undefined, 'hhh')
*/
if (typeof (item) === "function") {
// return item;
return item();
}
/**
* 对象为数组的情况
* # { tag: 'div', attrs: undefined, [ { tag: 'span', attrs: undefined, 'hhh' } ] } => h('div', undefined, [ h('span', undefined, 'hhh') ])
*/
if (Array.isArray(item)) {
const list = item.map(i => {
if (isVue3) {
// const comp = resolveComponent(i.tag);
return hh(i.tag, i.attrs, i.children);
// return hDemi(comp, i.attrs, renderContext(i.children, typeof (comp) !== 'string') as any); // vue3
}
// const { attrs, listeners, ref } = splitAttrs(i.attrs);
const slots = renderContext(i.children);
if (typeof (slots) === "object") {
return hh(i.tag, {
attrs: i.attrs
// attrs,
// on: listeners,
// scopedSlots: slots,
// ref
});
}
return hh(i.tag, {
attrs: i.attrs,
}, slots);
});
if (toSlot) {
return () => list;
}
return list;
}
/**
* 复合对象的情况
* #
*/
const children: Record<string, any> = {};
for (const key in item) {
children[key] = function (scope) {
const child = item[key](scope);
if (isVue3) {
return hh(child.tag, child.attrs, renderContext(child.children, true));
}
if (Array.isArray(child)) {
const slots = renderContext(child);
return slots;
} else {
// const { attrs, listeners, ref } = splitAttrs(child.attrs);
const slots = renderContext(child.children);
return hh(child.tag, {
attrs: child.attrs
// attrs,
// on: listeners,
// ref
}, slots);
}
};
}
return children;
}
export default defineComponent({ export default defineComponent({
name: 'Receiver', name: 'Receiver',
props: { props: {
@@ -98,13 +11,14 @@ export default defineComponent({
}, },
parentKey: { parentKey: {
type: String, type: String,
default: () => 'modInvoker' default: () => 'MES_MOD_INVOKERS'
} }
}, },
setup(props) { setup(props) {
const instance = getCurrentInstance(); const instance = getCurrentInstance();
let invokerContext: InvokerContext = null!; let invokerContext: InvokerContext = null!;
const renderVersion = ref(0); const renderVersion = ref(0);
console.log(`[Mod-Receiver] receiver setup, invokerId: ${props.parentId}, invokerKey: ${props.parentKey}`);
if (window.parent != window) { if (window.parent != window) {
const invoker = window.parent[props.parentKey][props.parentId]; const invoker = window.parent[props.parentKey][props.parentId];
invokerContext = invoker; invokerContext = invoker;
@@ -124,11 +38,12 @@ export default defineComponent({
return renderVersion.value; return renderVersion.value;
}, },
Update: function (): void { Update: function (): void {
console.log('[Receiver] Force Update', renderVersion.value); console.log('[Mod-Receiver] force update', renderVersion.value);
renderVersion.value += 1; renderVersion.value += 1;
renderItems.value = invokerContext.getRenderContext() ?? []; renderItems.value = invokerContext.getRenderContext() ?? [];
instance?.proxy?.$forceUpdate(); instance?.proxy?.$forceUpdate();
}, },
// @ts-ignore
get refs() { get refs() {
return instance?.proxy?.$refs; return instance?.proxy?.$refs;
} }
@@ -136,21 +51,22 @@ export default defineComponent({
} }
}); });
}); });
return () => { return (h) => {
try { try {
console.log('receiver-update', renderVersion.value);
if (renderVersion.value < 0) return; if (renderVersion.value < 0) return;
if (invokerContext.getRenderContext) { if (invokerContext.getRenderContext) {
const itemList = invokerContext.getRenderContext() ?? []; const itemList = invokerContext.getRenderContext() ?? [];
// const list = renderContext(itemList); // const list = renderContext(itemList);
// console.log('item-list', list); console.log("[Mod-Receiver] rendering: ", itemList);
return hh('div', undefined, itemList); // @ts-ignore
return hh('div', undefined, itemList, h);
} }
return hh('div'); console.log("[Mod-Receiver] 'getRenderContext' method not found");
return hh('div', undefined, undefined, h);
} }
catch (e) { catch (e) {
console.error("[Receiver] 渲染节点过程中出现错误", e); console.error("[Mod-Receiver] 渲染节点过程中出现错误", e);
} }
}; };
} }

View File

@@ -1,5 +1,19 @@
import { createApp, Vue2 } from 'vue-demi' import { createApp, Vue2 } from 'vue-demi'
import App from './App.vue' import App from './App.vue'
import { ConfigureModPage } from './modPageConfig'
ConfigureModPage({
Invoker: {
GetModUrl: (modName, invokerId, invokerKey) => {
const query: Record<string, string> = {
id: invokerId,
key: invokerKey
};
const searchParam = new URLSearchParams(query);
return `/child.html#/?${searchParam.toString()}`
}
}
})
const app = createApp(App) const app = createApp(App)
app.mount('#app') app.mount('#app')

View File

@@ -3,17 +3,29 @@ import { InvokerModName } from "./components/Invoker";
let _modPageConfig = { let _modPageConfig = {
Invoker: { Invoker: {
invokerKey: "MES_MOD_INVOKERS",
/** Invoker中计算ModUrl的方法 */ /** Invoker中计算ModUrl的方法 */
GetModUrl: undefined as ((modName: InvokerModName) => string | Promise<string>) | undefined, GetModUrl: undefined as ((modName: InvokerModName, invokerId: string, invokerKey: string) => string | Promise<string>) | undefined,
} }
} }
export function ConfigureModPage(config: typeof _modPageConfig) { export function ConfigureModPage(config: DeepPartial<typeof _modPageConfig>) {
Object.assign(_modPageConfig, { Object.assign(_modPageConfig, {
..._modPageConfig,
...config,
Invoker: { Invoker: {
...config.Invoker, ..._modPageConfig.Invoker,
...config?.Invoker
} }
}); });
} }
export const modPageConfig: DeepReadonly<typeof _modPageConfig> = _modPageConfig; export const modPageConfig: DeepReadonly<typeof _modPageConfig> = _modPageConfig;
type DeepPartial<T> = T extends Function // 排除Function因为Function也是object
? T
: T extends object
? {
[P in keyof T]?: DeepPartial<T[P]>;
}
: T;