1.index.js
const version = { "reflux-core": "@VERSION" }; // 用户设置createAction返回对象中待添加的方法,创建实例时不可被重复设置,通过闭包存储数据 // 且须保证方法名没有跟PublisherMethods中的方法名起冲突,preEmit、shouldEmit方法除外 import * as ActionMethods from "./ActionMethods"; // action、store扩展为事件对象 import * as ListenerMethods from "./ListenerMethods"; // store向listenable绑定函数,listenable触发事件时执行 import * as PublisherMethods from "./PublisherMethods"; // 用户设置store原型中待添加的方法,创建实例时不可被重复设置,通过闭包存储数据 // 且须保证方法名没有跟PublisherMethods、ListenerMethods中的方法名起冲突,preEmit、shouldEmit方法除外 import * as StoreMethods from "./StoreMethods"; import { staticJoinCreator as maker} from "./joins"; const joinTrailing = maker("last"); const all = joinTrailing; // Reflux.all alias for backward compatibility const joinLeading = maker("first"); const joinStrict = maker("strict"); const joinConcat = maker("all"); // 工具函数 import * as _ from "./utils"; const utils = _; // createAction工厂函数,构建仿函数functor,调用functor触发绑定事件 // functor上挂载listen、trigger、triggerAsync、deferWith方法 import {createAction} from "./createAction"; // 多个事件对象的相互监听、触发事件 import {createStore} from "./createStore"; // 创建多个action仿函数对象,对象形式返回 const createActions = (function() { var reducer = function(definitions, actions) { Object.keys(definitions).forEach(function(actionName) { var val = definitions[actionName]; actions[actionName] = createAction(val); }); }; return function(definitions) { var actions = {}; if (definitions instanceof Array) { definitions.forEach(function(val) { if (_.isObject(val)) { reducer(val, actions); } else { actions[val] = createAction(val); } }); } else { reducer(definitions, actions); } return actions; }; })(); /** * Sets the eventmitter that Reflux uses */ function setEventEmitter (ctx) { _.EventEmitter = ctx; } /** * Sets the method used for deferring actions and stores */ function nextTick (nextTick) { _.nextTick = nextTick; } function use (pluginCb) { pluginCb(this); } // 记录及操作已添加的action、store import * as __keep from "./Keep"; // export in format that supports syntax: var Reflux = require('reflux-core'); export { version, ActionMethods, ListenerMethods, PublisherMethods, StoreMethods, utils, createAction, createStore, createActions, setEventEmitter, nextTick, use, joinTrailing, all, joinLeading, joinStrict, joinConcat, __keep }; /*eslint-enable no-underscore-dangle*/ // export in format that supports syntax: import Reflux from 'reflux-core'; Object.defineProperty(exports, "default", { get: function() { return exports; } }); // 不支持bind方法时,错误提示 if (!Function.prototype.bind) { console.error( "Function.prototype.bind not available. " + "ES5 shim required. " + "https://github.com/spoike/refluxjs#es5" ); }
2.createAction.js
import * as _ from "./utils"; import * as ActionMethods from "./ActionMethods"; import * as PublisherMethods from "./PublisherMethods"; import * as Keep from "./Keep"; var allowed = { preEmit: 1, shouldEmit: 1 }; // 创建仿函数functor,参数definition中的属性和方法用于构建functor的属性和方法 // 同时functor拥有PublisherMethods中的listen、trigger、triggerAsync、deferWith方法 // 子action通过definition.children添加,asyncResult为真时有["completed", "failed"]两个子action export function createAction(definition) { definition = definition || {}; if (!_.isObject(definition)){ definition = {actionName: definition}; } // 校验ActionMethods中的方法没有跟PublisherMethods中的方法起冲突,preEmit、shouldEmit方法除外 for(var a in ActionMethods){ if (!allowed[a] && PublisherMethods[a]) { throw new Error("Cannot override API method " + a + " in Reflux.ActionMethods. Use another method name or override it on Reflux.PublisherMethods instead." ); } } // 校验createAction传参中的方法没有跟PublisherMethods中的方法起冲突,preEmit、shouldEmit方法除外 for(var d in definition){ if (!allowed[d] && PublisherMethods[d]) { throw new Error("Cannot override API method " + d + " in action creation. Use another method name or override it on Reflux.PublisherMethods instead." ); } } // childActions以子action名作为键,子action仿函数作为值 definition.children = definition.children || []; if (definition.asyncResult){ definition.children = definition.children.concat(["completed", "failed"]); } var i = 0, childActions = {}; for (; i < definition.children.length; i++) { var name = definition.children[i]; childActions[name] = createAction(name); } // 将PublisherMethods、ActionMethods、definition中的属性和方法赋予context,最终赋给functor // 仿函数functor的意义是派发、触发事件 var context = _.extend({ eventLabel: "action", emitter: new _.EventEmitter(), _isAction: true }, PublisherMethods, ActionMethods, definition); // 直接调用仿函数将使用通过PublisherMethods注入的trggier同步触发、triggerAsync异步触发方法 var functor = function() { var triggerType = functor.sync ? "trigger" : "triggerAsync"; return functor[triggerType].apply(functor, arguments); }; _.extend(functor, childActions, context); // 将仿函数functor添加到createdActions中,同时index.js模块中为Reflux提供createdActions的引用 Keep.addAction(functor); return functor; }
3.PublisherMethods.js
import * as _ from "./utils"; /** * A module of methods for object that you want to be able to listen to. * This module is consumed by `createStore` and `createAction` */ // 钩子事件,为shouldEmit钩子函数提供参数 // 返回undefined时,shouldEmit方法参数为trigger方法的传参 export var preEmit = function() {}; // 钩子事件,触发事件前校验触发条件是否达成 export var shouldEmit = function() { return true; }; // 通过event-emitter创建的事件对象this.emitter中添加绑定函数,需要修改函数上下文 // 返回撤销绑定函数的执行函数,aborted标记为真 export var listen = function(callback, bindContext) { bindContext = bindContext || this; var eventHandler = function(args) { if (aborted){ return; } callback.apply(bindContext, args); }, me = this, aborted = false; this.emitter.addListener(this.eventLabel, eventHandler); return function() { aborted = true; me.emitter.removeListener(me.eventLabel, eventHandler); }; }; // 触发事件,触发事件前执行钩子函数this.preEmit及this.shouldEmit // this.preEmit为this.shouldEmit提供参数,this.shouldEmit的返回值作为是否触发事件的凭据 export var trigger = function() { var args = arguments, pre = this.preEmit.apply(this, args); args = pre === undefined ? args : _.isArguments(pre) ? pre : [].concat(pre); if (this.shouldEmit.apply(this, args)) { this.emitter.emit(this.eventLabel, args); } }; export var triggerAsync = function(){ var args = arguments, me = this; _.nextTick(function() { me.trigger.apply(me, args); }); }; // 使用callback装饰之前的trigger函数,返回新的trigger函数 export var deferWith = function(callback) { var oldTrigger = this.trigger, ctx = this, resolver = function() { oldTrigger.apply(ctx, arguments); }; this.trigger = function() { callback.apply(ctx, [resolver].concat([].splice.call(arguments, 0))); }; };
4.createStore.js
import * as _ from "./utils"; import * as Keep from "./Keep"; import {mix as mixer} from "./mixer"; import {bindMethods} from "./bindMethods"; import * as StoreMethods from "./StoreMethods"; import * as PublisherMethods from "./PublisherMethods"; import * as ListenerMethods from "./ListenerMethods"; var allowed = { preEmit: 1, shouldEmit: 1 }; // 仿照react,参数用于配置构造函数,最终生成实例并返回 export function createStore(definition) { definition = definition || {}; // 校验StoreMethods中的方法没有跟PublisherMethods、ListenerMethods中的方法起冲突,preEmit、shouldEmit方法除外 for(var a in StoreMethods){ if (!allowed[a] && (PublisherMethods[a] || ListenerMethods[a])){ throw new Error("Cannot override API method " + a + " in Reflux.StoreMethods. Use another method name or override it on Reflux.PublisherMethods / Reflux.ListenerMethods instead." ); } } // 校验definition中的方法没有跟PublisherMethods、ListenerMethods中的方法起冲突,preEmit、shouldEmit方法除外 for(var d in definition){ if (!allowed[d] && (PublisherMethods[d] || ListenerMethods[d])){ throw new Error("Cannot override API method " + d + " in store creation. Use another method name or override it on Reflux.PublisherMethods / Reflux.ListenerMethods instead." ); } } // 类似react的mixins,多个init、preEmit、shouldEmit或其他mixins中方法均将执行 definition = mixer(definition); function Store() { var i = 0, arr; this.subscriptions = []; this.emitter = new _.EventEmitter(); this.eventLabel = "change"; // 配置definition的方法执行上下文更正为store bindMethods(this, definition); if (this.init && _.isFunction(this.init)) { this.init(); } // this.listenables以action相关对象作为项的数组,action名作为键,createAction返回值仿函数作为值 // action作为事件对象,同时获取事件名,绑定函数为store中的方法 if (this.listenables){ arr = [].concat(this.listenables); for(; i < arr.length; i++){ this.listenToMany(arr[i]); } } } // Store构造函数原型预先添加方法,再创建实例 // ListenerMethods注入hasListener、listenToMany、listenTo、stopListeningTo、stopListeningToAll方法 // 意义是向关联的action仿函数对象或另一个store实例绑定、解绑事件 // PublisherMethods注入listen绑定事件、trigger触发事件、triggerAsync、deferWith装饰触发方法 _.extend(Store.prototype, ListenerMethods, PublisherMethods, StoreMethods, definition); var store = new Store(); // 将仿函数functor添加到createdStores中,同时index.js模块中为Reflux提供createdStores的引用 Keep.addStore(store); return store; }
5.listenerMethods.js
import * as _ from "./utils"; import { instanceJoinCreator as maker } from "./joins"; // 获取listenable仿函数对象下的子仿函数对象,返回值为对象形式 var mapChildListenables = function(listenable) { var i = 0, children = {}, childName; for (;i < (listenable.children || []).length; ++i) { childName = listenable.children[i]; if(listenable[childName]){ children[childName] = listenable[childName]; } } return children; }; // 扁平化多个嵌套的action // 键值对记录仿函数对象及子仿函数对象,子仿函数对象为驼峰式action名+子action名 var flattenListenables = function(listenables) { var flattened = {}; for(var key in listenables){ var listenable = listenables[key];// 通过createAction创建的functor仿函数对象 var childMap = mapChildListenables(listenable); var children = flattenListenables(childMap); flattened[key] = listenable; for(var childKey in children){ var childListenable = children[childKey]; flattened[key + _.capitalize(childKey)] = childListenable; } } return flattened; }; export var hasListener = function(listenable) { var i = 0, j, listener, listenables; for (;i < (this.subscriptions || []).length; ++i) { listenables = [].concat(this.subscriptions[i].listenable); for (j = 0; j < listenables.length; j++){ listener = listenables[j]; if (listener === listenable || listener.hasListener && listener.hasListener(listenable)) { return true; } } } return false; }; // 向多个action绑定事件 export var listenToMany = function(listenables){ var allListenables = flattenListenables(listenables); for(var key in allListenables){ var cbname = _.callbackName(key),// "on"+key localname = this[cbname] ? cbname : this[key] ? key : undefined;// 绑定函数方法名,对应store中的同名方法 if (localname){ // allListenables[key]为通过createAction创建的functor仿函数对象,用以绑定、触发事件 this.listenTo(allListenables[key], localname, this[cbname + "Default"] || this[localname + "Default"] || localname); } } }; // 校验listenable,可能是store实例或action仿函数对象,不能形成循环绑定事件、须有listen方法 export var validateListening = function(listenable){ if (listenable === this) { return "Listener is not able to listen to itself"; } if (!_.isFunction(listenable.listen)) { return listenable + " is missing a listen method"; } if (listenable.hasListener && listenable.hasListener(this)) { return "Listener cannot listen to this listenable because of circular loop"; } }; // 绑定事件 export var listenTo = function(listenable, callback, defaultCallback) { var desub, unsubscriber, subscriptionobj, subs = this.subscriptions = this.subscriptions || []; _.throwIf(this.validateListening(listenable)); // 触发listenable的getInitialState方法,紧接着触发defaultCallback,处理当前store实例 this.fetchInitialState(listenable, defaultCallback); desub = listenable.listen(this[callback] || callback, this); // 解绑事件 unsubscriber = function() { var index = subs.indexOf(subscriptionobj); _.throwIf(index === -1, "Tried to remove listen already gone from subscriptions list!"); subs.splice(index, 1); desub(); }; subscriptionobj = { stop: unsubscriber, listenable: listenable }; // this.subscriptions存储解绑事件的函数、以及绑定的listenable subs.push(subscriptionobj); return subscriptionobj; }; // 根据listenable解绑事件 export var stopListeningTo = function(listenable){ var sub, i = 0, subs = this.subscriptions || []; for(;i < subs.length; i++){ sub = subs[i]; if (sub.listenable === listenable){ sub.stop(); _.throwIf(subs.indexOf(sub) !== -1, "Failed to remove listen from subscriptions list!"); return true; } } return false; }; // 解绑所有事件 export var stopListeningToAll = function(){ var remaining, subs = this.subscriptions || []; while((remaining = subs.length)){ subs[0].stop(); _.throwIf(subs.length !== remaining - 1, "Failed to remove listen from subscriptions list!"); } }; // 当store A监听store B时,store B中含getInitialState // defaultCallback回调将使store A获取store B的getInitialState情状进行后续操作 export var fetchInitialState = function(listenable, defaultCallback) { defaultCallback = (defaultCallback && this[defaultCallback]) || defaultCallback; var me = this; if (_.isFunction(defaultCallback) && _.isFunction(listenable.getInitialState)) { var data = listenable.getInitialState(); if (data && _.isFunction(data.then)) { data.then(function() { defaultCallback.apply(me, arguments); }); } else { defaultCallback.call(this, data); } } }; // joinTrailing( listenables...,callback) // 多个监听器构成的组合事件中,若单个监听器的事件触发多次时,回调中传参取该监听器接收的第一个参数 export var joinTrailing = maker("last"); // 多个监听器构成的组合事件中,若单个监听器的事件触发多次时,回调中传参取该监听器接收的第一个参数 export var joinLeading = maker("first"); // 多个监听器构成的组合事件中,若单个监听器的事件触发多次时,回调中传参取该监听器接收的所有参数 export var joinConcat = maker("all"); // 多个监听器构成的组合事件中,单个监听器的事件只能触发一次 export var joinStrict = maker("strict");
6.joins.js
/** * Internal module used to create static and instance join methods */ import {createStore} from "./createStore"; import * as _ from "./utils"; var slice = Array.prototype.slice, strategyMethodNames = { strict: "joinStrict", first: "joinLeading", last: "joinTrailing", all: "joinConcat" }; /** * Used in `index.js` to create the static join methods * @param {String} strategy Which strategy to use when tracking listenable trigger arguments * @returns {Function} A static function which returns a store with a join listen on the given listenables using the given strategy */ export function staticJoinCreator(strategy){ return function(/* listenables... */) { var listenables = slice.call(arguments); return createStore({ init: function(){ this[strategyMethodNames[strategy]].apply(this, listenables.concat("triggerAsync")); } }); }; } // 多个listenables绑定组合事件 export function instanceJoinCreator(strategy){ return function(/* listenables..., callback*/){ _.throwIf(arguments.length < 2, "Cannot create a join with less than 2 listenables!"); var listenables = slice.call(arguments), callback = listenables.pop(), numberOfListenables = listenables.length, join = { numberOfListenables: numberOfListenables, callback: this[callback] || callback, listener: this, strategy: strategy }, i, cancels = [], subobj; for (i = 0; i < numberOfListenables; i++) { _.throwIf(this.validateListening(listenables[i])); } for (i = 0; i < numberOfListenables; i++) { // listen方法参数回调函数、上下文 cancels.push(listenables[i].listen(newListener(i, join), this)); } reset(join); subobj = {listenable: listenables}; subobj.stop = makeStopper(subobj, cancels, this); this.subscriptions = (this.subscriptions || []).concat(subobj); return subobj; }; } // ---- internal join functions ---- // 解绑事件 function makeStopper(subobj, cancels, context){ return function() { var i, subs = context.subscriptions, index = (subs ? subs.indexOf(subobj) : -1); _.throwIf(index === -1, "Tried to remove join already gone from subscriptions list!"); for(i = 0; i < cancels.length; i++){ cancels[i](); } subs.splice(index, 1); }; } // 重置listenable的触发状况和传参 function reset(join) { join.listenablesEmitted = new Array(join.numberOfListenables); join.args = new Array(join.numberOfListenables); } // 同一个listenable多次触发,"last"取最后一次传参,"all"取所有传参,默认第一次传参 function newListener(i, join) { return function() { var callargs = slice.call(arguments); if (join.listenablesEmitted[i]){ switch(join.strategy){ case "strict": throw new Error("Strict join failed because listener triggered twice."); case "last": join.args[i] = callargs; break; case "all": join.args[i].push(callargs); } } else { join.listenablesEmitted[i] = true; join.args[i] = (join.strategy === "all" ? [callargs] : callargs); } emitIfAllListenablesEmitted(join); }; } // 多个listenable事件触发后,执行callback回调函数,传参为传入多个listenable事件的参数对象 function emitIfAllListenablesEmitted(join) { for (var i = 0; i < join.numberOfListenables; i++) { if (!join.listenablesEmitted[i]) { return; } } join.callback.apply(join.listener, join.args); reset(join); }
7.mixer.js
import * as _ from "./utils"; // 类似react的mixins,多个init、preEmit、shouldEmit或其他mixins中方法均将执行 export function mix(def) { var composed = { init: [], preEmit: [], shouldEmit: [] }; var updated = (function mixDef(mixin) { var mixed = {}; if (mixin.mixins) { mixin.mixins.forEach(function (subMixin) { _.extend(mixed, mixDef(subMixin)); }); } _.extend(mixed, mixin); Object.keys(composed).forEach(function (composable) { if (mixin.hasOwnProperty(composable)) { composed[composable].push(mixin[composable]); } }); return mixed; }(def)); if (composed.init.length > 1) { updated.init = function () { var args = arguments; composed.init.forEach(function (init) { init.apply(this, args); }, this); }; } if (composed.preEmit.length > 1) { updated.preEmit = function () { return composed.preEmit.reduce(function (args, preEmit) { var newValue = preEmit.apply(this, args); return newValue === undefined ? args : [newValue]; }.bind(this), arguments); }; } if (composed.shouldEmit.length > 1) { updated.shouldEmit = function () { var args = arguments; return !composed.shouldEmit.some(function (shouldEmit) { return !shouldEmit.apply(this, args); }, this); }; } Object.keys(composed).forEach(function (composable) { if (composed[composable].length === 1) { updated[composable] = composed[composable][0]; } }); return updated; }
8.bindMethods.js
export function bindMethods(store, definition) { for (var name in definition) { if (Object.getOwnPropertyDescriptor && Object.defineProperty) { var propertyDescriptor = Object.getOwnPropertyDescriptor(definition, name); if (!propertyDescriptor.value || typeof propertyDescriptor.value !== "function" || !definition.hasOwnProperty(name)) { continue; } store[name] = definition[name].bind(store); } else { var property = definition[name]; if (typeof property !== "function" || !definition.hasOwnProperty(name)) { continue; } store[name] = property.bind(store); } } return store; }
9.utils.js
export function capitalize(string) { return string.charAt(0).toUpperCase() + string.slice(1); } export function callbackName(string, prefix) { prefix = prefix || "on"; return prefix + exports.capitalize(string); } /* * isObject, extend, isFunction, isArguments are taken from underscore/lodash in * order to remove the dependency */ export function isObject(obj) { const type = typeof obj; return type === "function" || type === "object" && !!obj; } export function extend(obj) { if (!isObject(obj)) { return obj; } var source, keys, prop; for (var i = 1, length = arguments.length; i < length; i++) { source = arguments[i]; keys = Object.keys(source); for (var j = 0; j < keys.length; j++) { prop = keys[j]; if (Object.getOwnPropertyDescriptor && Object.defineProperty) { var propertyDescriptor = Object.getOwnPropertyDescriptor(source, prop); Object.defineProperty(obj, prop, propertyDescriptor); } else { obj[prop] = source[prop]; } } } return obj; } export function isFunction(value) { return typeof value === "function"; } exports.EventEmitter = require("eventemitter3"); export function nextTick(callback) { setTimeout(callback, 0); } export function object(keys, vals){ var o = {}, i = 0; for(;i < keys.length; i++){ o[keys[i]] = vals[i]; } return o; } export function isArguments(value) { return typeof value === "object" && ("callee" in value) && typeof value.length === "number"; } export function throwIf(val, msg){ if (val){ throw Error(msg || val); } }
10.Keep.js
// 记录及操作已添加的action、store var use = false; const createdStores = []; const createdActions = []; function useKeep(bool = true) { use = bool; } function addStore(str) { if (use) { createdStores.push(str); } } function addAction(act) { if (use) { createdActions.push(act); } } function reset() { while(createdStores.length) { createdStores.pop(); } while(createdActions.length) { createdActions.pop(); } } export { useKeep, addStore, addAction, createdStores, createdActions, reset };
11.actionMethods.js、storeMethods.js
export {};