Compare commits
13 Commits
fa8b468c32
...
main
Author | SHA1 | Date | |
---|---|---|---|
6ed8fd1ddc | |||
49afb5e3a6 | |||
326ef4ec77 | |||
1eae4e0029 | |||
da4e889ffa | |||
1fffd6f155 | |||
d851f951cd | |||
9cc1e91c91 | |||
935b69e939 | |||
89a1b631de | |||
0c75dca9c3 | |||
5d2af32d68 | |||
18582016b2 |
33
README.md
33
README.md
@@ -46,3 +46,36 @@
|
|||||||
|
|
||||||
1. 主项目和子项目需要同源,或是配置好了CORS策略
|
1. 主项目和子项目需要同源,或是配置好了CORS策略
|
||||||
2. 该库对Vue2.7的支持有限
|
2. 该库对Vue2.7的支持有限
|
||||||
|
3. 使用引入模块的方式渲染带有插槽的组件会导致错误,原因未知:
|
||||||
|
组件`JumpBtn.vue`
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<button @click="HandleClick">
|
||||||
|
<slot></slot>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
渲染函数
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import JumpBtn from './JumpBtn.vue';
|
||||||
|
hh(JumpBtn, null, "插槽内容"); // 会导致错误
|
||||||
|
```
|
||||||
|
|
||||||
|
若注册为全局组件并在调用渲染函数时使用组件标签名,则不会错误
|
||||||
|
`main.ts`
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import JumpBtn from './JumpBtn.vue';
|
||||||
|
app.component('jump-btn', JumpBtn);
|
||||||
|
```
|
||||||
|
|
||||||
|
渲染函数
|
||||||
|
|
||||||
|
```ts
|
||||||
|
h('jump-btn', null, "插槽内容"); // 并不会报错
|
||||||
|
```
|
||||||
|
11
index.ts
11
index.ts
@@ -1,11 +0,0 @@
|
|||||||
import Invoker from "./src/components/Invoker";
|
|
||||||
import { hh } from './src/components/DemiHelper';
|
|
||||||
import { InvokerItem } from "./src/components/InvokerItem";
|
|
||||||
import Receiver from "./src/components/Receiver";
|
|
||||||
|
|
||||||
export {
|
|
||||||
Invoker,
|
|
||||||
Receiver,
|
|
||||||
type InvokerItem,
|
|
||||||
hh,
|
|
||||||
}
|
|
18
package.json
18
package.json
@@ -1,11 +1,24 @@
|
|||||||
{
|
{
|
||||||
"name": "vue-modpage",
|
"name": "vue-modpage",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.0.0",
|
"version": "1.0.11",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"files": [
|
||||||
|
"public",
|
||||||
|
"src",
|
||||||
|
"README.md"
|
||||||
|
],
|
||||||
|
"main": "src/index.ts",
|
||||||
|
"module": "src/index.ts",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"import": "./src/index.ts",
|
||||||
|
"require": "./src/index.ts"
|
||||||
|
}
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vue-tsc -b && vite build",
|
"build": "vue-tsc --skipLibCheck && vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"switch:2": "vue-demi-switch 2.7 vue2",
|
"switch:2": "vue-demi-switch 2.7 vue2",
|
||||||
"switch:3": "vue-demi-switch 3",
|
"switch:3": "vue-demi-switch 3",
|
||||||
@@ -24,6 +37,7 @@
|
|||||||
"@vitejs/plugin-vue-jsx": "^4.1.1",
|
"@vitejs/plugin-vue-jsx": "^4.1.1",
|
||||||
"@vitejs/plugin-vue2": "^2.2.0",
|
"@vitejs/plugin-vue2": "^2.2.0",
|
||||||
"@vitejs/plugin-vue2-jsx": "^1.1.0",
|
"@vitejs/plugin-vue2-jsx": "^1.1.0",
|
||||||
|
"npm-run-all": "^4.1.5",
|
||||||
"typescript": "~5.6.2",
|
"typescript": "~5.6.2",
|
||||||
"vite": "^6.0.1",
|
"vite": "^6.0.1",
|
||||||
"vitest": "^2.1.8",
|
"vitest": "^2.1.8",
|
||||||
|
2430
pnpm-lock.yaml
generated
2430
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
39
src/App.vue
39
src/App.vue
@@ -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>
|
||||||
@@ -11,6 +11,7 @@ import { onMounted, version, ref, h } from 'vue-demi';
|
|||||||
import Invoker from './components/Invoker'
|
import Invoker from './components/Invoker'
|
||||||
import JumpBtn from './JumpBtn.vue';
|
import JumpBtn from './JumpBtn.vue';
|
||||||
import SlotTester from './SlotTester.vue';
|
import SlotTester from './SlotTester.vue';
|
||||||
|
import { InvokerItem } from './components/InvokerItem';
|
||||||
|
|
||||||
// Vue3
|
// Vue3
|
||||||
const dom = ref(h(SlotTester, null, { default: () => [
|
const dom = ref(h(SlotTester, null, { default: () => [
|
||||||
@@ -21,53 +22,59 @@ const dom = ref(h(SlotTester, null, { default: () => [
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
console.log(test.value);
|
console.log(testRef.value);
|
||||||
}, 3000);
|
}, 3000);
|
||||||
});
|
});
|
||||||
|
|
||||||
const test = ref<HTMLDivElement>();
|
const testRef = ref<HTMLDivElement>();
|
||||||
const vueVer = version;
|
const vueVer = version;
|
||||||
const items = [
|
const items = [
|
||||||
// 原生元素渲染测试
|
// 原生元素渲染测试
|
||||||
{
|
{
|
||||||
tag: 'div', data: { ref: test, style: { backgroundColor: 'gray', padding: '10px' } }, children: '字符串渲染测试' // 字符串测试 -- OK
|
tag: 'div', data: { style: { backgroundColor: 'gray', padding: '10px' } }, children: '1. 字符串渲染测试'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tag: 'div', data: { style: { background: 'cyan', padding: '10px' } }, children: [
|
tag: 'div', data: { style: { background: 'cyan', padding: '10px' } }, children: [
|
||||||
{ tag: 'span', data: null, children: "嵌套渲染测试1" },
|
{ tag: 'div', data: null, children: "2. 嵌套渲染测试 I" },
|
||||||
{ tag: 'span', data: null, children: "嵌套渲染测试2" }
|
{ tag: 'div', data: null, children: "3. 嵌套渲染测试 II" }
|
||||||
] // 子组件列表测试 -- OK
|
] // 子组件列表测试
|
||||||
},
|
},
|
||||||
// 组件渲染测试
|
// 组件渲染测试
|
||||||
{
|
{
|
||||||
tag: 'slot-tester', data: { padding: '10px', style: { backgroundColor: 'red' } }, children: {
|
tag: 'slot-tester', data: { padding: '10px', style: { backgroundColor: 'red' } }, children: {
|
||||||
default: () => {
|
default: () => {
|
||||||
return [
|
return [
|
||||||
{ tag: 'div', data: { style: { height: '25px', padding: '10px', backgroundColor: 'green' } }, children: "默认插槽嵌套渲染" }, // 一层嵌套
|
{ tag: 'div', data: { style: { height: '25px', padding: '10px', backgroundColor: 'green' } }, children: "4. 默认插槽嵌套渲染" }, // 一层嵌套
|
||||||
{ tag: 'div', data: { style: { height: '25px', padding: '10px', backgroundColor: 'blue' } }, children: [
|
{ tag: 'div', data: { style: { height: '25px', padding: '10px', backgroundColor: 'blue' } }, children: [
|
||||||
{ tag: 'div', data: { style: { height: '25px', backgroundColor: 'yellow' } }, children: "默认插槽嵌套二层渲染" } // 二层嵌套
|
{ tag: 'div', data: { style: { height: '25px', backgroundColor: 'yellow' } }, children: "5. 默认插槽嵌套二层渲染" } // 二层嵌套
|
||||||
] } // 三层嵌套
|
] } // 三层嵌套
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
title: () => "具名插槽渲染测试"
|
title: () => "6. 具名插槽渲染测试"
|
||||||
} // 具名插槽测试
|
} // 具名插槽测试
|
||||||
},
|
},
|
||||||
|
// {
|
||||||
|
// tag: JumpBtn,
|
||||||
|
// data: { url: 'https://ai.com'}, // 组件传参测试
|
||||||
|
// children: "7. 组件插槽测试" // 组件插槽测试
|
||||||
|
// },
|
||||||
{
|
{
|
||||||
tag: JumpBtn,
|
tag: 'div',
|
||||||
data: { url: 'https://ai.com'} // 组件传参测试 -- OK
|
children: '7.1 标签插槽测试'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tag: 'test-parent',
|
tag: 'test-parent',
|
||||||
data: {
|
data: {
|
||||||
message: 'test-parent',
|
ref: testRef,
|
||||||
|
message: '8. 事件调用测试',
|
||||||
style: 'color: red',
|
style: 'color: red',
|
||||||
onCallback: (msg) => { // 事件测试(可能要测试NativeOn?)-- OK
|
onCallback: (msg) => { // 事件测试(可能要测试NativeOn?)
|
||||||
alert(msg);
|
alert(msg);
|
||||||
console.log(test.value)
|
console.log(testRef.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
];
|
] as InvokerItem[];
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<button @click="HandleClick">事件测试</button>
|
<button @click="HandleClick"><slot></slot></button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import * as Vue3 from "vue";
|
import * as Vue3 from "vue";
|
||||||
import { h, isVue2, isVue3, type VNode, type VNodeChildren, type VNodeData } from "vue-demi";
|
// @ts-ignore
|
||||||
|
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> = {};
|
||||||
@@ -24,16 +24,17 @@ function splitAttrs(obj: object) : { attrs: Record<string, any>, on: Record<stri
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
function splitVue2Data(rawData?: Record<string, any> | null) {
|
function splitVue2Data(rawData?: Record<string, any> | null) {
|
||||||
console.warn("Handling Vue2 data: ", rawData);
|
console.log("[ModPage Render Function] Handling Vue2 data: ", rawData);
|
||||||
if (!rawData) return {};
|
if (!rawData) return null;
|
||||||
|
|
||||||
if (isVue3) throw new Error("Vue3 data object is not supported in Vue2");
|
if (isVue3) throw new Error("Vue3 data object is not supported in Vue2");
|
||||||
// Vue2分离式处理
|
// Vue2分离式处理
|
||||||
else {
|
else {
|
||||||
const on = {}; // 可能需要区分NativeOn
|
const on = {}; // 可能需要区分NativeOn
|
||||||
const { class: cls, style, attrs = {}, props = {}, ...rest } = rawData;
|
// 从 rawData 中显式提取 ref,其余的放入 rest
|
||||||
|
const { class: cls, style, attrs = {}, props = {}, ref: v2Ref, ...rest } = rawData;
|
||||||
|
|
||||||
// 处理事件
|
// 处理事件:从 rest 中提取事件处理器到 on 对象
|
||||||
for (const key in rest) {
|
for (const key in rest) {
|
||||||
if (key.indexOf('on') == 0 && key != 'on') {
|
if (key.indexOf('on') == 0 && key != 'on') {
|
||||||
const newKey = key[2].toLowerCase() + key.substring(3);
|
const newKey = key[2].toLowerCase() + key.substring(3);
|
||||||
@@ -44,8 +45,10 @@ function splitVue2Data(rawData?: Record<string, any> | null) {
|
|||||||
class: cls,
|
class: cls,
|
||||||
style,
|
style,
|
||||||
attrs: attrs,
|
attrs: attrs,
|
||||||
props: { ...props, ...rest }, // 未明确的属性放入props
|
// props 现在由原始 props 和 rest 中剩余的非事件、非 ref 属性组成
|
||||||
|
props: { ...props, ...rest },
|
||||||
on,
|
on,
|
||||||
|
ref: v2Ref, // 将提取的 ref 放置在 v2data 的顶层
|
||||||
}
|
}
|
||||||
return v2data;
|
return v2data;
|
||||||
}
|
}
|
||||||
@@ -57,7 +60,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);
|
||||||
// 字符串情况
|
// 字符串情况
|
||||||
@@ -66,17 +69,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;
|
||||||
@@ -102,7 +106,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') {
|
||||||
@@ -110,7 +115,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)
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -120,13 +125,17 @@ 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
|
||||||
|
// });
|
||||||
|
const result = render(processedTag, processedData, processedChildren);
|
||||||
|
console.log("[ModPage Render Function] result: ", result);
|
||||||
|
return result;
|
||||||
}
|
}
|
123
src/components/Invoker.ts
Normal file
123
src/components/Invoker.ts
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
import {
|
||||||
|
defineComponent,
|
||||||
|
h,
|
||||||
|
isVue2,
|
||||||
|
markRaw,
|
||||||
|
onMounted,
|
||||||
|
onUnmounted,
|
||||||
|
ref,
|
||||||
|
watch,
|
||||||
|
type PropType
|
||||||
|
} from "vue-demi";
|
||||||
|
import { ModContext } from "../types/ModContext";
|
||||||
|
import { InvokerItem } from "./InvokerItem";
|
||||||
|
import { modPageConfig } from "../modPageConfig";
|
||||||
|
import { hh } from "./DemiHelper";
|
||||||
|
|
||||||
|
let idCount = 0;
|
||||||
|
export type InvokerModName = string;
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
height: {
|
||||||
|
type: String,
|
||||||
|
default: () => "auto",
|
||||||
|
},
|
||||||
|
scroll: {
|
||||||
|
type: Boolean,
|
||||||
|
default: () => false,
|
||||||
|
},
|
||||||
|
items: {
|
||||||
|
type: Array,
|
||||||
|
default: () => undefined as InvokerItem[] | undefined,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: String as PropType<InvokerModName> | undefined,
|
||||||
|
default: () => undefined,
|
||||||
|
required: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ["destroyed", "load"],
|
||||||
|
setup(props, { slots, expose, emit }) {
|
||||||
|
/** Invoker实例ID */
|
||||||
|
const id = ++idCount;
|
||||||
|
/** vue实例上下文,此处获取的是Receiver的vue组件实例 */
|
||||||
|
let receiver: ModContext | null = null;
|
||||||
|
const invokerKey = modPageConfig.Invoker.invokerKey;
|
||||||
|
const url = ref<string | undefined>('');
|
||||||
|
|
||||||
|
console.log(`[Mod-Invoker] 1. setting up invoker, id: ${id}, name: ${props.name}, invokerKey: ${invokerKey}`);
|
||||||
|
|
||||||
|
window[invokerKey] ||= {};
|
||||||
|
window[invokerKey][id] = {
|
||||||
|
getRenderContext,
|
||||||
|
initFinish,
|
||||||
|
getReceiver() {
|
||||||
|
return receiver;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** 主动更新 */
|
||||||
|
function updateComponent() {
|
||||||
|
// 判断子页面vue对象是否存在
|
||||||
|
receiver?.Update(); // 强制渲染
|
||||||
|
console.log("[Mod-Invoker] update component");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 当Receiver初始化完毕后由它触发 */
|
||||||
|
function initFinish(context: ModContext) {
|
||||||
|
receiver = context;
|
||||||
|
console.log('[Mod-Invoker] 3.1. invoker init finished', receiver);
|
||||||
|
emit('load');
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRefs() {
|
||||||
|
const refs = receiver?.refs;
|
||||||
|
if (!refs) throw new Error("[Mod-Invoker] Receiver not found");
|
||||||
|
return refs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// vue3 渲染上下文
|
||||||
|
function getRenderContext() {
|
||||||
|
return markRaw(props.items);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 用expose外部没有感知,用defineExpose在vue2会报错,没办法
|
||||||
|
expose({
|
||||||
|
getRefs,
|
||||||
|
getRenderContext,
|
||||||
|
initFinish,
|
||||||
|
updateComponent
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.items,
|
||||||
|
() => {
|
||||||
|
updateComponent();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
url.value = await modPageConfig.Invoker.GetModUrl?.(props.name ?? '', id.toString(), invokerKey);
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
emit('destroyed');
|
||||||
|
});
|
||||||
|
|
||||||
|
return (createElem) => {
|
||||||
|
const render = isVue2 ? createElem : h;
|
||||||
|
if (slots.default) {
|
||||||
|
return render('div', {}, slots.default());
|
||||||
|
}
|
||||||
|
|
||||||
|
return hh('iframe', {
|
||||||
|
src: url.value,
|
||||||
|
scrolling: props.scroll ? "yes" : "no",
|
||||||
|
style: `border: none; width: 100%; height: ${props.height}; `
|
||||||
|
});
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
@@ -1,152 +0,0 @@
|
|||||||
import {
|
|
||||||
defineComponent,
|
|
||||||
onBeforeUpdate,
|
|
||||||
onUnmounted,
|
|
||||||
watch,
|
|
||||||
} from "vue-demi";
|
|
||||||
|
|
||||||
let idCount = 0 ;
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
props: {
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
height: {
|
|
||||||
type: String,
|
|
||||||
default: () => "auto",
|
|
||||||
},
|
|
||||||
scroll: {
|
|
||||||
type: Boolean,
|
|
||||||
default: () => false,
|
|
||||||
},
|
|
||||||
items: {
|
|
||||||
type: Array ,
|
|
||||||
default: () => undefined,
|
|
||||||
},
|
|
||||||
url: {
|
|
||||||
type: String,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup(props, { slots, expose }) {
|
|
||||||
// const { proxy } = getCurrentInstance();
|
|
||||||
//
|
|
||||||
// const id = modService.addInvoker(proxy);
|
|
||||||
const id = ++idCount;
|
|
||||||
const isLocalInvoker = false;
|
|
||||||
let modContext: any = null;
|
|
||||||
const invokeMethods: { method: string; args: any[] }[] = [];
|
|
||||||
let isTemplate = false;
|
|
||||||
|
|
||||||
window["MES_MOD_INVOKERS"] ||= {};
|
|
||||||
window["MES_MOD_INVOKERS"][id] = {
|
|
||||||
getRenderContext,
|
|
||||||
initFinish,
|
|
||||||
modContext,
|
|
||||||
};
|
|
||||||
|
|
||||||
function updateComponent() {
|
|
||||||
if (modContext) {
|
|
||||||
// 判断子页面vue对象是否存在
|
|
||||||
modContext.renderVersion += 1;
|
|
||||||
modContext.$forceUpdate(); // 强制渲染
|
|
||||||
// 重新计算el-button的computed
|
|
||||||
const list = [modContext];
|
|
||||||
|
|
||||||
while (list.length != 0) {
|
|
||||||
const item = list.pop();
|
|
||||||
list.push(...item.$children);
|
|
||||||
const tag = item.$options._componentTag;
|
|
||||||
|
|
||||||
if (tag == "el-button" || tag == "ElButton") {
|
|
||||||
if (item.disabled) item._computedWatchers.buttonDisabled.run();
|
|
||||||
console.log("[Invoker]: force recalculate computed property...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 子组件调用
|
|
||||||
function eventCallback(type: string, ...args: any[]) {
|
|
||||||
switch (type) {
|
|
||||||
case "created":
|
|
||||||
initFinish(args[0]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function initFinish(context) {
|
|
||||||
modContext = context;
|
|
||||||
let m: { method: string; args: any[] } | undefined;
|
|
||||||
while ((m = invokeMethods.pop())) {
|
|
||||||
invokeMethod(m.method, m.args);
|
|
||||||
}
|
|
||||||
console.log("invoker init finish");
|
|
||||||
}
|
|
||||||
function invokeMethod(
|
|
||||||
method: string,
|
|
||||||
args: any[] = [],
|
|
||||||
callback: ((result: any) => void) | null = null
|
|
||||||
) {
|
|
||||||
// if(!this.component) throw new Error('子组件不支持此方法调用');
|
|
||||||
if (modContext) {
|
|
||||||
console.log("method", modContext.$children);
|
|
||||||
const result = modContext.$children[0][method](...args);
|
|
||||||
if (callback) {
|
|
||||||
callback(result);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
invokeMethods.push({ method, args });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function getRefs() {
|
|
||||||
return modContext.$refs;
|
|
||||||
}
|
|
||||||
|
|
||||||
// vue3 渲染上下文
|
|
||||||
function getRenderContext() {
|
|
||||||
return props.items;
|
|
||||||
}
|
|
||||||
|
|
||||||
expose({
|
|
||||||
getRefs,
|
|
||||||
getRenderContext,
|
|
||||||
eventCallback,
|
|
||||||
initFinish,
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.items,
|
|
||||||
() => {
|
|
||||||
updateComponent();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
onBeforeUpdate(() => {
|
|
||||||
console.log("invoker-beforeUpdate", slots.default);
|
|
||||||
if (isTemplate) {
|
|
||||||
// 只更新slots变更
|
|
||||||
updateComponent();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
onUnmounted(() => {
|
|
||||||
console.log("invoker-destroyed");
|
|
||||||
modContext && modContext.$destroy();
|
|
||||||
});
|
|
||||||
|
|
||||||
return (h) => {
|
|
||||||
if (isLocalInvoker && slots.default) {
|
|
||||||
return <div>{slots.default()}</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<iframe
|
|
||||||
src={props.url.replace('{id}',id.toString())}
|
|
||||||
scrolling={props.scroll ? "yes" : "no"}
|
|
||||||
style={`border: none; width: 100%; height: ${props.height}; `}
|
|
||||||
></iframe>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
@@ -1,6 +1,6 @@
|
|||||||
export interface InvokerItem {
|
export interface InvokerItem {
|
||||||
/** 组件的标签或是Component对象 */
|
/** 组件的标签或是Component对象 */
|
||||||
tag: string,
|
tag: string | object,
|
||||||
/** 组件数据对象,包含attr, props, class, style, 事件等对象 */
|
/** 组件数据对象,包含attr, props, class, style, 事件等对象 */
|
||||||
data: Record<string, any> | null,
|
data: Record<string, any> | null,
|
||||||
/** 子节点和插槽 */
|
/** 子节点和插槽 */
|
||||||
|
@@ -1,135 +1,73 @@
|
|||||||
import { defineComponent, nextTick, onMounted, ref, getCurrentInstance, isVue3, h } from 'vue-demi';
|
import { defineComponent, nextTick, onMounted, ref, getCurrentInstance, isVue3, h, isVue2 } from 'vue-demi';
|
||||||
import { hh } from './DemiHelper';
|
import { hh } from './DemiHelper';
|
||||||
import SlotTester from '../SlotTester.vue';
|
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: {
|
||||||
parentId: {
|
parentId: {
|
||||||
type: [Number, String],
|
type: [Number, String],
|
||||||
default: () => 0
|
default: () => 0
|
||||||
|
},
|
||||||
|
parentKey: {
|
||||||
|
type: String,
|
||||||
|
default: () => 'MES_MOD_INVOKERS'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const instance = getCurrentInstance();
|
const instance = getCurrentInstance();
|
||||||
let invokerContext: any = null;
|
let invokerContext: InvokerContext = null!;
|
||||||
const renderVersion = ref(0);
|
const renderVersion = ref(0);
|
||||||
|
console.log(`[Mod-Receiver] 2. receiver setup, invokerId: ${props.parentId}, invokerKey: ${props.parentKey}`);
|
||||||
if (window.parent != window) {
|
if (window.parent != window) {
|
||||||
const invoker = window.parent['MES_MOD_INVOKERS'][props.parentId];
|
const invoker = window.parent[props.parentKey][props.parentId];
|
||||||
invokerContext = invoker;
|
invokerContext = invoker;
|
||||||
|
console.log("[Mod-Receiver] 3. got invoker context", invokerContext);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new Error("[Mod-Receiver] the receiver must be in an iframe");
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
const renderItems = ref(invokerContext.getRenderContext() ?? []);
|
||||||
nextTick().then(() => {
|
|
||||||
console.log('mounted-finish');
|
// 若Invoker未接收到当前实例(首次初始化),则进行初始化
|
||||||
if (!invokerContext['modContext']) {
|
onMounted(function () {
|
||||||
invokerContext.initFinish(instance?.proxy);
|
if (!invokerContext.receiver) {
|
||||||
}
|
invokerContext.initFinish({
|
||||||
});
|
get renderVersion() {
|
||||||
});
|
return renderVersion.value;
|
||||||
return () => {
|
},
|
||||||
|
Update: function (): void {
|
||||||
|
renderVersion.value += 1;
|
||||||
|
renderItems.value = invokerContext.getRenderContext() ?? [];
|
||||||
|
instance?.proxy?.$forceUpdate();
|
||||||
|
},
|
||||||
|
// @ts-ignore
|
||||||
|
get refs() {
|
||||||
|
return instance?.proxy?.$refs;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
return (createElem) => {
|
||||||
|
const render = isVue2 ? createElem : 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] 4. rendering: ", itemList);
|
||||||
return hh('div', undefined, itemList);
|
// @ts-ignore
|
||||||
|
return hh('div', undefined, itemList, render);
|
||||||
}
|
}
|
||||||
return hh('div');
|
console.warn("[Mod-Receiver] 'getRenderContext' method not found");
|
||||||
|
return hh('div', undefined, undefined, render);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
console.error("[Receiver] 渲染节点过程中出现错误", e);
|
console.error("[Mod-Receiver] 渲染节点过程中出现错误", e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
21
src/index.ts
Normal file
21
src/index.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import Invoker, { InvokerModName } from "./components/Invoker";
|
||||||
|
import { hh } from './components/DemiHelper';
|
||||||
|
import { InvokerItem } from "./components/InvokerItem";
|
||||||
|
import { InvokerContext } from "./types/InvokerContext";
|
||||||
|
import { Vue2Instance } from "./types/Vue2Instance";
|
||||||
|
import { ModContext } from "./types/ModContext";
|
||||||
|
import Receiver from "./components/Receiver";
|
||||||
|
import { ConfigureModPage, modPageConfig } from "./modPageConfig";
|
||||||
|
|
||||||
|
export {
|
||||||
|
modPageConfig,
|
||||||
|
ConfigureModPage,
|
||||||
|
Invoker,
|
||||||
|
Receiver,
|
||||||
|
type InvokerItem,
|
||||||
|
type InvokerContext,
|
||||||
|
type InvokerModName,
|
||||||
|
type Vue2Instance,
|
||||||
|
type ModContext,
|
||||||
|
hh,
|
||||||
|
}
|
14
src/main.ts
14
src/main.ts
@@ -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')
|
||||||
|
31
src/modPageConfig.ts
Normal file
31
src/modPageConfig.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { DeepReadonly } from "vue";
|
||||||
|
import { InvokerModName } from "./components/Invoker";
|
||||||
|
|
||||||
|
let _modPageConfig = {
|
||||||
|
Invoker: {
|
||||||
|
invokerKey: "MES_MOD_INVOKERS",
|
||||||
|
/** Invoker中计算ModUrl的方法 */
|
||||||
|
GetModUrl: undefined as ((modName: InvokerModName, invokerId: string, invokerKey: string) => string | Promise<string>) | undefined,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ConfigureModPage(config: DeepPartial<typeof _modPageConfig>) {
|
||||||
|
Object.assign(_modPageConfig, {
|
||||||
|
..._modPageConfig,
|
||||||
|
...config,
|
||||||
|
Invoker: {
|
||||||
|
..._modPageConfig.Invoker,
|
||||||
|
...config?.Invoker
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
7
src/types/InvokerContext.d.ts
vendored
Normal file
7
src/types/InvokerContext.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { ModContext } from "./ModContext";
|
||||||
|
|
||||||
|
export type InvokerContext = {
|
||||||
|
getRenderContext: () => unknown[] | undefined;
|
||||||
|
initFinish: (ctx: ModContext) => void;
|
||||||
|
receiver: ModContext | null;
|
||||||
|
}
|
16
src/types/ModContext.d.ts
vendored
Normal file
16
src/types/ModContext.d.ts
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { DefineComponent } from "vue";
|
||||||
|
import { Vue2Instance } from "./Vue2Instance";
|
||||||
|
|
||||||
|
export type ModContext = {
|
||||||
|
/** 渲染版本号 */
|
||||||
|
get renderVersion(): number;
|
||||||
|
/** 强制组件进行更新 */
|
||||||
|
Update(): void;
|
||||||
|
get refs(): {
|
||||||
|
[key: string]:
|
||||||
|
| Vue2Instance
|
||||||
|
| DefineComponent
|
||||||
|
| Element
|
||||||
|
| undefined
|
||||||
|
}
|
||||||
|
}
|
63
src/types/Vue2Instance.d.ts
vendored
Normal file
63
src/types/Vue2Instance.d.ts
vendored
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import { ComponentOptions, nextTick, VNode, WatchOptions } from "vue-demi"
|
||||||
|
|
||||||
|
export interface Vue2Instance<
|
||||||
|
Data = Record<string, any>,
|
||||||
|
Props = Record<string, any>,
|
||||||
|
Instance = never,
|
||||||
|
Options = never,
|
||||||
|
Emit = (event: string, ...args: any[]) => Vue2Instance
|
||||||
|
> {
|
||||||
|
// properties with different types in defineComponent()
|
||||||
|
readonly $data: Data
|
||||||
|
readonly $props: Props
|
||||||
|
readonly $parent: NeverFallback<Instance, Vue2Instance> | null
|
||||||
|
readonly $root: NeverFallback<Instance, Vue2Instance>
|
||||||
|
readonly $children: NeverFallback<Instance, Vue2Instance>[]
|
||||||
|
readonly $options: NeverFallback<Options, ComponentOptions<Vue2Instance>>
|
||||||
|
$emit: Emit
|
||||||
|
|
||||||
|
// Vue 2 only or shared
|
||||||
|
readonly $el: Element
|
||||||
|
readonly $refs: {
|
||||||
|
[key: string]:
|
||||||
|
| NeverFallback<Instance, Vue2Instance>
|
||||||
|
| Vue2Instance
|
||||||
|
| Element
|
||||||
|
| (NeverFallback<Instance, Vue2Instance> | Vue2Instance | Element)[]
|
||||||
|
| undefined
|
||||||
|
}
|
||||||
|
readonly $slots: { [key: string]: VNode[] | undefined }
|
||||||
|
readonly $scopedSlots: { [key: string]: NormalizedScopedSlot | undefined }
|
||||||
|
readonly $isServer: boolean
|
||||||
|
|
||||||
|
readonly $ssrContext: any
|
||||||
|
readonly $vnode: VNode
|
||||||
|
readonly $attrs: Record<string, string>
|
||||||
|
readonly $listeners: Record<string, Function | Function[]>
|
||||||
|
|
||||||
|
$mount(elementOrSelector?: Element | string, hydrating?: boolean): this
|
||||||
|
$forceUpdate(): void
|
||||||
|
$destroy(): void
|
||||||
|
$set: Function
|
||||||
|
$delete: Function
|
||||||
|
$watch(
|
||||||
|
expOrFn: string,
|
||||||
|
callback: (this: this, n: any, o: any) => void,
|
||||||
|
options?: WatchOptions
|
||||||
|
): () => void
|
||||||
|
$watch<T>(
|
||||||
|
expOrFn: (this: this) => T,
|
||||||
|
callback: (this: this, n: T, o: T) => void,
|
||||||
|
options?: WatchOptions
|
||||||
|
): () => void
|
||||||
|
$on(event: string | string[], callback: Function): this
|
||||||
|
$once(event: string | string[], callback: Function): this
|
||||||
|
$off(event?: string | string[], callback?: Function): this
|
||||||
|
$nextTick: typeof nextTick
|
||||||
|
$createElement: Function
|
||||||
|
}
|
||||||
|
|
||||||
|
type NeverFallback<T, D> = [T] extends [never] ? D : T
|
||||||
|
|
||||||
|
export type NormalizedScopedSlot = (props: any) => ScopedSlotChildren
|
||||||
|
export type ScopedSlotChildren = VNode[] | undefined
|
@@ -23,5 +23,5 @@
|
|||||||
"noUncheckedSideEffectImports": true,
|
"noUncheckedSideEffectImports": true,
|
||||||
"noImplicitAny": false
|
"noImplicitAny": false
|
||||||
},
|
},
|
||||||
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "index.ts"]
|
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "src/index.ts"]
|
||||||
}
|
}
|
||||||
|
@@ -4,14 +4,14 @@ import vueJsx from '@vitejs/plugin-vue-jsx'
|
|||||||
|
|
||||||
import vue2 from '@vitejs/plugin-vue2'
|
import vue2 from '@vitejs/plugin-vue2'
|
||||||
import vue2Jsx from '@vitejs/plugin-vue2-jsx'
|
import vue2Jsx from '@vitejs/plugin-vue2-jsx'
|
||||||
import { isVue2, isVue3,version } from 'vue-demi'
|
import { isVue2, isVue3, version } from 'vue-demi'
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import { createRequire } from 'node:module'
|
import { createRequire } from 'node:module'
|
||||||
|
|
||||||
const resolve = (str: string) => {
|
const resolve = (str: string) => {
|
||||||
return path.resolve(__dirname, str)
|
return path.resolve(__dirname, str)
|
||||||
}
|
}
|
||||||
console.log('vue',version)
|
console.log('vue', version)
|
||||||
|
|
||||||
function getV2Compiler() {
|
function getV2Compiler() {
|
||||||
const req = createRequire(import.meta.url);
|
const req = createRequire(import.meta.url);
|
||||||
@@ -24,15 +24,15 @@ function getV2Compiler() {
|
|||||||
|
|
||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: isVue3 ? [vue(), vueJsx()] : [vue2({
|
plugins: isVue3 ?
|
||||||
compiler: getV2Compiler()
|
[vue(), vueJsx()] :
|
||||||
}), vue2Jsx()],
|
[vue2({ compiler: getV2Compiler() }), vue2Jsx()],
|
||||||
resolve:{
|
resolve: {
|
||||||
alias:{
|
alias: {
|
||||||
vue: isVue2 ? resolve('./node_modules/vue2') : resolve('./node_modules/vue'),
|
vue: isVue2 ? resolve('./node_modules/vue2') : resolve('./node_modules/vue'),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
optimizeDeps: {
|
optimizeDeps: {
|
||||||
exclude: ['vue-demi']
|
exclude: ['vue-demi']
|
||||||
},
|
}
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user