这是我参与更文挑战的第13天,活动详情查看: 更文挑战javascript
前置文章:前端
公司之前的项目当中作过一个相关项目,店铺装修。当时的设计比较简陋,随着项目愈来愈大,上层建设和底层代码冗余度较高,维护起来比较麻烦,彼此之间没有清晰的分界线。因此那个时候就想着从新设计一个店铺装修,能够将边界划分清楚,维护起来相对简单的模式。
先看一下大的设计图。java
业务组件的入口统一在这里,和系统框架无关,将两者进行区分开。新的业务组件只须要按照这种方式注册就能够。node
import React from 'react';
import ShopDecorationNode from '../LeftNode/Nodes';
import { Yihangsitu, YihangsituProperty } from './Yihangsitu';
import Yihangyitu from './Yihangyitu';
const { registerNode, registerNodeProperty } = ShopDecorationNode;
registerNode('yihangsitu', (config: any) => {
return React.createElement((Yihangsitu as unknown) as React.FC<{}>, config);
});
registerNodeProperty('yihangsitu', (config: any) => {
return React.createElement((YihangsituProperty as unknown) as React.FC<{}>, config);
});
registerNode('yihangyitu', (config: any) => {
return React.createElement(Yihangyitu, config);
});
复制代码
该设计原则指出高层策略性的代码不该该依赖实现底层细节的代码,偏偏相反,那些实现底层细节的代码应该依赖高层策略性的代码。
react
依赖倒置原则的最重要问题就是确保应用程序或框架的主要组件从非重要的底层组件实现细节解耦出来,这将确保程序的最重要的部分不会由于低层次组件的变化修改而受影响。
web
整个店铺装修从设计原则来看:分为两大部分,框架层,业务组件层。他们存在各自的分工,以及数据交互。
设计模式
这部分实际上是根据固定的产品设计而来的。设计组件的时候,每一个组件具备本身固定的功能,彼此之间的数据交互能够经过相关的设计模式去进行弱关联。
数组
这里的开闭原则应用,对于业务组件的新增和修改所有由用户控制【开发者本身处理,不涉及到框架层改动】。markdown
这里个人应用规则是,全部的业务组件须要按照既定的规则来开发,须要经过的特定的方法进行和框架层次的数据交互。
antd
店铺装修对应的用户操做基本以下:
针对开发人员,当随着组建的增多,开发人员更多的想只须要操心对应的组件就好了,不要让我操做太多。
首先功能上确定要知足用户行为,而后兼顾开发人员的需求。固然从设计上来说,咱们也是想在后期的维护和扩展上面尽可能简单,将框架层设计,和业务层设计作到分离。
这里的关键点在哪里呢?
具体业务组件的引用和加载,左侧组件列表须要引入进来。组件渲染须要加载对应的组件。组件属性也须要引入对应的组件属性文件,而后动态加载。
这里核心的关键就是将组件的加载动态【注册的概念】引入进来。业务组件的引入不是经过important的方式引入进来了。
这个文字说明起来有点麻烦啊。那就经过单体来介绍,介绍自我功能,和外界交互功能。
NodeRegistry:类
页面遍历当前nodeTypes内部注册的组件,显示左侧店铺装修组件列表。
开发人员调用register**方法,注册组件。
registerNode('yihangsitu', (config: any) => {
return React.createElement((Yihangsitu as unknown) as React.FC<{}>, config);
});
registerNodeProperty('yihangsitu', (config: any) => {
return React.createElement((YihangsituProperty as unknown) as React.FC<{}>, config);
});
复制代码
组件的渲染会调用renderNode组件。同时传入对应的属性数据。
{renderNode(item.type, getCurrentNodeContent(item.key))}
复制代码
renderNode方法
public renderNode = (name: string, config: any) => {
return this.nodeTypes[name](config);
};
复制代码
右侧属性组件渲染的时候会调用:renderNodeProperty方法,三个属性,key,更新属性的方法,用于开发人员书写的组件,和系统进行数据通讯,content是当前最新的属性值。
<div key={property.key}>{renderNodeProperty(property.type, {
keyString: property.key,
onValuesChange: store.updateNodeContent.bind(store),
content: JSON.parse(property.content || '{}')
})}
</div>;
复制代码
renderNodeProperty
public renderNodeProperty = (name: string, config?: any) => {
const callback = this.nodePropertyTypes[name];
return callback ? callback(config) : '';
};
复制代码
tempStoreData:用于数据的更新,进行页面的从新渲染,这里其实个人想法还须要继续优化一下。经过HOC处理页面数据的更新。
循环遍历tempStoreData,而后调用renderNode方法渲染组件。获取当前组件的属性数据,同步传入进去。
获取当前须要展现的组件的type类型和key,而且获取到最新的属性值同步传入。调用renderProperty方法
<div key={property.key}>{renderNodeProperty(property.type, {
keyString: property.key,
onValuesChange: store.updateNodeContent.bind(store),
content: JSON.parse(property.content || '{}')
})}
</div>;
复制代码
import React from 'react';
import ShopDecorationNode from '../LeftNode/Nodes';
import { Yihangsitu, YihangsituProperty } from './Yihangsitu';
import Yihangyitu from './Yihangyitu';
const { registerNode, registerNodeProperty } = ShopDecorationNode;
registerNode('yihangsitu', (config: any) => {
return React.createElement((Yihangsitu as unknown) as React.FC<{}>, config);
});
registerNodeProperty('yihangsitu', (config: any) => {
return React.createElement((YihangsituProperty as unknown) as React.FC<{}>, config);
});
registerNode('yihangyitu', (config: any) => {
return React.createElement(Yihangyitu, config);
});
复制代码
我这里仍是使用的是dumi来建立项目。
yarn create @umijs/dumi-lib --site
复制代码
class NodeRegistry {
public nodeTypes: Record<string, (config: any) => {}> = Object.create(null);
public nodePropertyTypes: Record<string, (config: any) => {}> = Object.create(null);
public registerNode = (name: string, callback: any) => {
this.nodeTypes[name] = callback;
};
public renderNode = (name: string, config: any) => {
return this.nodeTypes[name](config);
};
public registerNodeProperty = (name: string, callback: any) => {
this.nodePropertyTypes[name] = callback;
};
public renderNodeProperty = (name: string, config?: any) => {
const callback = this.nodePropertyTypes[name];
return callback ? callback(config) : '';
};
}
const ShopDecoration = new NodeRegistry();
export default ShopDecoration;
复制代码
import React from 'react';
import { Button } from 'antd';
import styles from './index.less';
import ShopDecorationNode from './Nodes';
export default () => {
const ondragstart = (event: any, text: string) => {
event.dataTransfer.setData('Text', text);
};
const { nodeTypes } = ShopDecorationNode;
const nodes = Object.keys(nodeTypes);
return (
<div className={styles.left_node}> {nodes.map((item) => ( <Button type="primary" draggable={true} onDragStart={(event) => { ondragstart(event, item); }} > {item} </Button> ))} </div>
);
};
复制代码
/* * @Description: * @Author: rodchen * @Date: 2021-06-13 14:14:46 * @LastEditTime: 2021-06-13 18:20:11 * @LastEditors: rodchen */
import React, { useState } from 'react';
import styles from './index.less';
import store from '../Utils/store';
import ShopDecorationNode from '../LeftNode/Nodes';
import { NodeClass } from '../Type/interface';
import { NodeClassType } from '../Type/type';
export default () => {
const [tempStoreData, setTempStoreData] = useState<NodeClassType[]>([])
const { renderNode } = ShopDecorationNode;
const onDrop = (event: any) => {
const data: string = event.dataTransfer.getData('Text');
event.preventDefault();
const newNode = new NodeClass(data)
store.updateStoreData(store.storeData.concat([newNode]))
store.setCurrentNode(newNode);
};
store.subscriptionStoreDataChange((storeData: NodeClassType[]) => {
setTempStoreData(storeData)
})
const allowDrop = (ev: any) => {
ev.preventDefault();
};
const onClickForHanldeProperty = (item: NodeClassType) => {
store.setCurrentNode(item);
};
const getCurrentNodeContent = (key: string) => {
const content = tempStoreData.filter(innerItem => innerItem.key === key)[0].content;
try {
return JSON.parse(content as string)
} catch (e) {
return ''
}
}
return (
<div onDrop={onDrop} onDragOver={allowDrop} className={styles.content_render}> {tempStoreData.map((item) => ( <div key={item.key} className={styles.content_node} onClick={() => { onClickForHanldeProperty(item); }} > {renderNode(item.type, getCurrentNodeContent(item.key))} </div> ))} </div>
);
};
复制代码
import React, { useState } from 'react';
import store from '../Utils/store';
import ShopDecorationNode from '../LeftNode/Nodes';
import { NodeClassType } from '../Type/type';
export default () => {
const { renderNodeProperty } = ShopDecorationNode;
const [property, setProperty] = useState<NodeClassType>({type: '', key: ''});
store.subscriptionNodeChange((item: any) => {
setProperty(item);
});
return <div key={property.key}>{renderNodeProperty(property.type, { keyString: property.key, onValuesChange: store.updateNodeContent.bind(store), content: JSON.parse(property.content || '{}') })} </div>;
};
复制代码
import { NodeClass } from '../Type/interface';
import { NodeClassType } from '../Type/type';
class Store {
public storeData: NodeClassType[] = [];
public currentNode: NodeClassType = new NodeClass('');
public subscriptionNodeArray: any[] = [];
public subscriptionStoreDataArray: any[] = [];
public setCurrentNode(node: NodeClassType) {
this.currentNode = node;
this.subscriptionNodeArray.forEach((item) => {
item(node);
});
}
public updateStoreData(storeData: NodeClassType[]) {
this.storeData = storeData;
this.subscriptionStoreDataArray.forEach((item) => {
item(storeData);
});
}
public updateNodeContent({key, content}: {key: string, content: Object}) {
this.storeData = this.storeData.map(item => item.key === key ? ((item.content = JSON.stringify(content)), item) : item);
this.subscriptionStoreDataArray.forEach((item) => {
item(this.storeData);
});
}
public subscriptionNodeChange(callback: Function) {
this.subscriptionNodeArray.push(callback);
}
public subscriptionStoreDataChange(callback: Function) {
this.subscriptionStoreDataArray.push(callback);
}
}
const store = new Store();
export default store;
复制代码
import React from 'react';
import ShopDecorationNode from '../LeftNode/Nodes';
import { Yihangsitu, YihangsituProperty } from './Yihangsitu';
import Yihangyitu from './Yihangyitu';
const { registerNode, registerNodeProperty } = ShopDecorationNode;
registerNode('yihangsitu', (config: any) => {
return React.createElement((Yihangsitu as unknown) as React.FC<{}>, config);
});
registerNodeProperty('yihangsitu', (config: any) => {
return React.createElement((YihangsituProperty as unknown) as React.FC<{}>, config);
});
registerNode('yihangyitu', (config: any) => {
return React.createElement(Yihangyitu, config);
});
复制代码
时间问题,今天没空书写了,由于功能还不够完善,就不上传代码了。