基於svg寫了一個塗鴉組件,說項目以前先附上幾張效果圖:webpack
項目地址:https://github.com/linmingdao/SVGraffitigit
效果預覽: github
功能演示: web
因爲篇幅問題,本文先整體介紹一下項目的大概狀況,重點介紹一下組件間的通訊方式。npm
1、項目說明 api
該項目是基於webpack@3.x.x構建的多頁應用,使用ES6開發,以組件的方式組織代碼。 git clone項目後(文末附上該項目github倉庫地址),npm i安裝相關依賴,npm run dev運行項目,默認會打開應用的首頁,也就是上面的效果預覽對應的界面。開發過程會單獨地爲一些功能編寫一些測試代碼,因此該項目提供了不一樣的頁面對應於不一樣的功能,好比:緩存
color picker組件測試頁: bash
組件消息通訊框架測試頁: 框架
svg底層繪製api測試頁: ide
2、組件間通訊
一、組件間爲了實現最大程度的封裝與解耦,不直接進行互相通訊,而是經過「消息訂閱/發佈管理中心」(如下簡稱「消息中心」)進行間接通訊。組件經過聲明本身爲不一樣的角色從而擁有對應的通訊能力:
這裏以項目中的中間區域的畫板組件爲例,由於畫板組件只是接收Toolbar組件發來的切換繪製能力、清空繪製內容以及Settings組件發來的設置繪製參數信息,因此該組件只是一個消息訂閱者角色,編碼設計以下:
首先導入對應的角色類:
import Subscriber from '../../supports/pubsub/base/subscriber';
import Topics from '../../supports/pubsub/base/topics';
複製代碼
編寫對應的組件:
// 經過@Topics的形式訂閱感興趣的消息類型
@Topics(['function', 'resident_function', 'set_preference'])
export default class Sketchpad extends Subscriber {
// 構造器
constructor(sketchpad) {
super();
this.sketchpad = sketchpad;
// ...
}
/**
* 該接口由【PubSub消息管理中心】負責調用,畫板組件在此接口處理接收到的消息類型
* 一、處理Toolbar組件發送的 「切換畫板繪製狀態」 ,對應的消息類型爲:「function」
* 二、處理Toolbar組件發送的 「清空繪製內容」 ,對應的消息類型爲:「resident_function」
* 三、處理Settings組件發送的 「設置畫板繪製參數」 ,對應的消息類型爲:「set_preference」
* @param {String} topic 消息主題標識
* @param {Object} entity 消息實體對象
*/
notify(topic, entity) {
// 在此處理接收到的消息
}
}
複製代碼
注:@Topics是靜態的,如有些主題是須要運行時訂閱也能夠調用Subscriber角色提供的subscribe方法動態訂閱消息。
二、PubSub(消息訂閱/發佈管理中心)的實現 既然是底層通用能力就必定要實現的不帶任何具體的業務,不管是在命名規範仍是編碼實現上都要保證它是一個通用模塊
PubSub的實現:
/**
* 主題訂閱發佈中心
*/
export default class PubSub {
// 緩存主題和主題的訂閱者列表
static topics = {};
/**
* 發佈主題消息
* @param {String} topic 主題
* @param {*} entity 消息體
*/
static publish(topic, entity) {
if (!PubSub.topics[topic]) return;
// 獲取該主題的訂閱者列表
const subscribers = PubSub.topics[topic];
// 向全部該主題的訂閱者發送主題消息
for (let subscriber of subscribers) {
subscriber.notify && subscriber.notify(topic, entity);
}
}
/**
* 一次登記一個主題
* @param {String} topic
*/
static registerTopic(topic) {
const topics = PubSub['topics'];
!topics[topic] && (topics[topic] = []);
}
/**
* 同時登記多個主題
* @param {Array} topics
*/
static registerTopics(topics = []) {
topics.forEach(topic => {
this.registerTopic(topic);
});
}
/**
* 添加主題訂閱者
* @param {String} topic 主題
* @param {Object} subscriber 實現了notify接口的訂閱者
*/
static addSubscriber(topic, subscriber) {
const topics = PubSub['topics'];
!topics[topic] && (topics[topic] = []);
// 將該主題的訂閱者登記到對應的主題
topics[topic].push(subscriber);
}
/**
* 刪除對應的訂閱者
* @param subscriber
*/
static removeSubscriber(subscriber) {
const subs = [];
// 遍歷全部主題下的訂閱者列表,將對應訂閱者刪除
const topics = PubSub.topics;
Object.keys(topics).forEach(topicName => {
const topic = topics[topicName];
for (let i = 0; i < topic.length; ++i) {
if (topic[i] === subscriber) {
subs.push(topics[topic].splice(i, 1));
break;
}
}
});
return subs;
}
}
複製代碼
Subscriber的實現:
import PubSub from '../pubsub';
const addSubscribe = (topics = [], context) => {
topics.forEach(topic => {
PubSub.addSubscriber(topic, context);
});
}
/**
* 主題訂閱者
*/
export default class Subscriber {
constructor() {
addSubscribe(this.__proto__.constructor.topics, this);
}
subscribe(topic) {
PubSub.addSubscriber(topic, this);
}
}
複製代碼
爲了方便訂閱主題,再提供一個@Topics註解:
import PubSub from '../pubsub';
/**
* 訂閱者主題裝飾器
* @param {Array} topics
*/
export default function Topics(topics) {
return target => {
target.topics = topics;
PubSub.registerTopics(topics);
}
}
複製代碼
Publisher的實現:
import PubSub from '../pubsub';
/**
* 主題消息發佈者
*/
export default class Publisher {
publish(topic, entity) {
PubSub.publish(topic, entity);
}
}
複製代碼
SubScatterer的實現:
import PubSub from '../pubsub';
import Subscriber from './subscriber';
/**
* 主題訂閱者 and 主題消息發佈者
*/
export default class SubScatterer extends Subscriber {
publish(topic, entity) {
PubSub.publish(topic, entity);
}
}
複製代碼
本篇介紹了項目的大概狀況,重點分析瞭如何以發佈/訂閱的形式實現組件間的通訊,接下來還會抽時間寫幾個篇分別介紹「svg底層繪製能力的封裝」、「畫板不一樣繪製狀態的實現與管理」、「如何開發一個通用的ColorPicker」等等與本項目相關的文章,寫得很差求親噴。
項目地址:https://github.com/linmingdao/SVGraffiti
感興趣的同窗們歡迎star一塊兒交流。