A global data sharing tool based on event bus
Take you to hand-write event bus and encapsulate global data sharing library
Comments are articles
EventBus
//Global event bus // Author:coderwhy(mentor) // Rewrite:coderJoon(little fan) class EventBus {<!-- --> constructor() {<!-- --> // Object to store events this.EventBus = {<!-- -->}; } /** * Listen for events * @param {*} eventName event name * @param {*} eventCallback event callback * @param {*} thisArg this points to */ on(eventName, eventCallback, thisArg) {<!-- --> //Type checking if (typeof eventName !== 'string') {<!-- --> throw new TypeError('eventname must be string type'); } if (typeof eventCallback !== 'function') {<!-- --> throw new TypeError('eventCallback must be function type'); } // Get the corresponding data source according to the event name let handlers = this.EventBus[eventName]; // It may be empty the first time. If it cannot be obtained, we need to create a data structure and push the data structure into it. if (!handlers) {<!-- --> //Create data structure handlers = []; //Put the data structure in this.EventBus[eventName] = handlers; } //Add event callback and this pointer handlers.push({<!-- --> eventCallback, thisArg, }); // The purpose of returning this is for possible chain calls later. return this; } /** * trigger event * @param {*} eventName trigger event name * @param {...any} payload The data passed by the trigger event */ emit(eventName, ...payload) {<!-- --> if (typeof eventName !== 'string') {<!-- --> throw new TypeError('eventname must be string type'); } // Find the corresponding event callback according to the event name to trigger the function // 1. Get the event data structure // tips: The reason for giving it || is to prevent it from being undefined and to prevent errors. const handlers = this.EventBus[eventName] || []; handlers.forEach((handler) => {<!-- --> handler.eventCallback.apply(handler.thisArg, payload); }); return this; } /** * Cancel monitoring * @param {*} eventName event name * @param {*} eventCallback event callback */ off(eventName, eventCallback) {<!-- --> if (typeof eventName !== 'string') {<!-- --> throw new TypeError('eventname must be string type'); } if (typeof eventCallback !== 'function') {<!-- --> throw new TypeError('eventcallback must be function type'); } //The hardest part // 1. First get the data structure corresponding to the event name const handlers = this.EventBus[eventName]; //No need for || because there is a judgment below if (handlers & amp; & amp; eventCallback) {<!-- --> // Get the data object const newHandlers = [...handlers]; //The data structure is xxx:[{eventcallback,thisarg},...] // Traverse this structure for (let i = 0; i < newHandlers.length; i + + ) {<!-- --> const handler = newHandlers[i]; //Each object if (handler.eventCallback === eventCallback) {<!-- --> const index = handlers.indexOf(handler); handlers.splice(index, 1); } } } if (handlers.length === 0) {<!-- --> delete this.EventBus[eventName]; } } /** * Only listen once * @param {*} eventName event name * @param {*} eventCallback event callback * @param {*} thisArg this points to */ once(eventName, eventCallback, thisArg) {<!-- --> if (typeof eventName !== 'string') {<!-- --> throw new TypeError('eventname must be string type'); } if (typeof eventCallback !== 'function') {<!-- --> throw new TypeError('eventcallback must be function'); } //How do we write a function that only listens once? // 1. Be sure to perform monitoring // 2. Cancel all previous monitoring /* Implementation ideas: As long as you call my once method, you will definitely perform monitoring. But before executing the monitoring, I need to cancel all the previous monitoring. When emit is executed, we need to call this function to execute the callback Watch me operate */ const tempfunction = (...payload) => {<!-- --> this.off(eventName, tempfunction); //Cancel listening eventCallback.apply(thisArg, payload); //Execute function }; //Execute monitoring logic return this.on(eventName, tempfunction, thisArg); /* Questions you may ask Teacher, there is nothing wrong with you passing the eventname. Your tempfunction is passed to my off method. It uses eventcallback to determine whether to cancel. How do you cancel it? You have a bug? Answer: Look at this.on below to see if temfunction is pushed as a callback function, so the tem function can be canceled normally. */ } clear() {<!-- --> this.EventBus = {<!-- -->}; } hasEvent(eventName) {<!-- --> return Object.keys(this.EventBus).includes(eventName); } } module.exports = EventBus; //Global event bus methods /* on---listen for events once---monitor once emit---trigger event off---Cancel monitoring clear---Clear event hasEvent---whether it contains the event */
EventStore
//Introduce event bus const EventBus = require('./EventBus'); //Introduce tools const {<!-- --> isObject } = require('./utils'); class EventStore {<!-- --> //Initialize data--receive an opstion object constructor(opstion) {<!-- --> // Type judgment if (!isObject(opstion.state)) {<!-- --> throw new TypeError('state must be object type'); } if (opstion.action & amp; & amp; isObject(opstion.action)) {<!-- --> // Get all actions for judgment const values = Object.values(opstion.action); for (const value of values) {<!-- --> if (typeof value !== 'function') {<!-- --> throw new TypeError('action must be function type'); } } //Add to class this.action = opstion.action; } //Add the data in opstion to the class this.state = opstion.state; //Create two event buses for notifications this.even = new EventBus(); this.evens = new EventBus(); // Monitor state data this.observe(opstion.state); } observe(state) {<!-- --> // Get this const _this = this; // Get the key on the state and then get the data source based on the key Object.keys(state).forEach((key) => {<!-- --> let Orderdata = this.state[key]; //Get each data-old data //Call the object.defineproperty method to monitor the data Object.defineProperty(state, key, {<!-- --> get: function () {<!-- --> return Orderdata; }, set: function (NewData) {<!-- --> if (Orderdata === NewData) return; // 1. Update data Orderdata = NewData; // 2. Notification event _this.even.emit(key, Orderdata); _this.evens.emit(key, {<!-- --> [key]: Orderdata }); }, }); }); } /** * Listening function * @param {*} stateKey key in the monitored state * @param {*} stateCallback processing function after listening */ onState(stateKey, stateCallback) {<!-- --> const keys = Object.keys(this.state); if (keys.indexOf(stateKey) === -1) {<!-- --> throw new Error('the key does not in state'); } if (typeof stateCallback !== 'function') {<!-- --> throw new TypeError('the eventCallback must be function type'); } this.even.on(stateKey, stateCallback); const data = this.state[stateKey]; stateCallback.apply(this.state, [data]); } /** * Monitor multiple keys * @param {*} stateKeys multiple keys * @param {*} stateCallback a callback */ onStates(stateKeys, stateCallback) {<!-- --> if (typeof stateKeys !== 'object') {<!-- --> throw new TypeError('the onStates keys must be Array type'); } const keys = Object.keys(this.state); const value = {<!-- -->}; for (const key of stateKeys) {<!-- --> if (keys.indexOf(key) === -1) {<!-- --> throw new Error('the state dose node have your key'); } this.evens.on(key, stateCallback); value[key] = this.state[key]; } stateCallback.apply(this.state, [value]); } /** * Set data * @param {*} stateKey setkey * @param {*} stateValue setdata */ setState(stateKey, stateValue) {<!-- --> this.state[stateKey] = stateValue; } /** * Cancel listening function s version * @param {*} stateKeys keys that need to be canceled * @param {*} stateCallback The bound callback */ offStates(stateKeys, stateCallback) {<!-- --> const keys = Object.keys(this.state); if (typeof stateKeys !== 'object') {<!-- --> throw new TypeError('the keys must be Array type'); } stateKeys.forEach((key) => {<!-- --> if (keys.indexOf(key) === -1) {<!-- --> throw new TypeError('the key does not in state'); } this.evens.off(key, stateCallback); }); } /** * Cancel the listening function * @param {*} statekey key that needs to be canceled * @param {*} stateCallback The bound callback */ offState(statekey, stateCallback) {<!-- --> const keys = Object.keys(this.state); if (keys.indexOf(statekey) === -1) {<!-- --> throw new Error('the state does not have your key'); } this.even.off(statekey, stateCallback); } /** * Execute asynchronously * @param {*} actionName asynchronous name * @param {...any} args The parameters you want to pass */ dispatch(actionName, ...args) {<!-- --> if (typeof actionName !== 'string') {<!-- --> throw new TypeError('the action must be string type'); } if (Object.keys(this.action).indexOf(actionName) === -1) {<!-- --> throw new Error('the action dont in stateAction'); } // Call the function through this const actionFun = this.action[actionName]; //The second parameter is ctx. The purpose is to pass the state to make modifications and our payload. actionFun.apply(this, [this.state, ...args]); } } module.exports = EventStore;