Skip to content

Commit b4a69f5

Browse files
author
knight.chen
committed
feat(vue-renderer): 优化代码逻辑,移除 lodash 依赖
1 parent 6a0f5b8 commit b4a69f5

File tree

11 files changed

+562
-635
lines changed

11 files changed

+562
-635
lines changed

.eslintrc.cjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ module.exports = {
1313
'vue/prop-name-casing': 'off',
1414
'vue/one-component-per-file': 'off',
1515
'vue/multi-word-component-names': 'off',
16-
'@typescript-eslint/no-unused-vars': 'error',
16+
'@typescript-eslint/no-unused-vars': ['error', { varsIgnorePattern: '_+' }],
1717
'@typescript-eslint/ban-ts-comment': 'off',
1818
'@typescript-eslint/no-explicit-any': 'off',
1919
'@typescript-eslint/no-non-null-assertion': 'off',

packages/vue-renderer/package.json

+2-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"main": "./dist/vue-renderer.js",
44
"module": "./dist/vue-renderer.mjs",
55
"typings": "./dist/vue-renderer.d.ts",
6-
"version": "1.6.0-beta.0",
6+
"version": "1.6.0-beta.1",
77
"keywords": [
88
"vue",
99
"lowcode",
@@ -23,12 +23,10 @@
2323
"@alilc/lowcode-types": "^1.1.2",
2424
"@knxcloud/lowcode-hooks": "workspace:*",
2525
"@knxcloud/lowcode-utils": "workspace:*",
26-
"intl-messageformat": "^10.3.1",
27-
"lodash-es": "^4.17.21"
26+
"intl-messageformat": "^10.3.1"
2827
},
2928
"devDependencies": {
3029
"@alilc/lowcode-designer": "^1.1.2",
31-
"@types/lodash-es": "^4.17.6",
3230
"@vitejs/plugin-vue": "^4.0.0",
3331
"@vitejs/plugin-vue-jsx": "^3.0.0",
3432
"deepmerge": "^4.3.0",

packages/vue-renderer/src/core/base.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ export const leafProps = {
7575
type: Object as PropType<NodeSchema>,
7676
default: () => ({}),
7777
},
78+
__vnodeProps: {
79+
type: Object as PropType<Record<string, unknown>>,
80+
default: () => ({}),
81+
},
7882
} as const;
7983

8084
export interface LeafProps {
@@ -83,7 +87,7 @@ export interface LeafProps {
8387
__schema: NodeSchema;
8488
}
8589

86-
export const leafPropKeys = Object.keys(rendererProps) as (keyof LeafProps)[];
90+
export const leafPropKeys = Object.keys(leafProps) as (keyof LeafProps)[];
8791

8892
export type LeafComponent = {
8993
new (...args: any[]): {

packages/vue-renderer/src/core/hoc.ts

+123-148
Original file line numberDiff line numberDiff line change
@@ -1,170 +1,145 @@
1-
import type { ComponentPublicInstance } from 'vue';
2-
import type { PropSchemaMap, SlotSchemaMap } from './use';
3-
import type { IPublicModelNode } from '@alilc/lowcode-types';
1+
import {
2+
h,
3+
shallowRef,
4+
mergeProps,
5+
type InjectionKey,
6+
inject,
7+
provide,
8+
watch,
9+
} from 'vue';
410

5-
import { IPublicEnumTransformStage as TransformStage } from '@alilc/lowcode-types/es/shell/enum';
6-
import { isJSSlot, isNil } from '@knxcloud/lowcode-utils';
7-
import { useRendererContext } from '@knxcloud/lowcode-hooks';
8-
import { h, Fragment, reactive, onUnmounted, defineComponent } from 'vue';
11+
import { onUnmounted, defineComponent, nextTick } from 'vue';
912
import { leafProps } from './base';
10-
import { ensureArray } from '../utils';
11-
import { useLeaf } from './use';
13+
import { buildSchema, splitLeafProps, useLeaf, type SlotSchemaMap } from './use';
14+
import { useRendererContext } from '@knxcloud/lowcode-hooks';
15+
import type { IPublicTypeNodeSchema } from '@alilc/lowcode-types';
16+
import { exportSchema } from '@knxcloud/lowcode-utils';
17+
18+
const HOC_NODE_KEY: InjectionKey<{ rerenderSlots: () => void }> = Symbol('hocNode');
19+
const useHocNode = (rerenderSlots: () => void) => {
20+
const parentNode = inject(HOC_NODE_KEY, null);
21+
22+
let shouldRerender = false;
23+
24+
const debouncedRerender = () => {
25+
if (!shouldRerender) {
26+
shouldRerender = true;
27+
nextTick(() => {
28+
rerenderSlots();
29+
shouldRerender = false;
30+
});
31+
}
32+
};
33+
34+
provide(HOC_NODE_KEY, {
35+
rerenderSlots: debouncedRerender,
36+
});
37+
38+
if (!parentNode) {
39+
const { rerender } = useRendererContext();
40+
return {
41+
isRootNode: true,
42+
rerender: debouncedRerender,
43+
rerenderParent: rerender,
44+
};
45+
} else {
46+
return {
47+
isRootNode: false,
48+
rerender: debouncedRerender,
49+
rerenderParent: parentNode.rerenderSlots,
50+
};
51+
}
52+
};
1253

1354
export const Hoc = defineComponent({
1455
name: 'Hoc',
56+
inheritAttrs: false,
1557
props: leafProps,
16-
setup(props) {
17-
const { triggerCompGetCtx } = useRendererContext();
18-
const { node, locked, buildSchema, buildProps, buildSlots, buildLoop, buildShow } =
19-
useLeaf(props);
58+
setup(props, { slots, attrs }) {
59+
const showNode = shallowRef(true);
60+
const nodeSchmea = shallowRef(props.__schema);
61+
const slotSchema = shallowRef<SlotSchemaMap>();
62+
const updateSchema = (newSchema: IPublicTypeNodeSchema) => {
63+
nodeSchmea.value = newSchema;
64+
slotSchema.value = buildSchema(newSchema, node).slots;
65+
};
66+
const { rerenderParent, rerender, isRootNode } = useHocNode(() => {
67+
const newSchema = node ? exportSchema(node) : null;
68+
newSchema && updateSchema(newSchema);
69+
});
2070

21-
const { show, hidden, condition } = buildShow(props.__schema);
22-
const { loop, updateLoop, updateLoopArg, buildLoopScope } = buildLoop(props.__schema);
71+
const listenRecord: Record<string, () => void> = {};
72+
onUnmounted(() =>
73+
Object.keys(listenRecord).forEach((k) => {
74+
listenRecord[k]();
75+
delete listenRecord[k];
76+
})
77+
);
2378

24-
const compProps: PropSchemaMap = reactive({});
25-
const compSlots: SlotSchemaMap = reactive({});
26-
const result = buildSchema();
27-
Object.assign(compProps, result.props);
28-
Object.assign(compSlots, result.slots);
79+
const { locked, node, buildSlots, getNode } = useLeaf(props, (schema, show) => {
80+
const id = schema.id;
81+
if (id) {
82+
if (show && listenRecord[id]) {
83+
listenRecord[id]();
84+
delete listenRecord[id];
85+
} else if (!show && !listenRecord[id]) {
86+
const childNode = getNode(id);
87+
if (childNode) {
88+
listenRecord[id] = childNode.onVisibleChange(() => rerender());
89+
}
90+
}
91+
}
92+
});
2993

30-
// hoc
3194
if (node) {
32-
const disposeFunctions: Array<CallableFunction | undefined> = [];
33-
onUnmounted(() => disposeFunctions.forEach((dispose) => dispose?.()));
34-
disposeFunctions.push(
35-
node.onChildrenChange(() => {
36-
const schema = node.export(TransformStage.Render);
37-
compSlots.default = ensureArray(schema.children);
38-
})
39-
);
40-
disposeFunctions.push(
95+
onUnmounted(node.onChildrenChange(() => rerender()));
96+
onUnmounted(
4197
node.onPropChange((info) => {
42-
const { key, prop, newValue, oldValue } = info;
43-
44-
/**
45-
* 是否为根属性的修改
46-
*
47-
* @remark
48-
*
49-
* 对于 props
50-
*
51-
* ```js
52-
* {
53-
* color: 'red',
54-
* border: {
55-
* top: 12,
56-
* left: 20,
57-
* }
58-
* }
59-
* ```
60-
*
61-
* 则,对于属性 `color` 的修改为根属性的修改,而对于 `border.top` 的修改为非根属性的修改
62-
*/
98+
const { key, prop, newValue } = info;
6399
const isRootProp = prop.path.length === 1;
64-
65-
/** 根属性的 key 值 */
66-
const rootPropKey: string = prop.path[0];
67-
68-
if (isRootProp && key) {
69-
if (key === '___isLocked___') {
70-
// 设计器控制组件锁定
71-
locked.value = newValue;
72-
} else if (key === '___hidden___') {
73-
// 设计器控制组件渲染
74-
hidden(newValue);
75-
} else if (key === '___condition___') {
76-
// 条件渲染更新 v-if
77-
condition(newValue);
78-
} else if (key === '___loop___') {
79-
// 循环数据更新 v-for
80-
updateLoop(newValue);
81-
} else if (key === '___loopArgs___') {
82-
// 循环参数初始化 (item, index)
83-
updateLoopArg(newValue);
84-
} else if (key === 'children') {
85-
// 默认插槽更新
86-
if (isJSSlot(newValue)) {
87-
const slotNode: IPublicModelNode = prop.slotNode;
88-
const schema = slotNode.schema;
89-
compSlots.default = schema;
90-
} else if (!isNil(newValue)) {
91-
compSlots.default = ensureArray(newValue);
92-
} else {
93-
delete compSlots.default;
94-
}
95-
} else if (isJSSlot(newValue)) {
96-
// 具名插槽更新
97-
const slotNode: IPublicModelNode = prop.slotNode;
98-
const schema = slotNode.schema;
99-
compSlots[key] = schema;
100-
} else if (isNil(newValue) && isJSSlot(oldValue)) {
101-
// 具名插槽移除
102-
delete compSlots[key];
103-
} else {
104-
// 普通根属性更新
105-
compProps[key] = newValue;
106-
}
107-
} else if (rootPropKey === '___loopArgs___') {
108-
// 循环参数更新 (item, index)
109-
updateLoopArg(newValue, key);
110-
} else if (rootPropKey) {
111-
// 普通非根属性更新
112-
compProps[rootPropKey] = node.getPropValue(rootPropKey);
100+
if (isRootProp && key === '___isLocked___') {
101+
locked.value = newValue;
102+
} else {
103+
// 当前节点组件参数发生改变,通知父级组件重新渲染子组件
104+
rerenderParent();
113105
}
114106
})
115107
);
108+
onUnmounted(
109+
node.onVisibleChange((visible) => {
110+
isRootNode
111+
? // 当前节点为根节点(Page),直接隐藏
112+
(showNode.value = visible)
113+
: // 当前节点显示隐藏发生改变,通知父级组件重新渲染子组件
114+
rerenderParent();
115+
})
116+
);
116117
}
117118

118-
const getRef = (inst: ComponentPublicInstance) => {
119-
triggerCompGetCtx(props.__schema, inst);
120-
};
121-
122-
return {
123-
show,
124-
loop,
125-
compSlots,
126-
compProps,
127-
getRef,
128-
buildSlots,
129-
buildProps,
130-
buildLoopScope,
131-
};
132-
},
133-
render() {
134-
const {
135-
__comp: comp,
136-
show,
137-
loop,
138-
compProps,
139-
compSlots,
140-
getRef,
141-
buildSlots,
142-
buildProps,
143-
buildLoopScope,
144-
} = this;
145-
146-
if (!show) return null;
147-
if (!comp) return h('div', 'component not found');
119+
watch(
120+
() => props.__schema,
121+
(newSchema) => updateSchema(newSchema)
122+
);
148123

149-
if (!loop) {
150-
const props = buildProps(compProps, null, { ref: getRef });
151-
const slots = buildSlots(compSlots);
152-
return h(comp, props, slots);
153-
}
124+
const { triggerCompGetCtx } = useRendererContext();
154125

155-
if (!Array.isArray(loop)) {
156-
console.warn('[vue-renderer]: loop must be array', loop);
157-
return null;
158-
}
126+
return () => {
127+
const { __comp: comp, __vnodeProps: vnodeProps } = props;
128+
const compProps = splitLeafProps(attrs)[1];
129+
if (isRootNode && !showNode.value) return null;
159130

160-
return h(
161-
Fragment,
162-
loop.map((item, index, arr) => {
163-
const blockScope = buildLoopScope(item, index, arr.length);
164-
const props = buildProps(compProps, blockScope, { ref: getRef });
165-
const slots = buildSlots(compSlots, blockScope);
166-
return h(comp, props, slots);
167-
})
168-
);
131+
return comp
132+
? h(
133+
comp,
134+
mergeProps(compProps, vnodeProps, {
135+
onVnodeMounted(vnode) {
136+
const instance = vnode.component?.proxy;
137+
instance && triggerCompGetCtx(nodeSchmea.value, instance);
138+
},
139+
}),
140+
slotSchema.value ? buildSlots(slotSchema.value) : slots
141+
)
142+
: h('div', 'component not found');
143+
};
169144
},
170145
});

0 commit comments

Comments
 (0)