這是我參與更文挑戰的第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);
});
複製代碼
時間問題,今天沒空書寫了,由於功能還不夠完善,就不上傳代碼了。