Skip to content

Commit

Permalink
feat: 加上函数式组件调用支持(使用动态组件方式生成组件)
Browse files Browse the repository at this point in the history
  • Loading branch information
yuntian001 committed Jan 13, 2024
1 parent 4cd1ad4 commit 3e18778
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 7 deletions.
6 changes: 5 additions & 1 deletion src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@
<me-component :is="Component" done-progress close-loading="layout"></me-component>
</router-view>
<set-el-globel v-if="showSetElGlobel"></set-el-globel>
<Teleport to="body">
<component :is="item.component" v-for="item in globalComponents" :key="item.key" :ref="(vnode:any)=>item.vnode = vnode" v-bind="item.props"></component>
</Teleport>
</el-config-provider>
</template>
<script setup lang="ts">
import { useSettingStore } from '@/store';
import { useGlobalStore, useSettingStore } from '@/store';
import { SizeEnum } from '@/dict/configEnum';
import SetElGlobel from './setElGlobel';
const settingStore = useSettingStore();
const htmlDom = document.getElementsByTagName('html')[0];
const { globalComponents } = toRefs(useGlobalStore());
watchEffect(() => {
Object.entries(SizeEnum).forEach(([, value]) => {
htmlDom.classList.remove('me-' + value);
Expand Down
8 changes: 8 additions & 0 deletions src/router/routes/example/8-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { RouteRecordRaw } from 'vue-router';
export const routes: RouteRecordRaw[] = [
{
path: 'service',
component: () => import('@/views/example/service/index.vue'),
meta: { title: '函数式组件(动态组件方式生成)' },
},
];
35 changes: 35 additions & 0 deletions src/store/modules/global.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Composer } from 'vue-i18n';
import { event, mitter } from '@/event';
import { Component, VNode } from 'vue';
const WIDTH = 992; // refer to Bootstrap's responsive design
const isMobile = ref(window.document.body.offsetWidth < WIDTH);
mitter.on(event.RESIZE, () => {
Expand All @@ -26,6 +27,40 @@ export default defineStore('global', {
}),
isMobile,
layoutLoaded: false,
globalComponents: [] as Array<{
component: Component | VNode;
key: number | string | symbol;
props?: Record<string, any>;
vnode?: Component;
}>, //全局组件会渲染到app下
};
},
actions: {
addGlobalComponents(component: Component | VNode, props?: Record<string, any>, key?: number | string | symbol) {
if (!key) {
key = Symbol('componentKey');
}
this.globalComponents.push({
component: markRaw(component),
props,
key,
});
return key;
},
removeGlobalComponents(key: number | string | symbol) {
const index = this.globalComponents.findIndex((item) => item.key === key);
if (index > -1) {
this.globalComponents.splice(index, 1);
return true;
}
return false;
},
getVnode<T extends Component>(key: number | string | symbol) {
const index = this.globalComponents.findIndex((item) => item.key === key);
if (index > -1) {
return this.globalComponents[index].vnode as T;
}
return undefined;
},
},
});
21 changes: 21 additions & 0 deletions src/views/example/service/components/add.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useGlobalStore } from '@/store';
import Add from './add.vue';
export default async (props: Omit<ComponentProps<typeof Add>, 'show'> = {}) => {
const show = ref(true);
const globalStore = useGlobalStore();
const key = globalStore.addGlobalComponents(
Add,
computed(() => ({
show: show.value,
...props,
['onUpdate:show']: (value: boolean) => {
show.value = value;
},
onClosed: () => {
globalStore.removeGlobalComponents(key); //关闭时移除当前组件
props.onClosed && props.onClosed();
},
})),
);
await nextTick();
};
11 changes: 11 additions & 0 deletions src/views/example/service/components/add.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<template>
<el-dialog v-model="show" @closed="$emit('closed')">
<div>这是测试弹窗 <el-button @click="add()"> 再次打开弹窗 </el-button></div>
</el-dialog>
</template>

<script setup lang="ts" name="Add">
import add from './add';
const show = defineModel<boolean>('show');
defineEmits<{ closed: [] }>();
</script>
11 changes: 11 additions & 0 deletions src/views/example/service/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<template>
<div class="index">
点击按钮,将以函数调用方式打开组件,函数调用组件会渲染到body中。 设计成动态组件方式而非vnode方式是因为vnode插入方式脱离当前app根组件,无法使用vue-deetools调试
<br />
<el-button @click="add()"> 以函数方式打开弹窗</el-button>
</div>
</template>

<script setup lang="ts" name="Index">
import add from './components/add';
</script>
1 change: 0 additions & 1 deletion types/auto-imports.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ export {}
declare global {
const EffectScope: typeof import('vue')['EffectScope']
const ElLoading: typeof import('element-plus/es')['ElLoading']
const ElMessageBox: typeof import('element-plus/es')['ElMessageBox']
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
Expand Down
7 changes: 2 additions & 5 deletions types/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,8 @@ declare global {
/* eslint-disable */

type ComponentProps<Component> = {
-readonly [K in keyof Omit<
InstanceType<Component>['$props'],
keyof InstanceType<DefineComponent>['$props']
>]: InstanceType<Component>['$props'][K];
};
-readonly [K in keyof Omit<InstanceType<Component>['$props'], keyof InstanceType<DefineComponent>['$props']>]: InstanceType<Component>['$props'][K];
} & { [key: `on${Capitalize<string>}`]: (...args: any[]) => any };

type KeyOfMap<T extends Map> = Parameters<T['get']>[0];

Expand Down

0 comments on commit 3e18778

Please sign in to comment.