I. Observer model
Summary: Need to understand the relationship between the target and the observer
- The relationship between target and observer is similar to that between teacher and student. The teacher has a cram school, students have to go to the teacher to listen to the teacher, the teacher began to teach.
- The students understood the teacher’s lesson and updated their knowledge. The teacher has a direct relationship with the students, a face-to-face relationship between them.
Conclusion:
- Observer mode is mostly used within a single application without a third party to implement scheduling. Defines a one-to-many relationship between objects.
Implementation approach
- A) Subject B) Observer C) Subject D) Subject
code
// util.js
export const Subject = function(eventName = 'default') {
this.observerList = [];
this.eventName = eventName;
this._t;
}
// Register the observer to the target instance, which can be read as observer -> Subject;
Subject.prototype.add = function(observer) {
// observerList many students
if(Array.isArray(observer)) {
this.observerList.push(... observer); }else {
this.observerList.push(observer); }}// Remove the observer as an observer;
Subject.prototype.remove = function(observer) {
const index = this.observerList.findIndex((item) = >item === observer);
if(index ! = = -1) {
this.observerList.splice(index,1);
return true;
} else {
return false; }}// Notify all students of the content (observerList)
Subject.prototype.notify = function(data){
// Anti-shake processing
clearTimeout(this._t);
this._t = setTimeout(() = > {
const observerList = this.observerList;
for(let i=0,len=observerList.length; i < len; i++) {
// Update is an interface exposed by the Observer and cannot be notified if it is not provided
observerList[i].update({eventName: this.eventName, data})
}
}, 500);
// This is set to 500 to test the effect, usually set to 50
}
export const Observer = function(fn) {
const _fn = fn;
this.update = function(data) {
_fn(data)
}
}
Copy the code
The actual usage
Import * as React from 'React'; import { Subject, Observer } from './util'; const subject = new Subject(); export default class ClassRoom extends React.Component { constructor(props) { super(props); this.student = new Observer(this.callback); this.student2 = new Observer(this.callback2); subject.add([this.student,this.student2]); console.log(subject); } componentWillUnmount() { console.log(subject.remove(this.student)); console.log(subject.remove(this.student2)); console.log(subject); } callback = ({eventName, data}) => { console.log('student',data); }; callback2 = ({eventName, data}) => { console.log('student',data); } teacher = () => {subject. Notify (' subject... '); }; render() { return <button onClick={this.teacher}>notify</button> } }Copy the code
Publish and subscribe mode
Summary: Need to understand the relationship between dispatch center, subscriber, and publisher.
- Luo Xiang opened a column on the Internet. The teacher uploaded the class to the platform. Students who like the class subscribe to the column. Later, the teacher only needs to upload the video to the platform, and the platform will push the updated video to subscribers.
- The connection between the teacher and the student is not so big, and the two are connected through the platform.
conclusion
- The publish-subscribe model is a variation of the observer model, in which publishers and subscribers are unaware of each other’s existence and are connected to each other through a dispatch center. The name of the event has always been a condition for them to connect with each other.
- It is mainly used to flaten the multi-layer transparent communication mode.
Implementation approach
- It’s basically implementing register, Watcher, trigger, unregister which is a method. This is the order of the whole process.
- Watcher contains register and unregister.
Code (Regular version)
// util.js
export const pubsubFactory = function (myPubsub) {
let eventCenter = {};
let eventTimeout = {};
myPubsub.getEventCenter = function () {
return eventCenter;
};
myPubsub.watcher = function (eventName, fn) {
// If there is no register, register the event name and add fn to the queue
if(! eventCenter[eventName]) { eventCenter[eventName] = [fn]; }else {
eventCenter[eventName].push(fn);
}
// unregister
return function () {
let events = eventCenter[eventName];
eventCenter[eventName] = events.filter((handler) = >handler ! == fn); }; }; myPubsub.trigger =function (eventName, data) {
const events = eventCenter[eventName];
if(! events) {console.log(eventName + 'Event not registered');
return;
}
let _t = eventTimeout[eventName];
clearTimeout(_t);
eventTimeout[eventName] = setTimeout(() = > {
for (let i = 0, len = events.length; i < len; i++) { events[i]({ eventName, data }); }},50);
};
return myPubsub;
};
export const pubsub = pubsubFactory({});
Copy the code
The actual usage
// Actual usage
import * as React from 'React';
import { pubsub } from './util';
export default class PubSubDemo extends React.Component {
constructor(props) {
super(props);
this.subject = pubsub.watcher('event'.this.callback);
this.subject2 = pubsub.watcher('event2'.this.callback2);
}
componentWillUnmount() {
this.subject();
this.subject2();
console.log(pubsub.getEventCenter())
}
callback = ({ eventName, data }) = > {
console.log(eventName, data);
};
callback2 = ({ eventName, data }) = > {
console.log(eventName, data);
};
publish = () = > {
pubsub.trigger('event'.'data1');
};
publish2 = () = > {
pubsub.trigger('event2'.'data2');
};
render() {
return (
<div>
<button onClick={this.publish}>publish</button>
<button onClick={this.publish2}>publish2</button>
</div>); }}Copy the code
Code (Decorator version)
// util.js
// This version does not support hook components
import * as _ from "lodash";
function decorator() {
const events = [];
let cache = undefined;
let _t = null;
const register = callback= > {
if (!events.includes(callback)) {
events.push(callback);
}
};
const unregister = callback= > {
const index = events.indexOf(callback);
events.splice(index, 1);
return index > -1;
};
const trigger = value= > {
clearTimeout(_t);
cache = value;
_t = setTimeout(() = > {
events.forEach(callback= > {
_.isFunction(callback) && callback(cache);
});
}, 50);
};
const watcher = function(inst, funcName) {
const name = ` __${funcName}__ `;
const { componentDidMount, componentWillUnmount } = inst;
// There is a problem with multilevel recursive calls and it is not recommended to bind too many PubSub events to one component.
inst.componentDidMount = function() {
this[name] = this[funcName].bind(this);
register(this[name]);
if (_.isFunction(componentDidMount)) {
componentDidMount.call(this); }}; inst.componentWillUnmount =function() {
unregister(this[name]);
delete this[name];
if (_.isFunction(componentWillUnmount)) {
componentWillUnmount.call(this); }}; };return {
watcher,
trigger,
register,
unregister
};
}
const DF = {};
export default function decoratorFactory(name = "default") {
if(! (namein DF)) {
DF[name] = decorator();
}
return DF[name];
}
Copy the code
The actual usage
// Actual usage
import * as React from 'React';
import decoratorFactory from './util';
const state = decoratorFactory('event');
const state2 = decoratorFactory('event2');
export default class DecoratorPubsubDemo extends React.Component {
componentWillUnmount() {
console.log(state);
console.log(state2);
}
@state.watcher
callback = (data) = > {
console.log(data);
};
@state2.watcher
callback2 = (data) = > {
console.log(data);
};
publish = () = > {
state.trigger('data1');
};
publish2 = () = > {
state2.trigger('data2');
};
render() {
return (
<div>
<button onClick={this.publish}>publish</button>
<button onClick={this.publish2}>publish2</button>
</div>); }}Copy the code
Third, refer to the article
- Design patterns
- Ruan Yifeng, the use of decorators to achieve automatic publishing events
Four, conclusion
The above views are just personal understanding of the output of practice, welcome you to supplement the big guy passing by