├── src ├── router.js ├── innerPlugins.js ├── utils │ ├── promiseWrapper.js │ ├── url.js │ ├── wrapState.js │ ├── is.js │ └── manipulate.js ├── polyfill │ ├── index.js │ ├── string.prototype.startsWith.js │ ├── string.prototype.includes.js │ ├── array.prototype.findIndex.js │ ├── array.prototype.find.js │ └── array.prototype.includes.js ├── mixins │ └── default.js ├── provider.js ├── logger.js ├── mapGettersToState.js ├── storeConfigPreHandle.js ├── wrapDataInstance.js ├── dataTransform.js ├── mapHelpersToMethod.js ├── connect.js ├── global.js ├── emitter.js ├── createHelpers.js └── index.js ├── .eslintignore ├── .gitignore ├── dist ├── router.js ├── polyfill │ ├── string.prototype.startsWith.js │ ├── index.js │ ├── string.prototype.includes.js │ ├── array.prototype.findIndex.js │ ├── array.prototype.find.js │ └── array.prototype.includes.js ├── utils │ ├── promiseWrapper.js │ ├── url.js │ ├── wrapState.js │ ├── is.js │ └── manipulate.js ├── innerPlugins.js ├── mixins │ └── default.js ├── provider.js ├── logger.js ├── storeConfigPreHandle.js ├── mapGettersToState.js ├── wrapDataInstance.js ├── mapHelpersToMethod.js ├── dataTransform.js ├── connect.js ├── global.js ├── emitter.js ├── index.js └── createHelpers.js ├── CHANGELOG.md ├── .eslintrc ├── package.json └── README.md /src/router.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | patch -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /dist/router.js: -------------------------------------------------------------------------------- 1 | "use strict"; -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | ### 0.1.0 3 | -------------------------------------------------------------------------------- /src/innerPlugins.js: -------------------------------------------------------------------------------- 1 | import Logger from './logger'; 2 | 3 | export default { 4 | logger: Logger() 5 | }; 6 | -------------------------------------------------------------------------------- /src/utils/promiseWrapper.js: -------------------------------------------------------------------------------- 1 | // wrapper.js 2 | export default function wrapper(promise) { 3 | return promise 4 | .then(data => [undefined, data]) 5 | .catch(error => [error, undefined]); 6 | } 7 | -------------------------------------------------------------------------------- /src/polyfill/index.js: -------------------------------------------------------------------------------- 1 | import './array.prototype.find'; 2 | import './array.prototype.findIndex'; 3 | import './array.prototype.includes'; 4 | import './string.prototype.includes'; 5 | import './string.prototype.startsWith'; 6 | -------------------------------------------------------------------------------- /src/polyfill/string.prototype.startsWith.js: -------------------------------------------------------------------------------- 1 | if (!String.prototype.startsWith) { 2 | String.prototype.startsWith = function(search, pos) { 3 | return this.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search; 4 | }; 5 | } 6 | -------------------------------------------------------------------------------- /dist/polyfill/string.prototype.startsWith.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | if (!String.prototype.startsWith) { 4 | String.prototype.startsWith = function (search, pos) { 5 | return this.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search; 6 | }; 7 | } -------------------------------------------------------------------------------- /dist/polyfill/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('./array.prototype.find'); 4 | 5 | require('./array.prototype.findIndex'); 6 | 7 | require('./array.prototype.includes'); 8 | 9 | require('./string.prototype.includes'); 10 | 11 | require('./string.prototype.startsWith'); -------------------------------------------------------------------------------- /src/utils/url.js: -------------------------------------------------------------------------------- 1 | import { isObject } from './is'; 2 | 3 | export function buildQueryString(obj) { 4 | if (!obj || !isObject(obj)) { 5 | return ''; 6 | } 7 | return Object.keys(obj).reduce((p, v, i) => { 8 | return `${p}${i ? '&' : ''}${v}=${obj[v]}`; 9 | }, '?'); 10 | }; 11 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "standard", 3 | "globals": { 4 | "getCurrentPages": false 5 | }, 6 | "parser": "babel-eslint", 7 | "rules": { 8 | "semi": ["error", "always"], //必须使用分号 9 | "space-before-function-paren": 0, // function前面的空格没有必要校验, 10 | "no-unused-vars": 0, 11 | "one-var": 0, 12 | "no-extend-native": 0 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /dist/utils/promiseWrapper.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = wrapper; 7 | // wrapper.js 8 | function wrapper(promise) { 9 | return promise.then(function (data) { 10 | return [undefined, data]; 11 | }).catch(function (error) { 12 | return [error, undefined]; 13 | }); 14 | } -------------------------------------------------------------------------------- /dist/innerPlugins.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _logger = require('./logger'); 8 | 9 | var _logger2 = _interopRequireDefault(_logger); 10 | 11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 12 | 13 | exports.default = { 14 | logger: (0, _logger2.default)() 15 | }; -------------------------------------------------------------------------------- /dist/utils/url.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.buildQueryString = buildQueryString; 7 | 8 | var _is = require('./is'); 9 | 10 | function buildQueryString(obj) { 11 | if (!obj || !(0, _is.isObject)(obj)) { 12 | return ''; 13 | } 14 | return Object.keys(obj).reduce(function (p, v, i) { 15 | return '' + p + (i ? '&' : '') + v + '=' + obj[v]; 16 | }, '?'); 17 | }; -------------------------------------------------------------------------------- /src/polyfill/string.prototype.includes.js: -------------------------------------------------------------------------------- 1 | if (!String.prototype.includes) { 2 | Object.defineProperty(String.prototype, 'includes', { 3 | value: function(search, start) { 4 | if (typeof start !== 'number') { 5 | start = 0; 6 | } 7 | 8 | if (start + search.length > this.length) { 9 | return false; 10 | } else { 11 | return this.indexOf(search, start) !== -1; 12 | } 13 | } 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /dist/polyfill/string.prototype.includes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | if (!String.prototype.includes) { 4 | Object.defineProperty(String.prototype, 'includes', { 5 | value: function value(search, start) { 6 | if (typeof start !== 'number') { 7 | start = 0; 8 | } 9 | 10 | if (start + search.length > this.length) { 11 | return false; 12 | } else { 13 | return this.indexOf(search, start) !== -1; 14 | } 15 | } 16 | }); 17 | } -------------------------------------------------------------------------------- /src/mixins/default.js: -------------------------------------------------------------------------------- 1 | import { mapMutationsToMethod } from '../mapHelpersToMethod'; 2 | 3 | export default function(register) { 4 | const config = { 5 | onLoad(data) { 6 | this.dispatch('pageOnLoad', data); 7 | }, 8 | onReady(data) { 9 | this.dispatch('pageOnReady', data); 10 | }, 11 | onShow(data) { 12 | this.dispatch('pageOnShow', data); 13 | }, 14 | onHide(data) { 15 | this.dispatch('pageOnHide', data); 16 | }, 17 | onUnload(data) { 18 | this.dispatch('pageOnUnload', data); 19 | } 20 | }; 21 | mapMutationsToMethod(this.methods, config); 22 | return register(config); 23 | } 24 | -------------------------------------------------------------------------------- /src/provider.js: -------------------------------------------------------------------------------- 1 | import global from './global'; 2 | import configPreHandler from './storeConfigPreHandle'; 3 | 4 | function getPath(link) { 5 | return link && link.split('/')[1]; 6 | } 7 | // 允许空 8 | export default function GlobalStore(config = {}) { 9 | configPreHandler(config); 10 | global.setGlobalStoreConfig(config); 11 | return function(config) { 12 | const _onLoad = config.onLoad; 13 | config.onLoad = function(options) { 14 | global.emitter.emitEvent('updateCurrentPath', { 15 | currentPath: getPath(options.path), 16 | query: {} 17 | }); 18 | _onLoad(options); 19 | }; 20 | return config; 21 | }; 22 | }; 23 | -------------------------------------------------------------------------------- /src/utils/wrapState.js: -------------------------------------------------------------------------------- 1 | const reserveWord = ['getIn', 'setIn', 'deleteIn', '$update', '$produce', 'compose']; 2 | 3 | function omit(data) { 4 | return Object.keys(data).filter(d => reserveWord.indexOf(d) < 0).reduce((p, v) => { 5 | p[v] = data[v]; 6 | return p; 7 | }, {}); 8 | } 9 | // 打印时去除这类无聊信息 10 | function wrapState(state) { 11 | const filteredNewState = omit(state); 12 | if (filteredNewState.$getters) { 13 | filteredNewState.$getters = omit(filteredNewState.$getters); 14 | } 15 | if (filteredNewState.$global) { 16 | filteredNewState.$global = omit(filteredNewState.$global); 17 | } 18 | return filteredNewState; 19 | } 20 | 21 | export default wrapState; 22 | -------------------------------------------------------------------------------- /dist/utils/wrapState.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | var reserveWord = ['getIn', 'setIn', 'deleteIn', '$update', '$produce', 'compose']; 7 | 8 | function omit(data) { 9 | return Object.keys(data).filter(function (d) { 10 | return reserveWord.indexOf(d) < 0; 11 | }).reduce(function (p, v) { 12 | p[v] = data[v]; 13 | return p; 14 | }, {}); 15 | } 16 | // 打印时去除这类无聊信息 17 | function wrapState(state) { 18 | var filteredNewState = omit(state); 19 | if (filteredNewState.$getters) { 20 | filteredNewState.$getters = omit(filteredNewState.$getters); 21 | } 22 | if (filteredNewState.$global) { 23 | filteredNewState.$global = omit(filteredNewState.$global); 24 | } 25 | return filteredNewState; 26 | } 27 | 28 | exports.default = wrapState; -------------------------------------------------------------------------------- /dist/mixins/default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | exports.default = function (register) { 8 | var config = { 9 | onLoad: function onLoad(data) { 10 | this.dispatch('pageOnLoad', data); 11 | }, 12 | onReady: function onReady(data) { 13 | this.dispatch('pageOnReady', data); 14 | }, 15 | onShow: function onShow(data) { 16 | this.dispatch('pageOnShow', data); 17 | }, 18 | onHide: function onHide(data) { 19 | this.dispatch('pageOnHide', data); 20 | }, 21 | onUnload: function onUnload(data) { 22 | this.dispatch('pageOnUnload', data); 23 | } 24 | }; 25 | (0, _mapHelpersToMethod.mapMutationsToMethod)(this.methods, config); 26 | return register(config); 27 | }; 28 | 29 | var _mapHelpersToMethod = require('../mapHelpersToMethod'); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "herculex", 3 | "version": "0.2.15", 4 | "description": "Simple, predictable, developer friendly state management for alipay mini-program", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "lint": "eslint src", 8 | "lintf": "eslint src --fix" 9 | }, 10 | "prepublish": "npm run build && npm version patch", 11 | "author": "candy.zhengxq@gmail.com", 12 | "license": "ISC", 13 | "files": [ 14 | "dist" 15 | ], 16 | "pre-commit": [ 17 | "lint" 18 | ], 19 | "devDependencies": { 20 | "babel-eslint": "~7.1.1", 21 | "eslint": "~3.5.0", 22 | "eslint-config-standard": "~6.0.1", 23 | "eslint-plugin-promise": "^3.5.0", 24 | "eslint-plugin-standard": "^2.1.1", 25 | "pre-commit": "^1.2.2" 26 | }, 27 | "dependencies": { 28 | "immutability-helper-enhanced": "^2.8.1", 29 | "immer": "^1.7.2" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/logger.js: -------------------------------------------------------------------------------- 1 | import { isString, isSymbol } from './utils/is'; 2 | 3 | export default function logger (option = {}) { 4 | return function (store) { 5 | store.subscribe((mutation, state, prevState) => { 6 | const payload = isString(mutation.payload) ? mutation.payload : { ...mutation.payload }; 7 | console.info(`%c ${store.instanceName}Store:prev state`, 'color: #9E9E9E; font-weight: bold', prevState); 8 | console.info(`%c ${store.instanceName}Store:mutation: ${mutation.type}`, 'color: #03A9F4; font-weight: bold', payload, new Date().getTime()); 9 | console.info(`%c ${store.instanceName}Store:next state`, 'color: #4CAF50; font-weight: bold', state); 10 | }, (action = {}, next) => { 11 | let type = isSymbol(action.type) ? action.type.toString() : action.type; 12 | const payload = isString(action.payload) ? action.payload : { ...action.payload }; 13 | console.info(`%c ${store.instanceName}Store:action ${type} dispatching`, 'color: #9E9E9E; font-weight: bold', payload); 14 | next(); 15 | }); 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /dist/provider.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = GlobalStore; 7 | 8 | var _global = require('./global'); 9 | 10 | var _global2 = _interopRequireDefault(_global); 11 | 12 | var _storeConfigPreHandle = require('./storeConfigPreHandle'); 13 | 14 | var _storeConfigPreHandle2 = _interopRequireDefault(_storeConfigPreHandle); 15 | 16 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 17 | 18 | function getPath(link) { 19 | return link && link.split('/')[1]; 20 | } 21 | // 允许空 22 | function GlobalStore() { 23 | var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 24 | 25 | (0, _storeConfigPreHandle2.default)(config); 26 | _global2.default.setGlobalStoreConfig(config); 27 | return function (config) { 28 | var _onLoad = config.onLoad; 29 | config.onLoad = function (options) { 30 | _global2.default.emitter.emitEvent('updateCurrentPath', { 31 | currentPath: getPath(options.path), 32 | query: {} 33 | }); 34 | _onLoad(options); 35 | }; 36 | return config; 37 | }; 38 | }; -------------------------------------------------------------------------------- /src/mapGettersToState.js: -------------------------------------------------------------------------------- 1 | import { isFunc } from './utils/is'; 2 | import wrapInstance from './wrapDataInstance'; 3 | import global from './global'; 4 | 5 | function filterObjectByKey(array, object) { 6 | return array.reduce((p, v) => { 7 | if (object && object[v] !== undefined) { 8 | p[v] = object[v]; 9 | } 10 | return p; 11 | }, {}); 12 | }; 13 | 14 | export default function mapGettersToState(state, getters = {}, store) { 15 | const result = { ...state }; 16 | result.$getters = Object.keys(getters).reduce((p, v) => { 17 | const funcExec = getters[v]; 18 | p[v] = {}; 19 | Object.defineProperty(p, v, { 20 | get: function() { 21 | const globalData = store.connectGlobal ? global.getGlobalState(store.mapGlobal) : {}; 22 | const instance = store.getInstance() ? (store.getInstance().state || {}) : (this || {}); 23 | if (isFunc(funcExec)) { 24 | const params = filterObjectByKey(Object.keys(state), instance); 25 | return funcExec.call(this, wrapInstance(params), wrapInstance(instance.$getters), wrapInstance(globalData), global.getState); 26 | } 27 | return funcExec; 28 | } 29 | }); 30 | return p; 31 | }, {}); 32 | return result; 33 | } 34 | -------------------------------------------------------------------------------- /src/polyfill/array.prototype.findIndex.js: -------------------------------------------------------------------------------- 1 | // https://tc39.github.io/ecma262/#sec-array.prototype.findindex 2 | if (!Array.prototype.findIndex) { 3 | Object.defineProperty(Array.prototype, 'findIndex', { 4 | value: function(predicate) { 5 | // 1. Let O be ? ToObject(this value). 6 | if (this == null) { 7 | throw new TypeError('"this" is null or not defined'); 8 | } 9 | 10 | var o = Object(this); 11 | 12 | // 2. Let len be ? ToLength(? Get(O, "length")). 13 | var len = o.length >>> 0; 14 | 15 | // 3. If IsCallable(predicate) is false, throw a TypeError exception. 16 | if (typeof predicate !== 'function') { 17 | throw new TypeError('predicate must be a function'); 18 | } 19 | 20 | // 4. If thisArg was supplied, let T be thisArg; else let T be undefined. 21 | var thisArg = arguments[1]; 22 | 23 | // 5. Let k be 0. 24 | var k = 0; 25 | 26 | // 6. Repeat, while k < len 27 | while (k < len) { 28 | // a. Let Pk be ! ToString(k). 29 | // b. Let kValue be ? Get(O, Pk). 30 | // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)). 31 | // d. If testResult is true, return k. 32 | var kValue = o[k]; 33 | if (predicate.call(thisArg, kValue, k, o)) { 34 | return k; 35 | } 36 | // e. Increase k by 1. 37 | k++; 38 | } 39 | 40 | // 7. Return -1. 41 | return -1; 42 | }, 43 | configurable: true, 44 | writable: true 45 | }); 46 | } 47 | -------------------------------------------------------------------------------- /dist/polyfill/array.prototype.findIndex.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // https://tc39.github.io/ecma262/#sec-array.prototype.findindex 4 | if (!Array.prototype.findIndex) { 5 | Object.defineProperty(Array.prototype, 'findIndex', { 6 | value: function value(predicate) { 7 | // 1. Let O be ? ToObject(this value). 8 | if (this == null) { 9 | throw new TypeError('"this" is null or not defined'); 10 | } 11 | 12 | var o = Object(this); 13 | 14 | // 2. Let len be ? ToLength(? Get(O, "length")). 15 | var len = o.length >>> 0; 16 | 17 | // 3. If IsCallable(predicate) is false, throw a TypeError exception. 18 | if (typeof predicate !== 'function') { 19 | throw new TypeError('predicate must be a function'); 20 | } 21 | 22 | // 4. If thisArg was supplied, let T be thisArg; else let T be undefined. 23 | var thisArg = arguments[1]; 24 | 25 | // 5. Let k be 0. 26 | var k = 0; 27 | 28 | // 6. Repeat, while k < len 29 | while (k < len) { 30 | // a. Let Pk be ! ToString(k). 31 | // b. Let kValue be ? Get(O, Pk). 32 | // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)). 33 | // d. If testResult is true, return k. 34 | var kValue = o[k]; 35 | if (predicate.call(thisArg, kValue, k, o)) { 36 | return k; 37 | } 38 | // e. Increase k by 1. 39 | k++; 40 | } 41 | 42 | // 7. Return -1. 43 | return -1; 44 | }, 45 | configurable: true, 46 | writable: true 47 | }); 48 | } -------------------------------------------------------------------------------- /src/polyfill/array.prototype.find.js: -------------------------------------------------------------------------------- 1 | // https://tc39.github.io/ecma262/#sec-array.prototype.find 2 | if (!Array.prototype.find) { 3 | console.log('add polyfill find'); 4 | Object.defineProperty(Array.prototype, 'find', { 5 | value: function(predicate) { 6 | // 1. Let O be ? ToObject(this value). 7 | if (this == null) { 8 | throw new TypeError('"this" is null or not defined'); 9 | } 10 | 11 | var o = Object(this); 12 | 13 | // 2. Let len be ? ToLength(? Get(O, "length")). 14 | var len = o.length >>> 0; 15 | 16 | // 3. If IsCallable(predicate) is false, throw a TypeError exception. 17 | if (typeof predicate !== 'function') { 18 | throw new TypeError('predicate must be a function'); 19 | } 20 | 21 | // 4. If thisArg was supplied, let T be thisArg; else let T be undefined. 22 | var thisArg = arguments[1]; 23 | 24 | // 5. Let k be 0. 25 | var k = 0; 26 | 27 | // 6. Repeat, while k < len 28 | while (k < len) { 29 | // a. Let Pk be ! ToString(k). 30 | // b. Let kValue be ? Get(O, Pk). 31 | // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)). 32 | // d. If testResult is true, return kValue. 33 | var kValue = o[k]; 34 | if (predicate.call(thisArg, kValue, k, o)) { 35 | return kValue; 36 | } 37 | // e. Increase k by 1. 38 | k++; 39 | } 40 | 41 | // 7. Return undefined. 42 | return undefined; 43 | }, 44 | configurable: true, 45 | writable: true 46 | }); 47 | } 48 | -------------------------------------------------------------------------------- /src/storeConfigPreHandle.js: -------------------------------------------------------------------------------- 1 | import { isArray, isFunc } from './utils/is'; 2 | 3 | function shouldImmutableDecorate(f, config) { 4 | if (f._shouldImmutable || f._shouldImmutable === false) { 5 | return; 6 | } 7 | const functionString = f.toString(); 8 | // 当 mutation 写了 return 语句,则自己保证其 immutable,建议就使用提供的 immutable-helper 9 | if (config.$useImmer || !/return /gm.test(functionString)) { 10 | f._shouldImmutable = true; 11 | } 12 | } 13 | 14 | function wrapMutation(config) { 15 | Object.values(config).forEach(func => { 16 | shouldImmutableDecorate(func, config); 17 | }); 18 | } 19 | 20 | function configPreHandler(config) { 21 | // 防空 22 | config.state = config.state || {}; 23 | config.mutations = config.mutations || {}; 24 | config.actions = config.actions || {}; 25 | // 给插件提供修改初始配置的能力 26 | if (config.plugins) { 27 | config.plugins = config.plugins.map(plugin => { 28 | if (isArray(plugin)) { 29 | if (isFunc(plugin[1])) { 30 | plugin[1](config); 31 | } else { 32 | Object.assign(config, plugin[1]); 33 | } 34 | return plugin[0]; 35 | } 36 | return plugin; 37 | }); 38 | } 39 | // 给 mutaiton 包装是否需要 immer 操作 40 | if (config.mutations) { 41 | wrapMutation(config.mutations); 42 | } 43 | if (config.services) { 44 | const serviceRenameObj = Object.keys(config.services).reduce((p, v) => { 45 | p[`$service:${v}`] = config.services[v]; 46 | return p; 47 | }, {}); 48 | Object.assign(config.actions, serviceRenameObj); 49 | } 50 | } 51 | 52 | export default configPreHandler; 53 | -------------------------------------------------------------------------------- /dist/polyfill/array.prototype.find.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // https://tc39.github.io/ecma262/#sec-array.prototype.find 4 | if (!Array.prototype.find) { 5 | console.log('add polyfill find'); 6 | Object.defineProperty(Array.prototype, 'find', { 7 | value: function value(predicate) { 8 | // 1. Let O be ? ToObject(this value). 9 | if (this == null) { 10 | throw new TypeError('"this" is null or not defined'); 11 | } 12 | 13 | var o = Object(this); 14 | 15 | // 2. Let len be ? ToLength(? Get(O, "length")). 16 | var len = o.length >>> 0; 17 | 18 | // 3. If IsCallable(predicate) is false, throw a TypeError exception. 19 | if (typeof predicate !== 'function') { 20 | throw new TypeError('predicate must be a function'); 21 | } 22 | 23 | // 4. If thisArg was supplied, let T be thisArg; else let T be undefined. 24 | var thisArg = arguments[1]; 25 | 26 | // 5. Let k be 0. 27 | var k = 0; 28 | 29 | // 6. Repeat, while k < len 30 | while (k < len) { 31 | // a. Let Pk be ! ToString(k). 32 | // b. Let kValue be ? Get(O, Pk). 33 | // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)). 34 | // d. If testResult is true, return kValue. 35 | var kValue = o[k]; 36 | if (predicate.call(thisArg, kValue, k, o)) { 37 | return kValue; 38 | } 39 | // e. Increase k by 1. 40 | k++; 41 | } 42 | 43 | // 7. Return undefined. 44 | return undefined; 45 | }, 46 | configurable: true, 47 | writable: true 48 | }); 49 | } -------------------------------------------------------------------------------- /src/wrapDataInstance.js: -------------------------------------------------------------------------------- 1 | import { getIn, setIn, deleteIn, compose, produce, update } from './utils/manipulate'; 2 | import { isArray, isString } from './utils/is'; 3 | export default function(instance = {}, context) { 4 | // 当实例不是引用则不做wrap 5 | if (isString(instance) || typeof instance === 'number' || typeof instance === 'boolean') return instance; 6 | instance.getIn = function(path, initial, ...funcs) { 7 | const ctx = context ? context.data : this; 8 | const pathArray = isString(path) ? [path] : path; 9 | const result = getIn(ctx, pathArray, initial); 10 | if (funcs.length) { 11 | return compose([result].concat(funcs)); 12 | } 13 | return result; 14 | }; 15 | instance.setIn = function(path, initial) { 16 | const ctx = context ? context.data : this; 17 | const pathArray = isString(path) ? [path] : path; 18 | return setIn(ctx, pathArray, initial); 19 | }; 20 | instance.deleteIn = function(path) { 21 | const ctx = context ? context.data : this; 22 | const pathArray = isString(path) ? [path] : path; 23 | return deleteIn(ctx, pathArray); 24 | }; 25 | // use immutablity helper 26 | instance.$update = function(manipulate) { 27 | const ctx = context ? context.data : this; 28 | return update(ctx, manipulate); 29 | }; 30 | // use immer 31 | instance.$produce = function(manipulate) { 32 | const ctx = context ? context.data : this; 33 | return produce(ctx, manipulate); 34 | }; 35 | 36 | instance.compose = function(...args) { 37 | const ctx = context ? context.data : this; 38 | let composeArray = isArray(args[0]) ? args[0] : args; 39 | composeArray.unshift(ctx); 40 | return compose(composeArray); 41 | }; 42 | return instance; 43 | } 44 | -------------------------------------------------------------------------------- /src/polyfill/array.prototype.includes.js: -------------------------------------------------------------------------------- 1 | // https://tc39.github.io/ecma262/#sec-array.prototype.includes 2 | if (!Array.prototype.includes) { 3 | console.log('add polyfill includes'); 4 | Object.defineProperty(Array.prototype, 'includes', { 5 | value: function(searchElement, fromIndex) { 6 | if (this == null) { 7 | throw new TypeError('"this" is null or not defined'); 8 | } 9 | 10 | // 1. Let O be ? ToObject(this value). 11 | var o = Object(this); 12 | 13 | // 2. Let len be ? ToLength(? Get(O, "length")). 14 | var len = o.length >>> 0; 15 | 16 | // 3. If len is 0, return false. 17 | if (len === 0) { 18 | return false; 19 | } 20 | 21 | // 4. Let n be ? ToInteger(fromIndex). 22 | // (If fromIndex is undefined, this step produces the value 0.) 23 | var n = fromIndex | 0; 24 | 25 | // 5. If n ≥ 0, then 26 | // a. Let k be n. 27 | // 6. Else n < 0, 28 | // a. Let k be len + n. 29 | // b. If k < 0, let k be 0. 30 | var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0); 31 | 32 | function sameValueZero(x, y) { 33 | return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y)); 34 | } 35 | 36 | // 7. Repeat, while k < len 37 | while (k < len) { 38 | // a. Let elementK be the result of ? Get(O, ! ToString(k)). 39 | // b. If SameValueZero(searchElement, elementK) is true, return true. 40 | if (sameValueZero(o[k], searchElement)) { 41 | return true; 42 | } 43 | // c. Increase k by 1. 44 | k++; 45 | } 46 | 47 | // 8. Return false 48 | return false; 49 | } 50 | }); 51 | } 52 | -------------------------------------------------------------------------------- /dist/polyfill/array.prototype.includes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // https://tc39.github.io/ecma262/#sec-array.prototype.includes 4 | if (!Array.prototype.includes) { 5 | console.log('add polyfill includes'); 6 | Object.defineProperty(Array.prototype, 'includes', { 7 | value: function value(searchElement, fromIndex) { 8 | if (this == null) { 9 | throw new TypeError('"this" is null or not defined'); 10 | } 11 | 12 | // 1. Let O be ? ToObject(this value). 13 | var o = Object(this); 14 | 15 | // 2. Let len be ? ToLength(? Get(O, "length")). 16 | var len = o.length >>> 0; 17 | 18 | // 3. If len is 0, return false. 19 | if (len === 0) { 20 | return false; 21 | } 22 | 23 | // 4. Let n be ? ToInteger(fromIndex). 24 | // (If fromIndex is undefined, this step produces the value 0.) 25 | var n = fromIndex | 0; 26 | 27 | // 5. If n ≥ 0, then 28 | // a. Let k be n. 29 | // 6. Else n < 0, 30 | // a. Let k be len + n. 31 | // b. If k < 0, let k be 0. 32 | var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0); 33 | 34 | function sameValueZero(x, y) { 35 | return x === y || typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y); 36 | } 37 | 38 | // 7. Repeat, while k < len 39 | while (k < len) { 40 | // a. Let elementK be the result of ? Get(O, ! ToString(k)). 41 | // b. If SameValueZero(searchElement, elementK) is true, return true. 42 | if (sameValueZero(o[k], searchElement)) { 43 | return true; 44 | } 45 | // c. Increase k by 1. 46 | k++; 47 | } 48 | 49 | // 8. Return false 50 | return false; 51 | } 52 | }); 53 | } -------------------------------------------------------------------------------- /dist/logger.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _assign = require('babel-runtime/core-js/object/assign'); 8 | 9 | var _assign2 = _interopRequireDefault(_assign); 10 | 11 | var _extends = _assign2.default || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; 12 | 13 | exports.default = logger; 14 | 15 | var _is = require('./utils/is'); 16 | 17 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 18 | 19 | function logger() { 20 | var option = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 21 | 22 | return function (store) { 23 | store.subscribe(function (mutation, state, prevState) { 24 | var payload = (0, _is.isString)(mutation.payload) ? mutation.payload : _extends({}, mutation.payload); 25 | console.info('%c ' + store.instanceName + 'Store:prev state', 'color: #9E9E9E; font-weight: bold', prevState); 26 | console.info('%c ' + store.instanceName + 'Store:mutation: ' + mutation.type, 'color: #03A9F4; font-weight: bold', payload, new Date().getTime()); 27 | console.info('%c ' + store.instanceName + 'Store:next state', 'color: #4CAF50; font-weight: bold', state); 28 | }, function () { 29 | var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 30 | var next = arguments[1]; 31 | 32 | var type = (0, _is.isSymbol)(action.type) ? action.type.toString() : action.type; 33 | var payload = (0, _is.isString)(action.payload) ? action.payload : _extends({}, action.payload); 34 | console.info('%c ' + store.instanceName + 'Store:action ' + type + ' dispatching', 'color: #9E9E9E; font-weight: bold', payload); 35 | next(); 36 | }); 37 | }; 38 | } -------------------------------------------------------------------------------- /src/utils/is.js: -------------------------------------------------------------------------------- 1 | // {%TITLE=判断%} 2 | 3 | // -------------------- 常用数据类型判断 ------------------------------ 4 | 5 | // 输入任意类型, 判断是否是 array 类型 6 | var isArray = Array.isArray || function isArray(obj) { 7 | return Object.prototype.toString.call(obj) === '[object Array]'; 8 | }; 9 | 10 | // 判断是否为 object 对象 11 | /** 12 | * Solves equations of the form a * x = b 13 | * @example Example usage of method1. 14 | * {%isObject%} 15 | * @returns {Number} Returns the value of x for the equation. 16 | */ 17 | function isObject(obj) { 18 | return Object.prototype.toString.call(obj) === '[object Object]'; 19 | }; 20 | 21 | function isString(str) { 22 | return Object.prototype.toString.call(str) === '[object String]'; 23 | }; 24 | 25 | function isPromise(e) { 26 | return !!e && typeof e.then === 'function'; 27 | }; 28 | 29 | function isSymbol(d) { 30 | return Object.prototype.toString.call(d) === '[object Symbol]'; 31 | } 32 | 33 | function isFunc(fuc) { 34 | const t = Object.prototype.toString.call(fuc); 35 | return t === '[object Function]' || t === '[object AsyncFunction]'; 36 | } 37 | // TODO: is empty 38 | 39 | function isEmptyObject(obj) { 40 | if (!isObject(obj)) { 41 | return false; 42 | } 43 | return !Object.keys(obj).length; 44 | } 45 | 46 | function canParseJson(string) { 47 | try { 48 | return JSON.parse(string); 49 | } catch (e) { 50 | return false; 51 | } 52 | } 53 | 54 | function isTelNum(mobile) { 55 | return mobile && /^1\d{10}$/.test(mobile); 56 | } 57 | 58 | // ------------------- 常用设备的系统判断, android or ios ------------ 59 | 60 | function isIOS() { 61 | return /iPhone|iTouch|iPad/i.test(navigator.userAgent); 62 | } 63 | 64 | function isAndroid() { 65 | return /android/i.test(navigator.userAgent); 66 | } 67 | 68 | module.exports = { 69 | isArray, 70 | isObject, 71 | isString, 72 | isEmptyObject, 73 | isSymbol, 74 | isFunc, 75 | isPromise, 76 | canParseJson, 77 | // ------- 78 | isTelNum, 79 | // ------ 80 | isIOS, 81 | isAndroid 82 | }; 83 | -------------------------------------------------------------------------------- /dist/storeConfigPreHandle.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _assign = require('babel-runtime/core-js/object/assign'); 8 | 9 | var _assign2 = _interopRequireDefault(_assign); 10 | 11 | var _values = require('babel-runtime/core-js/object/values'); 12 | 13 | var _values2 = _interopRequireDefault(_values); 14 | 15 | var _is = require('./utils/is'); 16 | 17 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 18 | 19 | function shouldImmutableDecorate(f, config) { 20 | if (f._shouldImmutable || f._shouldImmutable === false) { 21 | return; 22 | } 23 | var functionString = f.toString(); 24 | // 当 mutation 写了 return 语句,则自己保证其 immutable,建议就使用提供的 immutable-helper 25 | if (config.$useImmer || !/return /gm.test(functionString)) { 26 | f._shouldImmutable = true; 27 | } 28 | } 29 | 30 | function wrapMutation(config) { 31 | (0, _values2.default)(config).forEach(function (func) { 32 | shouldImmutableDecorate(func, config); 33 | }); 34 | } 35 | 36 | function configPreHandler(config) { 37 | // 防空 38 | config.state = config.state || {}; 39 | config.mutations = config.mutations || {}; 40 | config.actions = config.actions || {}; 41 | // 给插件提供修改初始配置的能力 42 | if (config.plugins) { 43 | config.plugins = config.plugins.map(function (plugin) { 44 | if ((0, _is.isArray)(plugin)) { 45 | if ((0, _is.isFunc)(plugin[1])) { 46 | plugin[1](config); 47 | } else { 48 | (0, _assign2.default)(config, plugin[1]); 49 | } 50 | return plugin[0]; 51 | } 52 | return plugin; 53 | }); 54 | } 55 | // 给 mutaiton 包装是否需要 immer 操作 56 | if (config.mutations) { 57 | wrapMutation(config.mutations); 58 | } 59 | if (config.services) { 60 | var serviceRenameObj = Object.keys(config.services).reduce(function (p, v) { 61 | p['$service:' + v] = config.services[v]; 62 | return p; 63 | }, {}); 64 | (0, _assign2.default)(config.actions, serviceRenameObj); 65 | } 66 | } 67 | 68 | exports.default = configPreHandler; -------------------------------------------------------------------------------- /src/dataTransform.js: -------------------------------------------------------------------------------- 1 | import { isString, isArray, isFunc } from './utils/is'; 2 | import wrapDataInstance from './wrapDataInstance'; 3 | 4 | export function setDataByStateProps(mapStateToProps, data = {}, config, mapGettersToProps = [], instance, next) { 5 | let gettersState = {}; 6 | // data 是增量 7 | const finalData = next ? instance.data : data; 8 | const stateToExpose = wrapDataInstance({ ...finalData }); 9 | const gettersToExpose = wrapDataInstance({ ...finalData.$getters }); 10 | const shouldUpdateKeys = Object.keys(data); 11 | const ownProps = {...this.props}; 12 | 13 | if (mapGettersToProps) { 14 | gettersState = mapGettersToProps.filter(d => !!d).reduce((p, v) => { 15 | p[v] = gettersToExpose ? gettersToExpose[v] : (stateToExpose[v] || undefined); 16 | return p; 17 | }, {}); 18 | } 19 | // 对齐 redux 的用法,第二个为 ownProps,不是很推荐,每次更新都会计算 20 | // TODO: 增加记忆点,暂时开发者自己保证 21 | if (isFunc(mapStateToProps)) { 22 | return mapStateToProps(stateToExpose, wrapDataInstance(ownProps), gettersToExpose); 23 | } 24 | if (isArray(mapStateToProps)) { 25 | // 必须新增部分包含这样的更新 26 | const outterState = mapStateToProps 27 | .filter(d => !!d && shouldUpdateKeys.includes(d)) 28 | .reduce((p, v) => { 29 | p[v] = finalData[v]; 30 | return p; 31 | }, {}); 32 | return { ...outterState, ...gettersState }; 33 | } 34 | const outterState = Object.keys(mapStateToProps).reduce((p, v) => { 35 | if (isString(mapStateToProps[v])) { 36 | if (!shouldUpdateKeys.includes(mapStateToProps[v])) { 37 | // 如果 diff 不包含第二次就不理睬 38 | return p; 39 | } 40 | p[v] = finalData[mapStateToProps[v]]; 41 | } else { 42 | p[v] = mapStateToProps[v](stateToExpose, gettersToExpose, wrapDataInstance(ownProps), stateToExpose.$global, config); 43 | } 44 | return p; 45 | }, {}); 46 | return { ...outterState, ...gettersState }; 47 | } 48 | 49 | export function setStoreDataByState(storeData = {}, state = {}) { 50 | return Object.keys(state).reduce((p, v) => { 51 | p[v] = state[v]; 52 | return p; 53 | }, storeData); 54 | } 55 | -------------------------------------------------------------------------------- /dist/utils/is.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // {%TITLE=判断%} 4 | 5 | // -------------------- 常用数据类型判断 ------------------------------ 6 | 7 | // 输入任意类型, 判断是否是 array 类型 8 | var isArray = Array.isArray || function isArray(obj) { 9 | return Object.prototype.toString.call(obj) === '[object Array]'; 10 | }; 11 | 12 | // 判断是否为 object 对象 13 | /** 14 | * Solves equations of the form a * x = b 15 | * @example Example usage of method1. 16 | * {%isObject%} 17 | * @returns {Number} Returns the value of x for the equation. 18 | */ 19 | function isObject(obj) { 20 | return Object.prototype.toString.call(obj) === '[object Object]'; 21 | }; 22 | 23 | function isString(str) { 24 | return Object.prototype.toString.call(str) === '[object String]'; 25 | }; 26 | 27 | function isPromise(e) { 28 | return !!e && typeof e.then === 'function'; 29 | }; 30 | 31 | function isSymbol(d) { 32 | return Object.prototype.toString.call(d) === '[object Symbol]'; 33 | } 34 | 35 | function isFunc(fuc) { 36 | var t = Object.prototype.toString.call(fuc); 37 | return t === '[object Function]' || t === '[object AsyncFunction]'; 38 | } 39 | // TODO: is empty 40 | 41 | function isEmptyObject(obj) { 42 | if (!isObject(obj)) { 43 | return false; 44 | } 45 | return !Object.keys(obj).length; 46 | } 47 | 48 | function canParseJson(string) { 49 | try { 50 | return JSON.parse(string); 51 | } catch (e) { 52 | return false; 53 | } 54 | } 55 | 56 | function isTelNum(mobile) { 57 | return mobile && /^1\d{10}$/.test(mobile); 58 | } 59 | 60 | // ------------------- 常用设备的系统判断, android or ios ------------ 61 | 62 | function isIOS() { 63 | return (/iPhone|iTouch|iPad/i.test(navigator.userAgent) 64 | ); 65 | } 66 | 67 | function isAndroid() { 68 | return (/android/i.test(navigator.userAgent) 69 | ); 70 | } 71 | 72 | module.exports = { 73 | isArray: isArray, 74 | isObject: isObject, 75 | isString: isString, 76 | isEmptyObject: isEmptyObject, 77 | isSymbol: isSymbol, 78 | isFunc: isFunc, 79 | isPromise: isPromise, 80 | canParseJson: canParseJson, 81 | // ------- 82 | isTelNum: isTelNum, 83 | // ------ 84 | isIOS: isIOS, 85 | isAndroid: isAndroid 86 | }; -------------------------------------------------------------------------------- /dist/mapGettersToState.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _assign = require('babel-runtime/core-js/object/assign'); 8 | 9 | var _assign2 = _interopRequireDefault(_assign); 10 | 11 | var _extends = _assign2.default || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; 12 | 13 | exports.default = mapGettersToState; 14 | 15 | var _is = require('./utils/is'); 16 | 17 | var _wrapDataInstance = require('./wrapDataInstance'); 18 | 19 | var _wrapDataInstance2 = _interopRequireDefault(_wrapDataInstance); 20 | 21 | var _global = require('./global'); 22 | 23 | var _global2 = _interopRequireDefault(_global); 24 | 25 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 26 | 27 | function filterObjectByKey(array, object) { 28 | return array.reduce(function (p, v) { 29 | if (object && object[v] !== undefined) { 30 | p[v] = object[v]; 31 | } 32 | return p; 33 | }, {}); 34 | }; 35 | 36 | function mapGettersToState(state) { 37 | var getters = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 38 | var store = arguments[2]; 39 | 40 | var result = _extends({}, state); 41 | result.$getters = Object.keys(getters).reduce(function (p, v) { 42 | var funcExec = getters[v]; 43 | p[v] = {}; 44 | Object.defineProperty(p, v, { 45 | get: function get() { 46 | var globalData = store.connectGlobal ? _global2.default.getGlobalState(store.mapGlobal) : {}; 47 | var instance = store.getInstance() ? store.getInstance().state || {} : this || {}; 48 | if ((0, _is.isFunc)(funcExec)) { 49 | var params = filterObjectByKey(Object.keys(state), instance); 50 | return funcExec.call(this, (0, _wrapDataInstance2.default)(params), (0, _wrapDataInstance2.default)(instance.$getters), (0, _wrapDataInstance2.default)(globalData), _global2.default.getState); 51 | } 52 | return funcExec; 53 | } 54 | }); 55 | return p; 56 | }, {}); 57 | return result; 58 | } -------------------------------------------------------------------------------- /src/mapHelpersToMethod.js: -------------------------------------------------------------------------------- 1 | import { isArray, isFunc, isObject } from './utils/is'; 2 | import wrapDataInstance from './wrapDataInstance'; 3 | export function mapActionsToMethod(mappers, actions, target) { 4 | if (isArray(mappers)) { 5 | mappers.forEach(element => { 6 | // 强制不校验或校验但不通过 7 | if (actions === false || actions[element]) { 8 | target[element] = function(payload) { 9 | if (isObject(payload)) { 10 | wrapDataInstance(payload); 11 | } 12 | this.dispatch(element, payload); 13 | }; 14 | } 15 | }); 16 | } else if (isFunc(mappers)) { 17 | const result = mappers(this.dispatch, this); 18 | Object.assign(target, result); 19 | } else { 20 | Object.keys(mappers).forEach(element => { 21 | if (isFunc(methodName)) { 22 | target[element] = function(payload) { 23 | if (isObject(payload)) { 24 | wrapDataInstance(payload); 25 | } 26 | methodName.call(this, payload); 27 | }; 28 | return; 29 | } 30 | const methodName = mappers[element]; 31 | if (actions === false || actions[methodName]) { 32 | target[element] = function(e) { 33 | const payload = e; 34 | this.dispatch(methodName, payload); 35 | }; 36 | } 37 | }); 38 | } 39 | }; 40 | 41 | export function mapMutationsToMethod(mappers, target) { 42 | if (isArray(mappers)) { 43 | mappers.forEach(element => { 44 | target[element] = function(payload) { 45 | if (isObject(payload)) { 46 | wrapDataInstance(payload); 47 | } 48 | this.commit(element, payload); 49 | }; 50 | }); 51 | } else if (isFunc(mappers)) { 52 | const result = mappers(this.commit, this); 53 | Object.assign(target, result); 54 | } else { 55 | Object.keys(mappers).forEach(element => { 56 | const methodName = mappers[element]; 57 | if (isFunc(methodName)) { 58 | target[element] = function(payload) { 59 | if (isObject(payload)) { 60 | wrapDataInstance(payload); 61 | } 62 | methodName.call(this, payload); 63 | }; 64 | return; 65 | } 66 | target[element] = function(e) { 67 | const payload = e; 68 | this.commit(methodName, payload); 69 | }; 70 | }); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /dist/wrapDataInstance.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | exports.default = function () { 8 | var instance = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 9 | var context = arguments[1]; 10 | 11 | // 当实例不是引用则不做wrap 12 | if ((0, _is.isString)(instance) || typeof instance === 'number' || typeof instance === 'boolean') return instance; 13 | instance.getIn = function (path, initial) { 14 | var ctx = context ? context.data : this; 15 | var pathArray = (0, _is.isString)(path) ? [path] : path; 16 | var result = (0, _manipulate.getIn)(ctx, pathArray, initial); 17 | 18 | for (var _len = arguments.length, funcs = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { 19 | funcs[_key - 2] = arguments[_key]; 20 | } 21 | 22 | if (funcs.length) { 23 | return (0, _manipulate.compose)([result].concat(funcs)); 24 | } 25 | return result; 26 | }; 27 | instance.setIn = function (path, initial) { 28 | var ctx = context ? context.data : this; 29 | var pathArray = (0, _is.isString)(path) ? [path] : path; 30 | return (0, _manipulate.setIn)(ctx, pathArray, initial); 31 | }; 32 | instance.deleteIn = function (path) { 33 | var ctx = context ? context.data : this; 34 | var pathArray = (0, _is.isString)(path) ? [path] : path; 35 | return (0, _manipulate.deleteIn)(ctx, pathArray); 36 | }; 37 | // use immutablity helper 38 | instance.$update = function (manipulate) { 39 | var ctx = context ? context.data : this; 40 | return (0, _manipulate.update)(ctx, manipulate); 41 | }; 42 | // use immer 43 | instance.$produce = function (manipulate) { 44 | var ctx = context ? context.data : this; 45 | return (0, _manipulate.produce)(ctx, manipulate); 46 | }; 47 | 48 | instance.compose = function () { 49 | var ctx = context ? context.data : this; 50 | 51 | for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { 52 | args[_key2] = arguments[_key2]; 53 | } 54 | 55 | var composeArray = (0, _is.isArray)(args[0]) ? args[0] : args; 56 | composeArray.unshift(ctx); 57 | return (0, _manipulate.compose)(composeArray); 58 | }; 59 | return instance; 60 | }; 61 | 62 | var _manipulate = require('./utils/manipulate'); 63 | 64 | var _is = require('./utils/is'); -------------------------------------------------------------------------------- /dist/mapHelpersToMethod.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _assign = require('babel-runtime/core-js/object/assign'); 8 | 9 | var _assign2 = _interopRequireDefault(_assign); 10 | 11 | exports.mapActionsToMethod = mapActionsToMethod; 12 | exports.mapMutationsToMethod = mapMutationsToMethod; 13 | 14 | var _is = require('./utils/is'); 15 | 16 | var _wrapDataInstance = require('./wrapDataInstance'); 17 | 18 | var _wrapDataInstance2 = _interopRequireDefault(_wrapDataInstance); 19 | 20 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 21 | 22 | function mapActionsToMethod(mappers, actions, target) { 23 | if ((0, _is.isArray)(mappers)) { 24 | mappers.forEach(function (element) { 25 | // 强制不校验或校验但不通过 26 | if (actions === false || actions[element]) { 27 | target[element] = function (payload) { 28 | if ((0, _is.isObject)(payload)) { 29 | (0, _wrapDataInstance2.default)(payload); 30 | } 31 | this.dispatch(element, payload); 32 | }; 33 | } 34 | }); 35 | } else if ((0, _is.isFunc)(mappers)) { 36 | var result = mappers(this.dispatch, this); 37 | (0, _assign2.default)(target, result); 38 | } else { 39 | Object.keys(mappers).forEach(function (element) { 40 | if ((0, _is.isFunc)(methodName)) { 41 | target[element] = function (payload) { 42 | if ((0, _is.isObject)(payload)) { 43 | (0, _wrapDataInstance2.default)(payload); 44 | } 45 | methodName.call(this, payload); 46 | }; 47 | return; 48 | } 49 | var methodName = mappers[element]; 50 | if (actions === false || actions[methodName]) { 51 | target[element] = function (e) { 52 | var payload = e; 53 | this.dispatch(methodName, payload); 54 | }; 55 | } 56 | }); 57 | } 58 | }; 59 | 60 | function mapMutationsToMethod(mappers, target) { 61 | if ((0, _is.isArray)(mappers)) { 62 | mappers.forEach(function (element) { 63 | target[element] = function (payload) { 64 | if ((0, _is.isObject)(payload)) { 65 | (0, _wrapDataInstance2.default)(payload); 66 | } 67 | this.commit(element, payload); 68 | }; 69 | }); 70 | } else if ((0, _is.isFunc)(mappers)) { 71 | var result = mappers(this.commit, this); 72 | (0, _assign2.default)(target, result); 73 | } else { 74 | Object.keys(mappers).forEach(function (element) { 75 | var methodName = mappers[element]; 76 | if ((0, _is.isFunc)(methodName)) { 77 | target[element] = function (payload) { 78 | if ((0, _is.isObject)(payload)) { 79 | (0, _wrapDataInstance2.default)(payload); 80 | } 81 | methodName.call(this, payload); 82 | }; 83 | return; 84 | } 85 | target[element] = function (e) { 86 | var payload = e; 87 | this.commit(methodName, payload); 88 | }; 89 | }); 90 | } 91 | } -------------------------------------------------------------------------------- /dist/dataTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _assign = require('babel-runtime/core-js/object/assign'); 8 | 9 | var _assign2 = _interopRequireDefault(_assign); 10 | 11 | var _extends = _assign2.default || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; 12 | 13 | exports.setDataByStateProps = setDataByStateProps; 14 | exports.setStoreDataByState = setStoreDataByState; 15 | 16 | var _is = require('./utils/is'); 17 | 18 | var _wrapDataInstance = require('./wrapDataInstance'); 19 | 20 | var _wrapDataInstance2 = _interopRequireDefault(_wrapDataInstance); 21 | 22 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 23 | 24 | function setDataByStateProps(mapStateToProps) { 25 | var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 26 | var config = arguments[2]; 27 | var mapGettersToProps = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : []; 28 | var instance = arguments[4]; 29 | var next = arguments[5]; 30 | 31 | var gettersState = {}; 32 | // data 是增量 33 | var finalData = next ? instance.data : data; 34 | var stateToExpose = (0, _wrapDataInstance2.default)(_extends({}, finalData)); 35 | var gettersToExpose = (0, _wrapDataInstance2.default)(_extends({}, finalData.$getters)); 36 | var shouldUpdateKeys = Object.keys(data); 37 | var ownProps = _extends({}, this.props); 38 | 39 | if (mapGettersToProps) { 40 | gettersState = mapGettersToProps.filter(function (d) { 41 | return !!d; 42 | }).reduce(function (p, v) { 43 | p[v] = gettersToExpose ? gettersToExpose[v] : stateToExpose[v] || undefined; 44 | return p; 45 | }, {}); 46 | } 47 | // 对齐 redux 的用法,第二个为 ownProps,不是很推荐,每次更新都会计算 48 | // TODO: 增加记忆点,暂时开发者自己保证 49 | if ((0, _is.isFunc)(mapStateToProps)) { 50 | return mapStateToProps(stateToExpose, (0, _wrapDataInstance2.default)(ownProps), gettersToExpose); 51 | } 52 | if ((0, _is.isArray)(mapStateToProps)) { 53 | // 必须新增部分包含这样的更新 54 | var _outterState = mapStateToProps.filter(function (d) { 55 | return !!d && shouldUpdateKeys.includes(d); 56 | }).reduce(function (p, v) { 57 | p[v] = finalData[v]; 58 | return p; 59 | }, {}); 60 | return _extends({}, _outterState, gettersState); 61 | } 62 | var outterState = Object.keys(mapStateToProps).reduce(function (p, v) { 63 | if ((0, _is.isString)(mapStateToProps[v])) { 64 | if (!shouldUpdateKeys.includes(mapStateToProps[v])) { 65 | // 如果 diff 不包含第二次就不理睬 66 | return p; 67 | } 68 | p[v] = finalData[mapStateToProps[v]]; 69 | } else { 70 | p[v] = mapStateToProps[v](stateToExpose, gettersToExpose, (0, _wrapDataInstance2.default)(ownProps), stateToExpose.$global, config); 71 | } 72 | return p; 73 | }, {}); 74 | return _extends({}, outterState, gettersState); 75 | } 76 | 77 | function setStoreDataByState() { 78 | var storeData = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 79 | var state = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 80 | 81 | return Object.keys(state).reduce(function (p, v) { 82 | p[v] = state[v]; 83 | return p; 84 | }, storeData); 85 | } -------------------------------------------------------------------------------- /src/connect.js: -------------------------------------------------------------------------------- 1 | import { createConnectHelpers } from './createHelpers'; 2 | import { setDataByStateProps } from './dataTransform'; 3 | import { mapActionsToMethod, mapMutationsToMethod } from './mapHelpersToMethod'; 4 | import { isString } from './utils/is'; 5 | 6 | import global from './global'; 7 | 8 | function getPath(link, number = 1) { 9 | return isString(link) && link.split('/')[number]; 10 | } 11 | 12 | const defaultConfig = { 13 | data: {}, 14 | props: {}, 15 | methods: {} 16 | }; 17 | 18 | export default function connect(options) { 19 | const { mapStateToProps = [], mapGettersToProps = [], instanceName = '', namespace, data = {}, props = {} } = options; 20 | return function (config = defaultConfig) { 21 | config.data = config.data || {}; 22 | config.props = config.props || {}; 23 | config.methods = config.methods || {}; 24 | if (options.mapActionsToMethod) { 25 | mapActionsToMethod(options.mapActionsToMethod, false, config.methods); 26 | } 27 | if (options.methods) { 28 | mapMutationsToMethod(options.methods, config.methods); 29 | } 30 | if (options.mapMutationsToMethod) { 31 | mapMutationsToMethod(options.mapMutationsToMethod, config.methods); 32 | } 33 | const _didMount = config.didMount; 34 | const _didUnMount = config.didUnmount; 35 | const key = namespace || instanceName; 36 | Object.assign(config.data, data); 37 | Object.assign(config.props, props); 38 | return { 39 | ...config, 40 | methods: { 41 | ...config.methods, 42 | ...createConnectHelpers.call(global, global, key, config) 43 | }, 44 | didMount() { 45 | const that = this; 46 | // 组件可以添加 $ref 来拿相应的实例 47 | const propsRef = this.props.$ref; 48 | const key = namespace || instanceName || global.getCurrentPath() || global.getCurrentViewId() || -1; 49 | const targetInstanceObj = global.getInstance(key); 50 | if (!targetInstanceObj && typeof _didMount === 'function') { 51 | console.warn('未绑定 store'); 52 | _didMount.call(this); 53 | return; 54 | } 55 | // 当前component表达 56 | const componentIs = getPath(this.is, 2); 57 | const currentRoute = targetInstanceObj.store.getInstance().route; 58 | console.info(`${componentIs} 组件已关联 ${currentRoute}_${key} 的 store`, targetInstanceObj); 59 | Object.assign(this, { 60 | storeConfig: targetInstanceObj.config, 61 | storeInstance: targetInstanceObj.store 62 | }); 63 | this.$emitter = global.emitter; 64 | const store = targetInstanceObj.store; 65 | this.$store = store; 66 | const initialData = setDataByStateProps.call(that, mapStateToProps, store.getInstance().data, config, mapGettersToProps, store.getInstance()); 67 | this.setData(initialData); 68 | // 自动注册进 components 实例, propsRef 开发者自己保证唯一性 69 | global.registerComponents(propsRef || `${getPath(currentRoute)}:${componentIs}`, this); 70 | if (mapStateToProps) { 71 | // store 触发的更新 72 | this.herculexUpdateLisitener = store.$emitter.addListener('updateState', ({state = {}}) => { 73 | const nextData = setDataByStateProps.call(that, mapStateToProps, state, config, mapGettersToProps, store.getInstance(), true); 74 | const originBindViewId = this.$page.$viewId || -1; 75 | const currentViewId = getCurrentPages().pop() ? getCurrentPages().pop().$viewId || -1 : -1; 76 | if (originBindViewId !== currentViewId) return; 77 | that.setData(nextData); 78 | }); 79 | } 80 | if (typeof _didMount === 'function') { 81 | _didMount.call(this); 82 | } 83 | }, 84 | didUnmount() { 85 | this.herculexUpdateLisitener && this.herculexUpdateLisitener(); 86 | if (typeof _didUnMount === 'function') { 87 | _didUnMount.call(this); 88 | } 89 | } 90 | }; 91 | }; 92 | } 93 | -------------------------------------------------------------------------------- /src/utils/manipulate.js: -------------------------------------------------------------------------------- 1 | // {%TITLE=操作%} 2 | import { isFunc, isArray, isObject } from './is'; 3 | import update from 'immutability-helper-enhanced'; 4 | import produce from 'immer'; 5 | 6 | /** 7 | * @desc 从一个对象通过操作序列来拿里面的值,做了基本防空措施 8 | * @param {object} state - 需要获取的数据源 9 | * @param {array} array - 操作路径 10 | * @param {any} initial - 默认值,当没有内容的时候 11 | * @example Example usage of getIn. 12 | * // testcase 13 | * {%common%} 14 | * // getIn 15 | * {%getIn%} 16 | * @returns {any} expected - 获取的值 17 | */ 18 | export function getIn(state, array, initial = null) { 19 | let obj = Object.assign({}, state); 20 | 21 | for (let i = 0; i < array.length; i++) { 22 | // when is undefined return init immediately 23 | if (typeof obj !== 'object' || obj === null) { 24 | return initial; 25 | } 26 | 27 | const prop = array[i]; 28 | 29 | obj = obj[prop]; 30 | } 31 | if (obj === undefined || obj === null) { 32 | return initial; 33 | } 34 | 35 | return obj; 36 | } 37 | 38 | /** 39 | * @desc 一个对象通过操作序列来设置里面的值,做到自动添加值 40 | * @param {object} state - 需要获取的数据源 41 | * @param {array} array - 操作路径 42 | * @param {any} initial - 默认值,当没有内容的时候 43 | * @example Example usage of setIn. 44 | * // testcase 45 | * {%common%} 46 | * // setIn 47 | * {%setIn%} 48 | * @returns {any} expected - 返回操作完成后新的值 49 | */ 50 | export function setIn(state, array, value) { 51 | if (!array) return state; 52 | const setRecursively = function(state, array, value, index) { 53 | let clone = {}; 54 | let prop = array[index]; 55 | let newState; 56 | 57 | if (array.length > index) { 58 | // get cloned object 59 | if (isArray(state)) { 60 | clone = state.slice(0); 61 | } else { 62 | clone = Object.assign({}, state); 63 | } 64 | // not exists, make new {} 65 | newState = ((isObject(state) || isArray(state)) && state[prop] !== undefined) ? state[prop] : {}; 66 | clone[prop] = setRecursively(newState, array, value, index + 1); 67 | return clone; 68 | } 69 | 70 | return value; 71 | }; 72 | 73 | return setRecursively(state, array, value, 0); 74 | } 75 | 76 | /** 77 | * @desc 一个对象通过操作序列来删除里面的值, 做到防空, 返回新值 78 | * @param {object} state - 需要获取的数据源 79 | * @param {array} array - 操作路径 80 | * @example Example usage of deleteIn. 81 | * // testcase 82 | * {%common%} 83 | * // deleteIn 84 | * {%deleteIn%} 85 | * @returns {any} expected - 返回删除后新的对象 or 值 86 | */ 87 | export function deleteIn(state, array) { 88 | const deleteRecursively = function (state, array, index) { 89 | let clone = {}; 90 | let prop = array[index]; 91 | 92 | // not exists, just return, delete nothing 93 | if (!isObject(state) || state[prop] === undefined) { 94 | return state; 95 | } 96 | 97 | // not last one, just clone 98 | if (array.length - 1 !== index) { 99 | if (Array.isArray(state)) { 100 | clone = state.slice(); 101 | } else { 102 | clone = Object.assign({}, state); 103 | } 104 | 105 | clone[prop] = deleteRecursively(state[prop], array, index + 1); 106 | 107 | return clone; 108 | } 109 | 110 | // delete here 111 | if (Array.isArray(state)) { 112 | clone = [].concat(state.slice(0, prop), state.slice(prop + 1)); 113 | } else { 114 | clone = Object.assign({}, state); 115 | delete clone[prop]; 116 | } 117 | 118 | return clone; 119 | }; 120 | 121 | return deleteRecursively(state, array, 0); 122 | } 123 | 124 | /** 125 | * @desc 将一组操作通过 array 的形式 reduce 组合 126 | * @param {array} array - 组合方式 127 | * @example Example usage of compose. 128 | * {%compose%} 129 | */ 130 | export function compose(array) { 131 | return array.reduce((p, v) => { 132 | if (isFunc(v)) { 133 | return v(p); 134 | } 135 | if (isArray(v) && isFunc(v[0])) { 136 | return v[0](p, ...v.slice(1)); 137 | } 138 | return p; 139 | }); 140 | } 141 | export { update, produce }; 142 | -------------------------------------------------------------------------------- /src/global.js: -------------------------------------------------------------------------------- 1 | import EventEmitter from './emitter'; 2 | import wrapDataInstance from './wrapDataInstance'; 3 | import _innerPlugins from './innerPlugins'; 4 | import { isString } from './utils/is'; 5 | import wrapState from './utils/wrapState'; 6 | 7 | class Global { 8 | constructor() { 9 | this.emitter = new EventEmitter(); 10 | this.storeInstances = {}; 11 | this.components = {}; 12 | this.globalStoreConfig = { 13 | state: {} 14 | }; 15 | this.messageChannel = {}; 16 | this.router = { 17 | currentPath: '', 18 | query: null, 19 | context: {}, 20 | from: '', 21 | viewId: '', 22 | fromViewId: '' 23 | }; 24 | const that = this; 25 | this.emitter.addListener('updateCurrentPath', path => { 26 | Object.assign(that.router, path); 27 | const prevState = { ...that.globalStoreConfig.state }; 28 | // console.info(`%c mutation: ROUTER`, 'color: #03A9F4; font-weight: bold', { ...that.router }, new Date().getTime()); 29 | Object.assign(that.globalStoreConfig.state, { 30 | $router: that.router 31 | }); 32 | const nextState = { ...that.globalStoreConfig.state }; 33 | that.emitter.emitEvent('updateGlobalStore', { nextState, prevState, type: '$global:handleRouterChanged', payload: { ...path } }); 34 | }); 35 | this.emitter.addListener('updateState', data => { 36 | const { state, mutation } = data; 37 | const prevState = { ...that.globalStoreConfig.state }; 38 | Object.assign(that.globalStoreConfig.state, state); 39 | const nextState = { ...that.globalStoreConfig.state }; 40 | that.emitter.emitEvent('updateGlobalStore', { nextState, prevState, mutation }); 41 | }); 42 | this.messageManager = { 43 | clear: this.clearMessage.bind(this), 44 | push: this.pushMessage.bind(this), 45 | pop: this.popMessage.bind(this) 46 | }; 47 | } 48 | subscribe(subscriber, actionSubscriber) { 49 | const that = this; 50 | this.emitter.addListener('updateGlobalStore', ({ nextState, prevState, mutation = {} }) => { 51 | subscriber && subscriber(mutation, wrapState(nextState), wrapState(prevState)); 52 | }); 53 | // if (actionSubscriber) { 54 | // emitter.addListener('dispatchAction', (action) => { 55 | // actionSubscriber(action); 56 | // }); 57 | // } 58 | } 59 | getGlobalState(mapGlobalToState) { 60 | const state = wrapDataInstance(this.globalStoreConfig.state); 61 | if (mapGlobalToState) { 62 | return mapGlobalToState(state); 63 | } 64 | return state; 65 | } 66 | clearMessage(channel) { 67 | if (this.messageChannel[channel]) { 68 | this.messageChannel[channel] = []; 69 | } 70 | } 71 | pushMessage(channel, payload) { 72 | if (this.messageChannel[channel]) { 73 | this.messageChannel[channel].push(payload); 74 | } else { 75 | this.messageChannel[channel] = [payload]; 76 | } 77 | } 78 | popMessage(channel) { 79 | if (this.messageChannel[channel]) { 80 | return this.messageChannel[channel].pop(); 81 | } else { 82 | return null; 83 | } 84 | } 85 | getCurrentPath() { 86 | return this.router.currentPath; 87 | } 88 | getCurrentViewId() { 89 | return this.router.viewId; 90 | } 91 | setGlobalStoreConfig(data) { 92 | this.globalStoreConfig = data; 93 | this.instanceName = 'global'; 94 | if (this.globalStoreConfig.plugins) { 95 | this.globalStoreConfig.plugins.forEach(plugin => { 96 | const pluginFunc = isString(plugin) ? _innerPlugins[plugin] : plugin; 97 | pluginFunc(this); 98 | }); 99 | } 100 | } 101 | registerComponents(name, instance) { 102 | this.components[name] = instance; 103 | } 104 | getComponentRef(name) { 105 | if (!this.components[name]) { 106 | console.warn(`未找到${name}组件,请检查组件名是否正确,是否在onReady后使用`); 107 | return null; 108 | } 109 | return this.components[name]; 110 | } 111 | registerInstance(name, instance) { 112 | this.storeInstances[name] = instance; 113 | } 114 | getInstance(name) { 115 | return this.storeInstances[name]; 116 | } 117 | getInstanceByViewId(id) { 118 | // 通过 viewid 找 119 | const target = Object.values(this.storeInstances).find(i => i.viewId === id); 120 | return target; 121 | } 122 | getState(name) { 123 | const target = this.storeInstances[name]; 124 | if (target) { 125 | const { store } = target; 126 | const instance = store.getInstance(); 127 | return instance.data; 128 | } 129 | return null; 130 | } 131 | } 132 | export default new Global(); 133 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # herculeX 2 | Simple, predictable, lightweight, high performance, developer friendly state management for alipay mini-program 3 | 4 | ![setData (1).png](https://cdn.nlark.com/lark/0/2018/png/82549/1537904366328-49a7e2e5-5aeb-4326-be5f-8cf0eb603181.png) 5 | 6 | ## Feature 7 | 8 | - [x] Component, Page, global wrapper 9 | - [x] vuex-like apis and concept: actions, mutations, getters, plugins 10 | - [x] strengthen mutation, getters: add immutable helper, global power to getters.and improve mutation usecase, add some common innerMutation 11 | - [x] plugins support, add logger as inner plugin 12 | - [x] cross page communication: message channel, auto router dispatcher(manage router ), get ready-only State by namespace 13 | - [x] cross components communication: support centralized ref management 14 | - [x] connect: connect Page to Component, add mapStateToProps, mapGettersToProps, use more developer friendly way. 15 | - [x] mapHelpers: connect actions and mutations to Page, Componnet methods 16 | - [x] global store support: manage all store, component instance and global state, global message bus ,event bus 17 | - [x] event bus support 18 | - [x] router: improve router usecase, auto router dispatcher, add resume lifecycle 19 | - [x] utils: immutable heplers, common functional tools, urlParser, promiseWrapper... 20 | - [x] use immer and immutable helper to promise immutable 21 | - [x] magic memoization: add special memoization feature to mapHelpersToProps 22 | 23 | ## Installation 24 | 25 | * Installation: `npm install herculex --save`. 26 | * basic usage: 27 | * index.js 28 | ``` 29 | import store from './store'; 30 | const app = getApp(); 31 | Page(store.register({ 32 | mapActionsToMethod: ['getUserInfo'], 33 | mapMutationsToMethod: ['helperWay'], 34 | onLoad() { 35 | const message = this.$message.pop('card-home'); 36 | // get message from last page as you like 37 | }, 38 | onReady() { 39 | const componentInstance = this.$getRef('card-input'); 40 | // get component ref, then get data when you need ,specially in form condition 41 | }, 42 | onResume(ctx) { 43 | // get ctx here, 44 | }, 45 | ... 46 | onTap() { 47 | } 48 | }) 49 | ``` 50 | * store.js 51 | ``` 52 | export default new Store({ 53 | connectGlobal: true, 54 | state: { 55 | userInfo: {}, 56 | bannerList: [], 57 | UI, 58 | }, 59 | getters: { 60 | // functional promgraming, add some helpers 61 | cardCount: (state, getters, global) => global.getIn(['entity', 'cardList', 'length'], 0), 62 | avatar: state => state.getIn(['userInfo', 'iconUrl'], ASSETS.DEFAULT_AVATAR), 63 | nickName: state => state.getIn(['userInfo', 'nick']), 64 | cardList: (state, getters, global) => global.getIn(['entity', 'cardList'], []).map(mapCardItemToData), 65 | }, 66 | mutations: { 67 | mutableWay(state, payload) { 68 | // use immer promise immutable 69 | state.a = payload.a 70 | }, 71 | helperWay(state, payload) { 72 | // use inner helper: setIn, deleteIn, update 73 | return state.setIn(['userInfo', 'name'], payload.name) 74 | } 75 | }, 76 | plugins: [ 'logger' ], // inner plugin logger 77 | actions: { 78 | async getUserInfo({ commit, state, dispatch, global, getters, }, payload) { 79 | // get router and context in global store, all state are binded immutable helper 80 | const routerCtx = global.getIn(['router', 'currentRouter']); 81 | const avatar = getters.getIn('avatar'); 82 | const userInfo = await cardListService.getUserInfo(); 83 | commit('getUserInfo', { userInfo }); 84 | }, 85 | }, 86 | }); 87 | ``` 88 | * index.axml 89 | ``` 90 | 91 | {{userInfo.name}} 92 | 93 | 94 | 95 | 96 | ``` 97 | 98 | ## Examples 99 | * appx (alipay mini program) 100 | * quick start 101 | * [Counter](https://github.com/herculesJS/herculex-appx-examples/tree/master/quick-start/pages/counter) : show basic usage as commit & dispatch 102 | * [TODOS](https://github.com/herculesJS/herculex-appx-examples/tree/master/quick-start/pages/todos): show how components and modules work 103 | 104 | ## Turtoral 105 | 106 | ## TODO 107 | - [ ] add pageBeforeRender Action to do something before instance created 108 | - [ ] add travis ci 109 | - [ ] add middleware support, take over api middleware 110 | - [ ] doc & gitbook,github.io 111 | - [ ] examples & boilerplate 112 | - [ ] cli for static code check, and generate snipets 113 | - [ ] ide plugin 114 | - [ ] model based api middleware 115 | - [ ] test-utils & mock helper 116 | - [ ] dev-tools for ide & standalone 117 | - [ ] modules (need deeply design) 118 | - [ ] error catcher plugin 119 | - [ ] refactory and tests, separate appx data setter as independent repo 120 | -------------------------------------------------------------------------------- /dist/utils/manipulate.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.produce = exports.update = undefined; 7 | 8 | var _assign = require('babel-runtime/core-js/object/assign'); 9 | 10 | var _assign2 = _interopRequireDefault(_assign); 11 | 12 | exports.getIn = getIn; 13 | exports.setIn = setIn; 14 | exports.deleteIn = deleteIn; 15 | exports.compose = compose; 16 | 17 | var _is = require('./is'); 18 | 19 | var _immutabilityHelperEnhanced = require('immutability-helper-enhanced'); 20 | 21 | var _immutabilityHelperEnhanced2 = _interopRequireDefault(_immutabilityHelperEnhanced); 22 | 23 | var _immer = require('immer'); 24 | 25 | var _immer2 = _interopRequireDefault(_immer); 26 | 27 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 28 | 29 | /** 30 | * @desc 从一个对象通过操作序列来拿里面的值,做了基本防空措施 31 | * @param {object} state - 需要获取的数据源 32 | * @param {array} array - 操作路径 33 | * @param {any} initial - 默认值,当没有内容的时候 34 | * @example Example usage of getIn. 35 | * // testcase 36 | * {%common%} 37 | * // getIn 38 | * {%getIn%} 39 | * @returns {any} expected - 获取的值 40 | */ 41 | function getIn(state, array) { 42 | var initial = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; 43 | 44 | var obj = (0, _assign2.default)({}, state); 45 | 46 | for (var i = 0; i < array.length; i++) { 47 | // when is undefined return init immediately 48 | if (typeof obj !== 'object' || obj === null) { 49 | return initial; 50 | } 51 | 52 | var prop = array[i]; 53 | 54 | obj = obj[prop]; 55 | } 56 | if (obj === undefined || obj === null) { 57 | return initial; 58 | } 59 | 60 | return obj; 61 | } 62 | 63 | /** 64 | * @desc 一个对象通过操作序列来设置里面的值,做到自动添加值 65 | * @param {object} state - 需要获取的数据源 66 | * @param {array} array - 操作路径 67 | * @param {any} initial - 默认值,当没有内容的时候 68 | * @example Example usage of setIn. 69 | * // testcase 70 | * {%common%} 71 | * // setIn 72 | * {%setIn%} 73 | * @returns {any} expected - 返回操作完成后新的值 74 | */ 75 | // {%TITLE=操作%} 76 | function setIn(state, array, value) { 77 | if (!array) return state; 78 | var setRecursively = function setRecursively(state, array, value, index) { 79 | var clone = {}; 80 | var prop = array[index]; 81 | var newState = void 0; 82 | 83 | if (array.length > index) { 84 | // get cloned object 85 | if ((0, _is.isArray)(state)) { 86 | clone = state.slice(0); 87 | } else { 88 | clone = (0, _assign2.default)({}, state); 89 | } 90 | // not exists, make new {} 91 | newState = ((0, _is.isObject)(state) || (0, _is.isArray)(state)) && state[prop] !== undefined ? state[prop] : {}; 92 | clone[prop] = setRecursively(newState, array, value, index + 1); 93 | return clone; 94 | } 95 | 96 | return value; 97 | }; 98 | 99 | return setRecursively(state, array, value, 0); 100 | } 101 | 102 | /** 103 | * @desc 一个对象通过操作序列来删除里面的值, 做到防空, 返回新值 104 | * @param {object} state - 需要获取的数据源 105 | * @param {array} array - 操作路径 106 | * @example Example usage of deleteIn. 107 | * // testcase 108 | * {%common%} 109 | * // deleteIn 110 | * {%deleteIn%} 111 | * @returns {any} expected - 返回删除后新的对象 or 值 112 | */ 113 | function deleteIn(state, array) { 114 | var deleteRecursively = function deleteRecursively(state, array, index) { 115 | var clone = {}; 116 | var prop = array[index]; 117 | 118 | // not exists, just return, delete nothing 119 | if (!(0, _is.isObject)(state) || state[prop] === undefined) { 120 | return state; 121 | } 122 | 123 | // not last one, just clone 124 | if (array.length - 1 !== index) { 125 | if (Array.isArray(state)) { 126 | clone = state.slice(); 127 | } else { 128 | clone = (0, _assign2.default)({}, state); 129 | } 130 | 131 | clone[prop] = deleteRecursively(state[prop], array, index + 1); 132 | 133 | return clone; 134 | } 135 | 136 | // delete here 137 | if (Array.isArray(state)) { 138 | clone = [].concat(state.slice(0, prop), state.slice(prop + 1)); 139 | } else { 140 | clone = (0, _assign2.default)({}, state); 141 | delete clone[prop]; 142 | } 143 | 144 | return clone; 145 | }; 146 | 147 | return deleteRecursively(state, array, 0); 148 | } 149 | 150 | /** 151 | * @desc 将一组操作通过 array 的形式 reduce 组合 152 | * @param {array} array - 组合方式 153 | * @example Example usage of compose. 154 | * {%compose%} 155 | */ 156 | function compose(array) { 157 | return array.reduce(function (p, v) { 158 | if ((0, _is.isFunc)(v)) { 159 | return v(p); 160 | } 161 | if ((0, _is.isArray)(v) && (0, _is.isFunc)(v[0])) { 162 | return v[0].apply(v, [p].concat(v.slice(1))); 163 | } 164 | return p; 165 | }); 166 | } 167 | exports.update = _immutabilityHelperEnhanced2.default; 168 | exports.produce = _immer2.default; -------------------------------------------------------------------------------- /dist/connect.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _assign = require('babel-runtime/core-js/object/assign'); 8 | 9 | var _assign2 = _interopRequireDefault(_assign); 10 | 11 | var _extends = _assign2.default || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; 12 | 13 | exports.default = connect; 14 | 15 | var _createHelpers = require('./createHelpers'); 16 | 17 | var _dataTransform = require('./dataTransform'); 18 | 19 | var _mapHelpersToMethod = require('./mapHelpersToMethod'); 20 | 21 | var _is = require('./utils/is'); 22 | 23 | var _global = require('./global'); 24 | 25 | var _global2 = _interopRequireDefault(_global); 26 | 27 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 28 | 29 | function getPath(link) { 30 | var number = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; 31 | 32 | return (0, _is.isString)(link) && link.split('/')[number]; 33 | } 34 | 35 | var defaultConfig = { 36 | data: {}, 37 | props: {}, 38 | methods: {} 39 | }; 40 | 41 | function connect(options) { 42 | var _options$mapStateToPr = options.mapStateToProps, 43 | mapStateToProps = _options$mapStateToPr === undefined ? [] : _options$mapStateToPr, 44 | _options$mapGettersTo = options.mapGettersToProps, 45 | mapGettersToProps = _options$mapGettersTo === undefined ? [] : _options$mapGettersTo, 46 | _options$instanceName = options.instanceName, 47 | instanceName = _options$instanceName === undefined ? '' : _options$instanceName, 48 | namespace = options.namespace, 49 | _options$data = options.data, 50 | data = _options$data === undefined ? {} : _options$data, 51 | _options$props = options.props, 52 | props = _options$props === undefined ? {} : _options$props; 53 | 54 | return function () { 55 | var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaultConfig; 56 | 57 | config.data = config.data || {}; 58 | config.props = config.props || {}; 59 | config.methods = config.methods || {}; 60 | if (options.mapActionsToMethod) { 61 | (0, _mapHelpersToMethod.mapActionsToMethod)(options.mapActionsToMethod, false, config.methods); 62 | } 63 | if (options.methods) { 64 | (0, _mapHelpersToMethod.mapMutationsToMethod)(options.methods, config.methods); 65 | } 66 | if (options.mapMutationsToMethod) { 67 | (0, _mapHelpersToMethod.mapMutationsToMethod)(options.mapMutationsToMethod, config.methods); 68 | } 69 | var _didMount = config.didMount; 70 | var _didUnMount = config.didUnmount; 71 | var key = namespace || instanceName; 72 | (0, _assign2.default)(config.data, data); 73 | (0, _assign2.default)(config.props, props); 74 | return _extends({}, config, { 75 | methods: _extends({}, config.methods, _createHelpers.createConnectHelpers.call(_global2.default, _global2.default, key, config)), 76 | didMount: function didMount() { 77 | var _this = this; 78 | 79 | var that = this; 80 | // 组件可以添加 $ref 来拿相应的实例 81 | var propsRef = this.props.$ref; 82 | var key = namespace || instanceName || _global2.default.getCurrentPath() || _global2.default.getCurrentViewId() || -1; 83 | var targetInstanceObj = _global2.default.getInstance(key); 84 | if (!targetInstanceObj && typeof _didMount === 'function') { 85 | console.warn('未绑定 store'); 86 | _didMount.call(this); 87 | return; 88 | } 89 | // 当前component表达 90 | var componentIs = getPath(this.is, 2); 91 | var currentRoute = targetInstanceObj.store.getInstance().route; 92 | console.info(componentIs + ' \u7EC4\u4EF6\u5DF2\u5173\u8054 ' + currentRoute + '_' + key + ' \u7684 store', targetInstanceObj); 93 | (0, _assign2.default)(this, { 94 | storeConfig: targetInstanceObj.config, 95 | storeInstance: targetInstanceObj.store 96 | }); 97 | this.$emitter = _global2.default.emitter; 98 | var store = targetInstanceObj.store; 99 | this.$store = store; 100 | var initialData = _dataTransform.setDataByStateProps.call(that, mapStateToProps, store.getInstance().data, config, mapGettersToProps, store.getInstance()); 101 | this.setData(initialData); 102 | // 自动注册进 components 实例, propsRef 开发者自己保证唯一性 103 | _global2.default.registerComponents(propsRef || getPath(currentRoute) + ':' + componentIs, this); 104 | if (mapStateToProps) { 105 | // store 触发的更新 106 | this.herculexUpdateLisitener = store.$emitter.addListener('updateState', function (_ref) { 107 | var _ref$state = _ref.state, 108 | state = _ref$state === undefined ? {} : _ref$state; 109 | 110 | var nextData = _dataTransform.setDataByStateProps.call(that, mapStateToProps, state, config, mapGettersToProps, store.getInstance(), true); 111 | var originBindViewId = _this.$page.$viewId || -1; 112 | var currentViewId = getCurrentPages().pop() ? getCurrentPages().pop().$viewId || -1 : -1; 113 | if (originBindViewId !== currentViewId) return; 114 | that.setData(nextData); 115 | }); 116 | } 117 | if (typeof _didMount === 'function') { 118 | _didMount.call(this); 119 | } 120 | }, 121 | didUnmount: function didUnmount() { 122 | this.herculexUpdateLisitener && this.herculexUpdateLisitener(); 123 | if (typeof _didUnMount === 'function') { 124 | _didUnMount.call(this); 125 | } 126 | } 127 | }); 128 | }; 129 | } -------------------------------------------------------------------------------- /dist/global.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _values = require('babel-runtime/core-js/object/values'); 8 | 9 | var _values2 = _interopRequireDefault(_values); 10 | 11 | var _assign = require('babel-runtime/core-js/object/assign'); 12 | 13 | var _assign2 = _interopRequireDefault(_assign); 14 | 15 | var _extends = _assign2.default || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; 16 | 17 | var _emitter = require('./emitter'); 18 | 19 | var _emitter2 = _interopRequireDefault(_emitter); 20 | 21 | var _wrapDataInstance = require('./wrapDataInstance'); 22 | 23 | var _wrapDataInstance2 = _interopRequireDefault(_wrapDataInstance); 24 | 25 | var _innerPlugins2 = require('./innerPlugins'); 26 | 27 | var _innerPlugins3 = _interopRequireDefault(_innerPlugins2); 28 | 29 | var _is = require('./utils/is'); 30 | 31 | var _wrapState = require('./utils/wrapState'); 32 | 33 | var _wrapState2 = _interopRequireDefault(_wrapState); 34 | 35 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 36 | 37 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 38 | 39 | var Global = function () { 40 | function Global() { 41 | _classCallCheck(this, Global); 42 | 43 | this.emitter = new _emitter2.default(); 44 | this.storeInstances = {}; 45 | this.components = {}; 46 | this.globalStoreConfig = { 47 | state: {} 48 | }; 49 | this.messageChannel = {}; 50 | this.router = { 51 | currentPath: '', 52 | query: null, 53 | context: {}, 54 | from: '', 55 | viewId: '', 56 | fromViewId: '' 57 | }; 58 | var that = this; 59 | this.emitter.addListener('updateCurrentPath', function (path) { 60 | (0, _assign2.default)(that.router, path); 61 | var prevState = _extends({}, that.globalStoreConfig.state); 62 | // console.info(`%c mutation: ROUTER`, 'color: #03A9F4; font-weight: bold', { ...that.router }, new Date().getTime()); 63 | (0, _assign2.default)(that.globalStoreConfig.state, { 64 | $router: that.router 65 | }); 66 | var nextState = _extends({}, that.globalStoreConfig.state); 67 | that.emitter.emitEvent('updateGlobalStore', { nextState: nextState, prevState: prevState, type: '$global:handleRouterChanged', payload: _extends({}, path) }); 68 | }); 69 | this.emitter.addListener('updateState', function (data) { 70 | var state = data.state, 71 | mutation = data.mutation; 72 | 73 | var prevState = _extends({}, that.globalStoreConfig.state); 74 | (0, _assign2.default)(that.globalStoreConfig.state, state); 75 | var nextState = _extends({}, that.globalStoreConfig.state); 76 | that.emitter.emitEvent('updateGlobalStore', { nextState: nextState, prevState: prevState, mutation: mutation }); 77 | }); 78 | this.messageManager = { 79 | clear: this.clearMessage.bind(this), 80 | push: this.pushMessage.bind(this), 81 | pop: this.popMessage.bind(this) 82 | }; 83 | } 84 | 85 | Global.prototype.subscribe = function subscribe(subscriber, actionSubscriber) { 86 | var that = this; 87 | this.emitter.addListener('updateGlobalStore', function (_ref) { 88 | var nextState = _ref.nextState, 89 | prevState = _ref.prevState, 90 | _ref$mutation = _ref.mutation, 91 | mutation = _ref$mutation === undefined ? {} : _ref$mutation; 92 | 93 | subscriber && subscriber(mutation, (0, _wrapState2.default)(nextState), (0, _wrapState2.default)(prevState)); 94 | }); 95 | // if (actionSubscriber) { 96 | // emitter.addListener('dispatchAction', (action) => { 97 | // actionSubscriber(action); 98 | // }); 99 | // } 100 | }; 101 | 102 | Global.prototype.getGlobalState = function getGlobalState(mapGlobalToState) { 103 | var state = (0, _wrapDataInstance2.default)(this.globalStoreConfig.state); 104 | if (mapGlobalToState) { 105 | return mapGlobalToState(state); 106 | } 107 | return state; 108 | }; 109 | 110 | Global.prototype.clearMessage = function clearMessage(channel) { 111 | if (this.messageChannel[channel]) { 112 | this.messageChannel[channel] = []; 113 | } 114 | }; 115 | 116 | Global.prototype.pushMessage = function pushMessage(channel, payload) { 117 | if (this.messageChannel[channel]) { 118 | this.messageChannel[channel].push(payload); 119 | } else { 120 | this.messageChannel[channel] = [payload]; 121 | } 122 | }; 123 | 124 | Global.prototype.popMessage = function popMessage(channel) { 125 | if (this.messageChannel[channel]) { 126 | return this.messageChannel[channel].pop(); 127 | } else { 128 | return null; 129 | } 130 | }; 131 | 132 | Global.prototype.getCurrentPath = function getCurrentPath() { 133 | return this.router.currentPath; 134 | }; 135 | 136 | Global.prototype.getCurrentViewId = function getCurrentViewId() { 137 | return this.router.viewId; 138 | }; 139 | 140 | Global.prototype.setGlobalStoreConfig = function setGlobalStoreConfig(data) { 141 | var _this = this; 142 | 143 | this.globalStoreConfig = data; 144 | this.instanceName = 'global'; 145 | if (this.globalStoreConfig.plugins) { 146 | this.globalStoreConfig.plugins.forEach(function (plugin) { 147 | var pluginFunc = (0, _is.isString)(plugin) ? _innerPlugins3.default[plugin] : plugin; 148 | pluginFunc(_this); 149 | }); 150 | } 151 | }; 152 | 153 | Global.prototype.registerComponents = function registerComponents(name, instance) { 154 | this.components[name] = instance; 155 | }; 156 | 157 | Global.prototype.getComponentRef = function getComponentRef(name) { 158 | if (!this.components[name]) { 159 | console.warn('\u672A\u627E\u5230' + name + '\u7EC4\u4EF6\uFF0C\u8BF7\u68C0\u67E5\u7EC4\u4EF6\u540D\u662F\u5426\u6B63\u786E\uFF0C\u662F\u5426\u5728onReady\u540E\u4F7F\u7528'); 160 | return null; 161 | } 162 | return this.components[name]; 163 | }; 164 | 165 | Global.prototype.registerInstance = function registerInstance(name, instance) { 166 | this.storeInstances[name] = instance; 167 | }; 168 | 169 | Global.prototype.getInstance = function getInstance(name) { 170 | return this.storeInstances[name]; 171 | }; 172 | 173 | Global.prototype.getInstanceByViewId = function getInstanceByViewId(id) { 174 | // 通过 viewid 找 175 | var target = (0, _values2.default)(this.storeInstances).find(function (i) { 176 | return i.viewId === id; 177 | }); 178 | return target; 179 | }; 180 | 181 | Global.prototype.getState = function getState(name) { 182 | var target = this.storeInstances[name]; 183 | if (target) { 184 | var store = target.store; 185 | 186 | var instance = store.getInstance(); 187 | return instance.data; 188 | } 189 | return null; 190 | }; 191 | 192 | return Global; 193 | }(); 194 | 195 | exports.default = new Global(); -------------------------------------------------------------------------------- /src/emitter.js: -------------------------------------------------------------------------------- 1 | function EventEmitter() {} 2 | 3 | var proto = EventEmitter.prototype; 4 | var originalGlobalValue = exports.EventEmitter; 5 | 6 | function indexOfListener(listeners, listener) { 7 | var i = listeners.length; 8 | while (i--) { 9 | if (listeners[i].listener === listener) { 10 | return i; 11 | } 12 | } 13 | 14 | return -1; 15 | } 16 | 17 | function alias(name) { 18 | return function aliasClosure() { 19 | return this[name].apply(this, arguments); 20 | }; 21 | } 22 | 23 | proto.getListeners = function getListeners(evt) { 24 | var events = this._getEvents(); 25 | var response; 26 | var key; 27 | 28 | if (evt instanceof RegExp) { 29 | response = {}; 30 | for (key in events) { 31 | if (events.hasOwnProperty(key) && evt.test(key)) { 32 | response[key] = events[key]; 33 | } 34 | } 35 | } else { 36 | response = events[evt] || (events[evt] = []); 37 | } 38 | 39 | return response; 40 | }; 41 | 42 | /** 43 | * Takes a list of listener objects and flattens it into a list of listener functions. 44 | * 45 | * @param {Object[]} listeners Raw listener objects. 46 | * @return {Function[]} Just the listener functions. 47 | */ 48 | proto.flattenListeners = function flattenListeners(listeners) { 49 | var flatListeners = []; 50 | var i; 51 | 52 | for (i = 0; i < listeners.length; i += 1) { 53 | flatListeners.push(listeners[i].listener); 54 | } 55 | 56 | return flatListeners; 57 | }; 58 | 59 | /** 60 | * Fetches the requested listeners via getListeners but will always return the results inside an object. This is mainly for internal use but others may find it useful. 61 | * 62 | * @param {String|RegExp} evt Name of the event to return the listeners from. 63 | * @return {Object} All listener functions for an event in an object. 64 | */ 65 | proto.getListenersAsObject = function getListenersAsObject(evt) { 66 | var listeners = this.getListeners(evt); 67 | var response; 68 | 69 | if (listeners instanceof Array) { 70 | response = {}; 71 | response[evt] = listeners; 72 | } 73 | 74 | return response || listeners; 75 | }; 76 | 77 | function isValidListener(listener) { 78 | if (typeof listener === 'function' || listener instanceof RegExp) { 79 | return true; 80 | } else if (listener && typeof listener === 'object') { 81 | return isValidListener(listener.listener); 82 | } else { 83 | return false; 84 | } 85 | } 86 | 87 | proto.addListener = function addListener(evt, listener) { 88 | if (!isValidListener(listener)) { 89 | throw new TypeError('listener must be a function'); 90 | } 91 | 92 | var listeners = this.getListenersAsObject(evt); 93 | var listenerIsWrapped = typeof listener === 'object'; 94 | var key; 95 | var uid; 96 | for (key in listeners) { 97 | if (listeners.hasOwnProperty(key) && indexOfListener(listeners[key], listener) === -1) { 98 | uid = `lisitener_${key}_${new Date().getTime()}`; 99 | listeners[key].push(listenerIsWrapped ? listener : { 100 | listener: listener, 101 | once: false, 102 | uid 103 | }); 104 | } 105 | } 106 | return function() { 107 | const removeIndex = listeners[key].findIndex(o => o.uid === uid); 108 | if (removeIndex !== -1) { 109 | listeners[key].splice(removeIndex, 1); 110 | } 111 | return proto; 112 | }; 113 | }; 114 | 115 | proto.on = alias('addListener'); 116 | 117 | proto.addOnceListener = function addOnceListener(evt, listener) { 118 | return this.addListener(evt, { 119 | listener: listener, 120 | once: true 121 | }); 122 | }; 123 | 124 | proto.once = alias('addOnceListener'); 125 | 126 | proto.defineEvent = function defineEvent(evt) { 127 | this.getListeners(evt); 128 | return this; 129 | }; 130 | 131 | proto.defineEvents = function defineEvents(evts) { 132 | for (var i = 0; i < evts.length; i += 1) { 133 | this.defineEvent(evts[i]); 134 | } 135 | return this; 136 | }; 137 | 138 | proto.removeListener = function removeListener(evt, listener) { 139 | var listeners = this.getListenersAsObject(evt); 140 | var index; 141 | var key; 142 | 143 | for (key in listeners) { 144 | if (listeners.hasOwnProperty(key)) { 145 | index = indexOfListener(listeners[key], listener); 146 | if (index !== -1) { 147 | listeners[key].splice(index, 1); 148 | } 149 | } 150 | } 151 | 152 | return this; 153 | }; 154 | 155 | proto.off = alias('removeListener'); 156 | 157 | proto.addListeners = function addListeners(evt, listeners) { 158 | return this.manipulateListeners(false, evt, listeners); 159 | }; 160 | 161 | proto.removeListeners = function removeListeners(evt, listeners) { 162 | return this.manipulateListeners(true, evt, listeners); 163 | }; 164 | 165 | proto.manipulateListeners = function manipulateListeners(remove, evt, listeners) { 166 | var i; 167 | var value; 168 | var single = remove ? this.removeListener : this.addListener; 169 | var multiple = remove ? this.removeListeners : this.addListeners; 170 | 171 | // If evt is an object then pass each of its properties to this method 172 | if (typeof evt === 'object' && !(evt instanceof RegExp)) { 173 | for (i in evt) { 174 | if (evt.hasOwnProperty(i) && (value = evt[i])) { 175 | if (typeof value === 'function') { 176 | single.call(this, i, value); 177 | } else { 178 | multiple.call(this, i, value); 179 | } 180 | } 181 | } 182 | } else { 183 | i = listeners.length; 184 | while (i--) { 185 | single.call(this, evt, listeners[i]); 186 | } 187 | } 188 | 189 | return this; 190 | }; 191 | 192 | proto.removeEvent = function removeEvent(evt) { 193 | var type = typeof evt; 194 | var events = this._getEvents(); 195 | var key; 196 | if (type === 'string') { 197 | delete events[evt]; 198 | } else if (evt instanceof RegExp) { 199 | // Remove all events matching the regex. 200 | for (key in events) { 201 | if (events.hasOwnProperty(key) && evt.test(key)) { 202 | delete events[key]; 203 | } 204 | } 205 | } else { 206 | delete this._events; 207 | } 208 | 209 | return this; 210 | }; 211 | 212 | proto.removeAllListeners = alias('removeEvent'); 213 | 214 | proto.emitEvent = function emitEvent(evt, args) { 215 | var listenersMap = this.getListenersAsObject(evt); 216 | var listeners; 217 | var listener; 218 | var i; 219 | var key; 220 | var response; 221 | for (key in listenersMap) { 222 | if (listenersMap.hasOwnProperty(key)) { 223 | listeners = listenersMap[key].slice(0); 224 | 225 | for (i = 0; i < listeners.length; i++) { 226 | listener = listeners[i]; 227 | if (listener.once === true) { 228 | this.removeListener(evt, listener.listener); 229 | } 230 | response = listener.listener.call(this, args || []); 231 | if (response === this._getOnceReturnValue()) { 232 | this.removeListener(evt, listener.listener); 233 | } 234 | } 235 | } 236 | } 237 | 238 | return this; 239 | }; 240 | 241 | proto.emitEventChain = function emitEventWithNext(evt, args, cb = d => d) { 242 | var listenersMap = this.getListenersAsObject(evt); 243 | var listeners; 244 | var key; 245 | for (key in listenersMap) { 246 | if (listenersMap.hasOwnProperty(key)) { 247 | listeners = listenersMap[key].slice(0); 248 | listeners.push({ 249 | listener: function(action, next, last = {}) { 250 | // 最后一个回调获取最终上一次的结果 251 | cb(last); 252 | } 253 | }); 254 | const that = this; 255 | (function createNextFunc(i) { 256 | const listener = listeners[i]; 257 | if (!listener) { 258 | return d => d; 259 | } 260 | if (listener.once === true) { 261 | this.removeListener(evt, listener.listener); 262 | } 263 | return listener.listener.bind(that, args || [], createNextFunc(i + 1)); 264 | })(0)(); 265 | } 266 | } 267 | return this; 268 | }; 269 | 270 | proto.trigger = alias('emitEvent'); 271 | proto.emit = function emit(evt) { 272 | var args = Array.prototype.slice.call(arguments, 1); 273 | return this.emitEvent(evt, args); 274 | }; 275 | 276 | proto.setOnceReturnValue = function setOnceReturnValue(value) { 277 | this._onceReturnValue = value; 278 | return this; 279 | }; 280 | 281 | proto._getOnceReturnValue = function _getOnceReturnValue() { 282 | if (this.hasOwnProperty('_onceReturnValue')) { 283 | return this._onceReturnValue; 284 | } else { 285 | return true; 286 | } 287 | }; 288 | 289 | proto._getEvents = function _getEvents() { 290 | return this._events || (this._events = {}); 291 | }; 292 | 293 | EventEmitter.noConflict = function noConflict() { 294 | exports.EventEmitter = originalGlobalValue; 295 | return EventEmitter; 296 | }; 297 | 298 | export default EventEmitter; 299 | -------------------------------------------------------------------------------- /dist/emitter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | function EventEmitter() {} 7 | 8 | var proto = EventEmitter.prototype; 9 | var originalGlobalValue = exports.EventEmitter; 10 | 11 | function indexOfListener(listeners, listener) { 12 | var i = listeners.length; 13 | while (i--) { 14 | if (listeners[i].listener === listener) { 15 | return i; 16 | } 17 | } 18 | 19 | return -1; 20 | } 21 | 22 | function alias(name) { 23 | return function aliasClosure() { 24 | return this[name].apply(this, arguments); 25 | }; 26 | } 27 | 28 | proto.getListeners = function getListeners(evt) { 29 | var events = this._getEvents(); 30 | var response; 31 | var key; 32 | 33 | if (evt instanceof RegExp) { 34 | response = {}; 35 | for (key in events) { 36 | if (events.hasOwnProperty(key) && evt.test(key)) { 37 | response[key] = events[key]; 38 | } 39 | } 40 | } else { 41 | response = events[evt] || (events[evt] = []); 42 | } 43 | 44 | return response; 45 | }; 46 | 47 | /** 48 | * Takes a list of listener objects and flattens it into a list of listener functions. 49 | * 50 | * @param {Object[]} listeners Raw listener objects. 51 | * @return {Function[]} Just the listener functions. 52 | */ 53 | proto.flattenListeners = function flattenListeners(listeners) { 54 | var flatListeners = []; 55 | var i; 56 | 57 | for (i = 0; i < listeners.length; i += 1) { 58 | flatListeners.push(listeners[i].listener); 59 | } 60 | 61 | return flatListeners; 62 | }; 63 | 64 | /** 65 | * Fetches the requested listeners via getListeners but will always return the results inside an object. This is mainly for internal use but others may find it useful. 66 | * 67 | * @param {String|RegExp} evt Name of the event to return the listeners from. 68 | * @return {Object} All listener functions for an event in an object. 69 | */ 70 | proto.getListenersAsObject = function getListenersAsObject(evt) { 71 | var listeners = this.getListeners(evt); 72 | var response; 73 | 74 | if (listeners instanceof Array) { 75 | response = {}; 76 | response[evt] = listeners; 77 | } 78 | 79 | return response || listeners; 80 | }; 81 | 82 | function isValidListener(listener) { 83 | if (typeof listener === 'function' || listener instanceof RegExp) { 84 | return true; 85 | } else if (listener && typeof listener === 'object') { 86 | return isValidListener(listener.listener); 87 | } else { 88 | return false; 89 | } 90 | } 91 | 92 | proto.addListener = function addListener(evt, listener) { 93 | if (!isValidListener(listener)) { 94 | throw new TypeError('listener must be a function'); 95 | } 96 | 97 | var listeners = this.getListenersAsObject(evt); 98 | var listenerIsWrapped = typeof listener === 'object'; 99 | var key; 100 | var uid; 101 | for (key in listeners) { 102 | if (listeners.hasOwnProperty(key) && indexOfListener(listeners[key], listener) === -1) { 103 | uid = 'lisitener_' + key + '_' + new Date().getTime(); 104 | listeners[key].push(listenerIsWrapped ? listener : { 105 | listener: listener, 106 | once: false, 107 | uid: uid 108 | }); 109 | } 110 | } 111 | return function () { 112 | var removeIndex = listeners[key].findIndex(function (o) { 113 | return o.uid === uid; 114 | }); 115 | if (removeIndex !== -1) { 116 | listeners[key].splice(removeIndex, 1); 117 | } 118 | return proto; 119 | }; 120 | }; 121 | 122 | proto.on = alias('addListener'); 123 | 124 | proto.addOnceListener = function addOnceListener(evt, listener) { 125 | return this.addListener(evt, { 126 | listener: listener, 127 | once: true 128 | }); 129 | }; 130 | 131 | proto.once = alias('addOnceListener'); 132 | 133 | proto.defineEvent = function defineEvent(evt) { 134 | this.getListeners(evt); 135 | return this; 136 | }; 137 | 138 | proto.defineEvents = function defineEvents(evts) { 139 | for (var i = 0; i < evts.length; i += 1) { 140 | this.defineEvent(evts[i]); 141 | } 142 | return this; 143 | }; 144 | 145 | proto.removeListener = function removeListener(evt, listener) { 146 | var listeners = this.getListenersAsObject(evt); 147 | var index; 148 | var key; 149 | 150 | for (key in listeners) { 151 | if (listeners.hasOwnProperty(key)) { 152 | index = indexOfListener(listeners[key], listener); 153 | if (index !== -1) { 154 | listeners[key].splice(index, 1); 155 | } 156 | } 157 | } 158 | 159 | return this; 160 | }; 161 | 162 | proto.off = alias('removeListener'); 163 | 164 | proto.addListeners = function addListeners(evt, listeners) { 165 | return this.manipulateListeners(false, evt, listeners); 166 | }; 167 | 168 | proto.removeListeners = function removeListeners(evt, listeners) { 169 | return this.manipulateListeners(true, evt, listeners); 170 | }; 171 | 172 | proto.manipulateListeners = function manipulateListeners(remove, evt, listeners) { 173 | var i; 174 | var value; 175 | var single = remove ? this.removeListener : this.addListener; 176 | var multiple = remove ? this.removeListeners : this.addListeners; 177 | 178 | // If evt is an object then pass each of its properties to this method 179 | if (typeof evt === 'object' && !(evt instanceof RegExp)) { 180 | for (i in evt) { 181 | if (evt.hasOwnProperty(i) && (value = evt[i])) { 182 | if (typeof value === 'function') { 183 | single.call(this, i, value); 184 | } else { 185 | multiple.call(this, i, value); 186 | } 187 | } 188 | } 189 | } else { 190 | i = listeners.length; 191 | while (i--) { 192 | single.call(this, evt, listeners[i]); 193 | } 194 | } 195 | 196 | return this; 197 | }; 198 | 199 | proto.removeEvent = function removeEvent(evt) { 200 | var type = typeof evt; 201 | var events = this._getEvents(); 202 | var key; 203 | if (type === 'string') { 204 | delete events[evt]; 205 | } else if (evt instanceof RegExp) { 206 | // Remove all events matching the regex. 207 | for (key in events) { 208 | if (events.hasOwnProperty(key) && evt.test(key)) { 209 | delete events[key]; 210 | } 211 | } 212 | } else { 213 | delete this._events; 214 | } 215 | 216 | return this; 217 | }; 218 | 219 | proto.removeAllListeners = alias('removeEvent'); 220 | 221 | proto.emitEvent = function emitEvent(evt, args) { 222 | var listenersMap = this.getListenersAsObject(evt); 223 | var listeners; 224 | var listener; 225 | var i; 226 | var key; 227 | var response; 228 | for (key in listenersMap) { 229 | if (listenersMap.hasOwnProperty(key)) { 230 | listeners = listenersMap[key].slice(0); 231 | 232 | for (i = 0; i < listeners.length; i++) { 233 | listener = listeners[i]; 234 | if (listener.once === true) { 235 | this.removeListener(evt, listener.listener); 236 | } 237 | response = listener.listener.call(this, args || []); 238 | if (response === this._getOnceReturnValue()) { 239 | this.removeListener(evt, listener.listener); 240 | } 241 | } 242 | } 243 | } 244 | 245 | return this; 246 | }; 247 | 248 | proto.emitEventChain = function emitEventWithNext(evt, args) { 249 | var _this = this; 250 | 251 | var cb = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : function (d) { 252 | return d; 253 | }; 254 | 255 | var listenersMap = this.getListenersAsObject(evt); 256 | var listeners; 257 | var key; 258 | for (key in listenersMap) { 259 | if (listenersMap.hasOwnProperty(key)) { 260 | (function () { 261 | listeners = listenersMap[key].slice(0); 262 | listeners.push({ 263 | listener: function listener(action, next) { 264 | var last = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; 265 | 266 | // 最后一个回调获取最终上一次的结果 267 | cb(last); 268 | } 269 | }); 270 | var that = _this; 271 | (function createNextFunc(i) { 272 | var listener = listeners[i]; 273 | if (!listener) { 274 | return function (d) { 275 | return d; 276 | }; 277 | } 278 | if (listener.once === true) { 279 | this.removeListener(evt, listener.listener); 280 | } 281 | return listener.listener.bind(that, args || [], createNextFunc(i + 1)); 282 | })(0)(); 283 | })(); 284 | } 285 | } 286 | return this; 287 | }; 288 | 289 | proto.trigger = alias('emitEvent'); 290 | proto.emit = function emit(evt) { 291 | var args = Array.prototype.slice.call(arguments, 1); 292 | return this.emitEvent(evt, args); 293 | }; 294 | 295 | proto.setOnceReturnValue = function setOnceReturnValue(value) { 296 | this._onceReturnValue = value; 297 | return this; 298 | }; 299 | 300 | proto._getOnceReturnValue = function _getOnceReturnValue() { 301 | if (this.hasOwnProperty('_onceReturnValue')) { 302 | return this._onceReturnValue; 303 | } else { 304 | return true; 305 | } 306 | }; 307 | 308 | proto._getEvents = function _getEvents() { 309 | return this._events || (this._events = {}); 310 | }; 311 | 312 | EventEmitter.noConflict = function noConflict() { 313 | exports.EventEmitter = originalGlobalValue; 314 | return EventEmitter; 315 | }; 316 | 317 | exports.default = EventEmitter; -------------------------------------------------------------------------------- /src/createHelpers.js: -------------------------------------------------------------------------------- 1 | import { setIn, update, produce, deleteIn } from './utils/manipulate'; 2 | import { isObject, isFunc, isString } from './utils/is'; 3 | import global from './global'; 4 | import wrapDataInstance from './wrapDataInstance'; 5 | 6 | // TODO: 这个页面需要重构! 7 | function startsWith(data, search, pos) { 8 | return data.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search; 9 | }; 10 | 11 | function dispatchActionPromise(instance, args) { 12 | return new Promise((resolve, reject) => { 13 | try { 14 | instance.emitEventChain('dispatchAction', args, d => { 15 | resolve(d); 16 | }); 17 | } catch (e) { 18 | reject(e); 19 | } 20 | }); 21 | } 22 | 23 | // 保证每次更改 store 是 immutable 的 24 | const innerMutation = { 25 | $setIn: (s, d) => setIn(s, d.path, d.value), 26 | $update: (s, o) => update(s, o), 27 | $deleteIn: (s, d) => deleteIn(s, d), 28 | $resetStore: function() { 29 | const { config } = global.getInstanceByViewId(global.getCurrentViewId()); 30 | let next = { ...config.state }; 31 | return next; 32 | } 33 | }; 34 | function mutationHandler (func, state, payload, innerHelper) { 35 | if (innerHelper) { 36 | func = isFunc(innerHelper) ? func || innerHelper : func || innerMutation[innerHelper]; 37 | } 38 | if (!func) { 39 | return payload; 40 | } 41 | const payloadWithHelper = wrapDataInstance(payload); 42 | if (func._shouldImmutable) { 43 | return produce(state, draftState => { 44 | func(draftState, payloadWithHelper); 45 | }); 46 | } 47 | const result = func(state, payloadWithHelper); 48 | // 确保return的值是一个新对象 49 | return result === state ? { ...result } : result; 50 | } 51 | 52 | function commitGlobal(type, payload, innerHelper) { 53 | const { 54 | mutations = {} 55 | } = global.globalStoreConfig; 56 | if (!type) { 57 | throw new Error(`not found ${type} action`); 58 | } 59 | if (isObject(type)) { 60 | payload = type; 61 | type = 'update'; 62 | } 63 | const finalMutation = mutationHandler(mutations[type], global.getGlobalState(), payload, innerHelper); 64 | const tmp = { state: finalMutation, mutation: { type: `$global:${type}`, payload } }; 65 | global.emitter.emitEvent('updateState', tmp); 66 | // commit 的结果是一个同步行为 67 | return global.getGlobalState(); 68 | } 69 | 70 | async function dispatchGlobal(type, payload) { 71 | const { 72 | actions = {} 73 | } = global.globalStoreConfig; 74 | const actionFunc = actions[type]; 75 | const self = this; 76 | let res = {}; 77 | res = await dispatchActionPromise(global.emitter, { type, payload }); 78 | if (!actionFunc) { 79 | console.warn('not found action', type, actions); 80 | return Promise.resolve(res); 81 | } 82 | res = await actionFunc.call(self, { 83 | commit: commitGlobal.bind(self), 84 | dispatch: dispatchGlobal.bind(self), 85 | message: global.messageManager, 86 | put: function (type, ...args) { 87 | const func = actions[type]; 88 | if (!func) { 89 | throw new Error(`not found ${type} action`); 90 | } 91 | if (func) { 92 | func.apply(self, args); 93 | } 94 | }, 95 | get state() { 96 | return wrapDataInstance(global.getGlobalState()); 97 | }, 98 | get getters() { 99 | return wrapDataInstance(global.getGlobalState().$getters); 100 | }, 101 | get global() { 102 | return wrapDataInstance(global.getGlobalState()); 103 | }, 104 | getRef(name) { 105 | return global.getComponentRef(name); 106 | }, 107 | select(filter) { 108 | return filter(wrapDataInstance({ ...global.getGlobalState() })); 109 | }, 110 | getState(instanceName) { 111 | if (!instanceName) { 112 | return wrapDataInstance(global.getGlobalState()); 113 | } 114 | return global.getState(instanceName); 115 | } 116 | }, wrapDataInstance(payload)); 117 | // 保证结果为一个 promise 118 | if (res instanceof Promise) { 119 | return res; 120 | } 121 | return Promise.resolve(res); 122 | } 123 | 124 | function getConfigFromGlobal(global, key) { 125 | const targetInstanceObj = global.getInstance(key || global.getCurrentViewId()); 126 | const instance = targetInstanceObj ? targetInstanceObj.store.getInstance() : {}; 127 | return { ...targetInstanceObj.config, instance }; 128 | } 129 | function getConfigFromInstance(target) { 130 | return { 131 | mutations: target.mutations, 132 | actions: target.actions, 133 | instance: target.getInstance() 134 | }; 135 | } 136 | export function createConnectHelpers(global, key, config = {}, isInstance) { 137 | return { 138 | commitGlobal: commitGlobal.bind(this), 139 | dispatchGlobal: dispatchGlobal.bind(this), 140 | commit(type, payload, innerHelper) { 141 | const finalKey = key || global.getCurrentPath() || global.getCurrentViewId() || -1; 142 | const { instance, mutations = {} } = global.storeInstance ? getConfigFromInstance(global) : getConfigFromGlobal(global, finalKey); 143 | Object.assign(mutations, config.mutations); 144 | if (!type) { 145 | throw new Error(`${type} not found`); 146 | } 147 | if (isObject(type)) { 148 | payload = type; 149 | type = 'update'; 150 | } 151 | if (isString(type) && startsWith(type, '$global:')) { 152 | const realType = type.split(':').pop(); 153 | return commitGlobal.call(instance, realType, payload); 154 | } 155 | const prevState = { ...instance.data }; 156 | const finalMutation = mutationHandler(mutations[type], wrapDataInstance(instance.data), payload, innerHelper); 157 | instance.$emitter.emitEvent('updateState', { state: finalMutation, mutation: { type, payload }, prevState }); 158 | // commit 的结果是一个同步行为 159 | return instance.data; 160 | }, 161 | async dispatch(type, payload) { 162 | const finalKey = key || global.getCurrentPath() || global.getCurrentViewId() || -1; 163 | const { 164 | instance, 165 | mutations = {}, 166 | actions = {} 167 | } = global.storeInstance ? getConfigFromInstance(global) : getConfigFromGlobal(global, finalKey); 168 | if (!type) { 169 | throw new Error('action type not found'); 170 | } 171 | if (isString(type) && startsWith(type, '$global:')) { 172 | const realType = type.split(':').pop(); 173 | return dispatchGlobal.call(this, realType, payload); 174 | } 175 | // 获取目标 instance 的数据 176 | Object.assign(mutations, config.mutations); 177 | Object.assign(actions, config.actions); 178 | 179 | const actionFunc = actions[type]; 180 | const self = this; 181 | let res = {}; 182 | res = await dispatchActionPromise(instance.$emitter, { type, payload }); 183 | if (!actionFunc) { 184 | console.warn('not found action', type, actions); 185 | return Promise.resolve(res); 186 | } 187 | res = await actionFunc.call(self, { 188 | commit: this.commit.bind(self), 189 | dispatch: this.dispatch.bind(self), 190 | message: global.messageManager, 191 | dispatchGlobal: dispatchGlobal.bind(self), 192 | commitGlobal: commitGlobal.bind(self), 193 | put: function (type, ...args) { 194 | const func = actions[type]; 195 | if (!func) { 196 | throw new Error(`not found ${type} action`); 197 | } 198 | if (func) { 199 | func.apply(self, args); 200 | } 201 | }, 202 | get state() { 203 | return wrapDataInstance(instance.data, self); 204 | }, 205 | get getters() { 206 | return wrapDataInstance(instance.data.$getters, self); 207 | }, 208 | get global() { 209 | return wrapDataInstance(instance.data.$global); 210 | }, 211 | getRef(name) { 212 | return global.getComponentRef(name); 213 | }, 214 | getState(instanceName) { 215 | if (!instanceName) { 216 | return wrapDataInstance(instance.data, self); 217 | } 218 | return global.getState(instanceName); 219 | }, 220 | select(filter) { 221 | return filter(wrapDataInstance({ ...instance.data })); 222 | } 223 | }, wrapDataInstance(payload)); 224 | // 保证结果为一个 promise 225 | if (res instanceof Promise) { 226 | return res; 227 | } 228 | return Promise.resolve(res); 229 | } 230 | }; 231 | } 232 | // 创建 commit 和 dispatch instance 233 | export default function createHelpers(actions, mutationsObj, emitter, getInstance) { 234 | const mutations = Object.assign({}, mutationsObj, innerMutation); 235 | return { 236 | commitGlobal: commitGlobal.bind(this), 237 | dispatchGlobal: dispatchGlobal.bind(this), 238 | commit(type, payload, innerHelper) { 239 | if (!type) { 240 | throw new Error(`not found ${type} action`); 241 | } 242 | if (isObject(type)) { 243 | payload = type; 244 | type = 'update'; 245 | } 246 | if (isString(type) && startsWith(type, '$global:')) { 247 | const realType = type.split(':').pop(); 248 | return commitGlobal.call(this, realType, payload); 249 | } 250 | const prevState = { ...this.data }; 251 | const finalMutation = mutationHandler(mutations[type], wrapDataInstance(this.data), payload, innerHelper); 252 | // 触发更新机制 253 | emitter.emitEvent('updateState', { state: finalMutation, mutation: { type, payload }, prevState }); 254 | // commit 的结果是一个同步行为,返回值 255 | return this.data; 256 | }, 257 | async dispatch(type, payload) { 258 | const actionCache = Object.assign({}, actions, this); 259 | if (!type) { 260 | throw new Error('action type not found'); 261 | } 262 | if (isString(type) && startsWith(type, '$global:')) { 263 | const realType = type.split(':').pop(); 264 | return dispatchGlobal.call(this, realType, payload); 265 | } 266 | const actionFunc = actionCache[type]; 267 | const self = this; 268 | let res = {}; 269 | res = await dispatchActionPromise(emitter, { type, payload }); 270 | if (!actionFunc) { 271 | console.warn('not found action', type, actions); 272 | return Promise.resolve(res); 273 | } 274 | res = await actionFunc.call(self, { 275 | commit: this.commit.bind(self), 276 | dispatch: this.dispatch.bind(self), 277 | dispatchGlobal: dispatchGlobal.bind(self), 278 | commitGlobal: commitGlobal.bind(self), 279 | message: global.messageManager, 280 | put: function (type, ...args) { 281 | const func = actionCache[type]; 282 | if (!func) { 283 | throw new Error(`not found ${type} action`); 284 | } 285 | if (func) { 286 | func.apply(self, args); 287 | } 288 | }, 289 | get state() { 290 | return wrapDataInstance(self.data, self); 291 | }, 292 | get getters() { 293 | return wrapDataInstance(self.data.$getters, self); 294 | }, 295 | get global() { 296 | return wrapDataInstance(self.data.$global); 297 | }, 298 | getRef(name) { 299 | return global.getComponentRef(name); 300 | }, 301 | getState(instanceName) { 302 | if (!instanceName) { 303 | return wrapDataInstance(self.data, self); 304 | } 305 | return global.getState(instanceName); 306 | }, 307 | select(filter) { 308 | return filter(wrapDataInstance({ ...self.data })); 309 | } 310 | }, wrapDataInstance(payload)); 311 | // 保证结果为一个 promise 312 | if (res instanceof Promise) { 313 | return res; 314 | } 315 | return Promise.resolve(res); 316 | } 317 | }; 318 | } 319 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { isString, isArray, isFunc } from './utils/is'; 2 | import EventEmitter from './emitter'; 3 | import _innerPlugins from './innerPlugins'; 4 | import mapGettersToState from './mapGettersToState'; 5 | import createHelpers, { createConnectHelpers } from './createHelpers'; 6 | import { setDataByStateProps, setStoreDataByState } from './dataTransform'; 7 | import wrapDataInstance from './wrapDataInstance'; 8 | import global from './global'; 9 | import connect from './connect'; 10 | import GlobalStore from './provider'; 11 | import { mapActionsToMethod, mapMutationsToMethod } from './mapHelpersToMethod'; 12 | import configPreHandler from './storeConfigPreHandle'; 13 | import wrapState from './utils/wrapState'; 14 | import defaultMixin from './mixins/default'; 15 | import './polyfill/index'; 16 | 17 | function getPath(link) { 18 | return isString(link) && link.split('/')[1]; 19 | } 20 | 21 | class Store { 22 | constructor(store, options) { 23 | this.$global = global; 24 | this.$emitter = new EventEmitter(); 25 | // 预处理配置转化 26 | configPreHandler(store); 27 | Object.assign(this, { 28 | connectGlobal: store.connectGlobal, 29 | mapGlobals: store.mapGlobals, 30 | actions: store.actions, 31 | methods: store.methods || {}, 32 | mutations: store.mutations || {}, 33 | plugins: store.plugins || [], 34 | getters: store.getters || {}, 35 | instanceName: store.namespace || store.instanceName 36 | }); 37 | this.stateConfig = mapGettersToState(store.state || {}, this.getters, this); 38 | this.stateConfig.$global = this.connectGlobal ? global.getGlobalState(this.mapGlobals) : {}; 39 | this.subscribe = this.subscribe.bind(this); 40 | this.register = this.register.bind(this); 41 | this.subscribeAction = this.subscribeAction.bind(this); 42 | this.when = this.when.bind(this); 43 | this.watch = this.watch.bind(this); 44 | } 45 | getInstance() { 46 | return this.storeInstance; 47 | } 48 | watch(predicate, effect) { 49 | this.when(predicate, effect, true); 50 | } 51 | // 实现 mobx when 52 | when (predicate, effect, isWatch) { 53 | const emitter = this.$emitter; 54 | if (!predicate) return Promise.reject(); 55 | return new Promise((resolve) => { 56 | const initialData = this.storeInstance ? this.storeInstance.data : {}; 57 | if (predicate(initialData)) { 58 | if (effect) { 59 | effect.call(this, initialData); 60 | } 61 | return resolve(initialData); 62 | } 63 | const dispose = emitter.addListener('updateState', ({ state, mutation, prevState }) => { 64 | const newData = setStoreDataByState(this.storeInstance.data, state); 65 | const currentPageInstance = getCurrentPages().pop() || {}; 66 | const instanceView = this.storeInstance.$viewId || -1; 67 | const currentView = currentPageInstance.$viewId || -1; 68 | // 已经不在当前页面的不再触发 69 | if (instanceView === currentView) { 70 | if (predicate(newData)) { 71 | dispose(); 72 | if (effect) { 73 | effect.call(this, newData); 74 | } 75 | resolve(newData); 76 | } 77 | } 78 | }); 79 | }); 80 | } 81 | // 实现 store.subscribe 82 | subscribe (subscriber, actionSubscriber) { 83 | const emitter = this.$emitter; 84 | const originViewInstance = getCurrentPages().pop() || {}; 85 | if (subscriber) { 86 | this.storeUpdateLisitenerDispose = emitter.addListener('updateState', ({ state, mutation, prevState }) => { 87 | const currentPageInstance = getCurrentPages().pop() || {}; 88 | const instanceView = originViewInstance.$viewId || -1; 89 | const currentView = currentPageInstance.$viewId || -1; 90 | // 已经不在当前页面的不再触发 91 | if (instanceView === currentView) { 92 | subscriber(mutation, wrapState({ ...this.storeInstance.data }), wrapState({ ...prevState })); 93 | } 94 | }); 95 | } 96 | if (actionSubscriber) { 97 | this.storeDispatchActionLisitenerDispose = emitter.addListener('dispatchAction', (action, next) => { 98 | actionSubscriber(action, next); 99 | }); 100 | } 101 | }; 102 | subscribeAction(actionSubscriber) { 103 | const emitter = this.$emitter; 104 | const originViewInstance = getCurrentPages().pop() || {}; 105 | if (actionSubscriber) { 106 | emitter.addListener('dispatchAction', (action, next) => { 107 | const currentPageInstance = getCurrentPages().pop() || {}; 108 | const instanceView = originViewInstance.$viewId || -1; 109 | const currentView = currentPageInstance.$viewId || -1; 110 | if (instanceView === currentView) { 111 | return actionSubscriber(action, next); 112 | } 113 | }); 114 | } 115 | } 116 | use(option = defaultMixin) { 117 | if (isFunc(option)) { 118 | return option.call(this, this.register, global); 119 | } else { 120 | return this.register(option); 121 | } 122 | } 123 | register(config = {}) { 124 | const that = this; 125 | config.data = config.data || {}; 126 | Object.assign(config.data, this.stateConfig, config.state); 127 | const initialState = { ...config.data }; 128 | const originOnLoad = config.onLoad; 129 | const originOnUnload = config.onUnload; 130 | const originOnShow = config.onShow; 131 | const originOnHide = config.onHide; 132 | const emitter = this.$emitter; 133 | // mappers 134 | if (config.mapActionsToMethod) { 135 | mapActionsToMethod(config.mapActionsToMethod, this.actions, config); 136 | } 137 | if (config.methods) { 138 | mapMutationsToMethod(config.methods, config); 139 | } 140 | if (config.mapMutationsToMethod) { 141 | mapMutationsToMethod(config.mapMutationsToMethod, config); 142 | } 143 | config.onHide = function() { 144 | const currentPageInstance = getCurrentPages().pop() || {}; 145 | global.emitter.emitEvent('updateCurrentPath', { 146 | from: getPath(currentPageInstance.route), 147 | fromViewId: currentPageInstance.$viewId || -1 148 | }); 149 | originOnHide && originOnHide.apply(this, arguments); 150 | this._isHided = true; 151 | }; 152 | config.onUnload = function() { 153 | const currentPageInstance = getCurrentPages().pop() || {}; 154 | global.emitter.emitEvent('updateCurrentPath', { 155 | from: getPath(currentPageInstance.route) 156 | }); 157 | this.herculexUpdateLisitener && this.herculexUpdateLisitener(); 158 | this.herculexUpdateLisitenerGlobal && this.herculexUpdateLisitenerGlobal(); 159 | if (this.$store) { 160 | this.$store.storeUpdateLisitenerDispose && this.$store.storeUpdateLisitenerDispose(); 161 | this.$store.storeDispatchActionLisitenerDispose && this.$store.storeDispatchActionLisitenerDispose(); 162 | } 163 | originOnUnload && originOnUnload.apply(this, arguments); 164 | }; 165 | config.onShow = function(d) { 166 | const currentPageInstance = getCurrentPages().pop() || {}; 167 | // 消费 Resume 字段 168 | const resumeData = global.messageManager.pop('$RESUME') || {}; 169 | global.emitter.emitEvent('updateCurrentPath', Object.assign(currentPageInstance.$routeConfig || {}, { 170 | currentPath: getPath(currentPageInstance.route), 171 | context: resumeData 172 | })); 173 | // 如果有开全局,先触发 174 | if (that.connectGlobal) { 175 | // sync global data 176 | emitter.emitEvent('updateState', { 177 | state: { 178 | ...this.data, 179 | $global: { 180 | ...this.data.$global, 181 | ...global.getGlobalState(this.mapGlobals) 182 | } 183 | }, 184 | mutation: { 185 | type: 'sync_global_data' 186 | }, 187 | prevState: this.data 188 | }); 189 | } 190 | originOnShow && originOnShow.apply(this, arguments); 191 | if (this._isHided) { 192 | config.onResume && config.onResume.call(this, Object.assign({}, d, resumeData)); 193 | this._isHided = false; 194 | } 195 | }; 196 | config.onLoad = function(query) { 197 | const onloadInstance = this; 198 | this.$emitter = emitter; 199 | this.$globalEmitter = global.emitter; 200 | this.$message = global.messageManager; 201 | this.$store = that; 202 | this.$when = that.when; 203 | // 先榜上更新 store 的 监听器 204 | this.herculexUpdateLisitener = emitter.addListener('updateState', ({ state }) => { 205 | const newData = setStoreDataByState(this.data, state); 206 | const currentPageInstance = getCurrentPages().pop() || {}; 207 | const instanceView = onloadInstance.$viewId || -1; 208 | const currentView = currentPageInstance.$viewId || -1; 209 | // 已经不在当前页面的不再触发 210 | if (instanceView === currentView) { 211 | this.setData(newData); 212 | } 213 | }); 214 | if (that.connectGlobal) { 215 | // 立马触发同步 216 | emitter.emitEvent('updateState', { 217 | state: { 218 | ...this.data, 219 | $global: { 220 | ...this.data.$global, 221 | ...global.getGlobalState(this.mapGlobals) 222 | } 223 | }, 224 | mutation: { 225 | type: 'sync_global_data' 226 | }, 227 | prevState: this.data 228 | }); 229 | 230 | // 增加nextprops的关联 231 | this.herculexUpdateLisitenerGlobal = global.emitter.addListener('updateGlobalStore', () => { 232 | const currentPageInstance = getCurrentPages().pop() || {}; 233 | const instanceView = onloadInstance.$viewId || -1; 234 | const currentView = currentPageInstance.$viewId || -1; 235 | // 已经不在当前页面的不再触发 236 | if (instanceView !== currentView) return; 237 | emitter.emitEvent('updateState', { 238 | state: { 239 | ...this.data, 240 | $global: { 241 | ...this.data.$global, 242 | ...global.getGlobalState(this.mapGlobals) 243 | } 244 | }, 245 | mutation: { 246 | type: 'sync_global_data' 247 | }, 248 | prevState: this.data 249 | }); 250 | }); 251 | } 252 | this.subscribe = that.subscribe; 253 | this.subscribeAction = that.subscribeAction; 254 | // 设置页面 path 和 query 255 | const currentPageInstance = getCurrentPages().pop() || {}; 256 | const currentPath = getPath(currentPageInstance.route); 257 | // 外面携带的数据 258 | const contextData = global.messageManager.pop('$RESUME') || {}; 259 | const viewId = currentPageInstance.$viewId || -1; 260 | this.$routeConfig = { 261 | currentPath, 262 | query, 263 | context: contextData, 264 | viewId 265 | }; 266 | global.emitter.emitEvent('updateCurrentPath', this.$routeConfig); 267 | // query.$context = loadData; 268 | that.storeInstance = this; 269 | const name = that.instanceName || currentPath || viewId || -1; 270 | // 把命名空间灌到实例 271 | this.instanceName = name; 272 | global.registerInstance(name, { 273 | config: { actions: that.actions, mutations: that.mutations, state: initialState }, 274 | store: that, 275 | name, 276 | currentPath, 277 | viewId 278 | }); 279 | if (that.plugins) { 280 | that.plugins.forEach(element => { 281 | const pluginFunc = isString(element) ? _innerPlugins[element] : element; 282 | pluginFunc(that.storeInstance); 283 | }); 284 | } 285 | // 绑定属性关系 286 | Object.defineProperty(this, 'state', { 287 | get: function() { return wrapDataInstance(this.data); } 288 | }); 289 | this.$getters = wrapDataInstance(this.state.$getters); 290 | // this.$global = wrapDataInstance({ ...this.state.$global }); 291 | // 获取其他 store 的只读数据 292 | this.$getState = function(name) { 293 | if (!name) return this.state; 294 | return global.getState(name); 295 | }; 296 | this.$getRef = function(name) { 297 | return global.getComponentRef(name); 298 | }; 299 | 300 | if (originOnLoad) { 301 | originOnLoad.call(this, query, contextData); 302 | } 303 | }; 304 | return { 305 | ...config, 306 | ...createHelpers.call(this, that.actions, that.mutations, that.$emitter) 307 | }; 308 | } 309 | // connect(options) { 310 | // const { mapStateToProps = [], mapGettersToProps } = options; 311 | // const that = this; 312 | // return function (config) { 313 | // const _didMount = config.didMount; 314 | // Object.assign(that.mutations, config.mutations || {}); 315 | // return { 316 | // ...config, 317 | // methods: { 318 | // ...config.methods, 319 | // ...createConnectHelpers.call(this, that) 320 | // }, 321 | // didMount() { 322 | // const initialData = setDataByStateProps(mapStateToProps, that.getInstance().data, config, mapGettersToProps); 323 | // this.setData(initialData); 324 | // if (mapStateToProps) { 325 | // that.$emitter.addListener('updateState', ({state = {}}) => { 326 | // const nextData = setDataByStateProps(mapStateToProps, state, config, mapGettersToProps); 327 | // this.setData(nextData); 328 | // }); 329 | // } 330 | // if (typeof _didMount === 'function') { 331 | // _didMount.call(this); 332 | // } 333 | // } 334 | // }; 335 | // }; 336 | // } 337 | } 338 | 339 | export default Store; 340 | export { 341 | connect, 342 | GlobalStore 343 | }; 344 | -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.GlobalStore = exports.connect = undefined; 7 | 8 | var _assign = require('babel-runtime/core-js/object/assign'); 9 | 10 | var _assign2 = _interopRequireDefault(_assign); 11 | 12 | var _extends = _assign2.default || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; 13 | 14 | var _is = require('./utils/is'); 15 | 16 | var _emitter = require('./emitter'); 17 | 18 | var _emitter2 = _interopRequireDefault(_emitter); 19 | 20 | var _innerPlugins2 = require('./innerPlugins'); 21 | 22 | var _innerPlugins3 = _interopRequireDefault(_innerPlugins2); 23 | 24 | var _mapGettersToState = require('./mapGettersToState'); 25 | 26 | var _mapGettersToState2 = _interopRequireDefault(_mapGettersToState); 27 | 28 | var _createHelpers = require('./createHelpers'); 29 | 30 | var _createHelpers2 = _interopRequireDefault(_createHelpers); 31 | 32 | var _dataTransform = require('./dataTransform'); 33 | 34 | var _wrapDataInstance = require('./wrapDataInstance'); 35 | 36 | var _wrapDataInstance2 = _interopRequireDefault(_wrapDataInstance); 37 | 38 | var _global = require('./global'); 39 | 40 | var _global2 = _interopRequireDefault(_global); 41 | 42 | var _connect = require('./connect'); 43 | 44 | var _connect2 = _interopRequireDefault(_connect); 45 | 46 | var _provider = require('./provider'); 47 | 48 | var _provider2 = _interopRequireDefault(_provider); 49 | 50 | var _mapHelpersToMethod = require('./mapHelpersToMethod'); 51 | 52 | var _storeConfigPreHandle = require('./storeConfigPreHandle'); 53 | 54 | var _storeConfigPreHandle2 = _interopRequireDefault(_storeConfigPreHandle); 55 | 56 | var _wrapState = require('./utils/wrapState'); 57 | 58 | var _wrapState2 = _interopRequireDefault(_wrapState); 59 | 60 | var _default = require('./mixins/default'); 61 | 62 | var _default2 = _interopRequireDefault(_default); 63 | 64 | require('./polyfill/index'); 65 | 66 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 67 | 68 | var _ExternalPromiseCached; 69 | 70 | function _ExternalPromise() { if (_ExternalPromiseCached) return _ExternalPromiseCached; if (typeof window !== 'undefined' && window.Promise && typeof window.Promise.resolve === 'function') { _ExternalPromiseCached = window.Promise; } else { _ExternalPromiseCached = require('babel-runtime/core-js/promise').default || require('babel-runtime/core-js/promise'); } return _ExternalPromiseCached; } 71 | 72 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 73 | 74 | function getPath(link) { 75 | return (0, _is.isString)(link) && link.split('/')[1]; 76 | } 77 | 78 | var Store = function () { 79 | function Store(store, options) { 80 | _classCallCheck(this, Store); 81 | 82 | this.$global = _global2.default; 83 | this.$emitter = new _emitter2.default(); 84 | // 预处理配置转化 85 | (0, _storeConfigPreHandle2.default)(store); 86 | (0, _assign2.default)(this, { 87 | connectGlobal: store.connectGlobal, 88 | mapGlobals: store.mapGlobals, 89 | actions: store.actions, 90 | methods: store.methods || {}, 91 | mutations: store.mutations || {}, 92 | plugins: store.plugins || [], 93 | getters: store.getters || {}, 94 | instanceName: store.namespace || store.instanceName 95 | }); 96 | this.stateConfig = (0, _mapGettersToState2.default)(store.state || {}, this.getters, this); 97 | this.stateConfig.$global = this.connectGlobal ? _global2.default.getGlobalState(this.mapGlobals) : {}; 98 | this.subscribe = this.subscribe.bind(this); 99 | this.register = this.register.bind(this); 100 | this.subscribeAction = this.subscribeAction.bind(this); 101 | this.when = this.when.bind(this); 102 | this.watch = this.watch.bind(this); 103 | } 104 | 105 | Store.prototype.getInstance = function getInstance() { 106 | return this.storeInstance; 107 | }; 108 | 109 | Store.prototype.watch = function watch(predicate, effect) { 110 | this.when(predicate, effect, true); 111 | }; 112 | // 实现 mobx when 113 | 114 | 115 | Store.prototype.when = function when(predicate, effect, isWatch) { 116 | var _this = this; 117 | 118 | var emitter = this.$emitter; 119 | if (!predicate) return _ExternalPromise().reject(); 120 | return new (_ExternalPromise())(function (resolve) { 121 | var initialData = _this.storeInstance ? _this.storeInstance.data : {}; 122 | if (predicate(initialData)) { 123 | if (effect) { 124 | effect.call(_this, initialData); 125 | } 126 | return resolve(initialData); 127 | } 128 | var dispose = emitter.addListener('updateState', function (_ref) { 129 | var state = _ref.state, 130 | mutation = _ref.mutation, 131 | prevState = _ref.prevState; 132 | 133 | var newData = (0, _dataTransform.setStoreDataByState)(_this.storeInstance.data, state); 134 | var currentPageInstance = getCurrentPages().pop() || {}; 135 | var instanceView = _this.storeInstance.$viewId || -1; 136 | var currentView = currentPageInstance.$viewId || -1; 137 | // 已经不在当前页面的不再触发 138 | if (instanceView === currentView) { 139 | if (predicate(newData)) { 140 | dispose(); 141 | if (effect) { 142 | effect.call(_this, newData); 143 | } 144 | resolve(newData); 145 | } 146 | } 147 | }); 148 | }); 149 | }; 150 | // 实现 store.subscribe 151 | 152 | 153 | Store.prototype.subscribe = function subscribe(subscriber, actionSubscriber) { 154 | var _this2 = this; 155 | 156 | var emitter = this.$emitter; 157 | var originViewInstance = getCurrentPages().pop() || {}; 158 | if (subscriber) { 159 | this.storeUpdateLisitenerDispose = emitter.addListener('updateState', function (_ref2) { 160 | var state = _ref2.state, 161 | mutation = _ref2.mutation, 162 | prevState = _ref2.prevState; 163 | 164 | var currentPageInstance = getCurrentPages().pop() || {}; 165 | var instanceView = originViewInstance.$viewId || -1; 166 | var currentView = currentPageInstance.$viewId || -1; 167 | // 已经不在当前页面的不再触发 168 | if (instanceView === currentView) { 169 | subscriber(mutation, (0, _wrapState2.default)(_extends({}, _this2.storeInstance.data)), (0, _wrapState2.default)(_extends({}, prevState))); 170 | } 171 | }); 172 | } 173 | if (actionSubscriber) { 174 | this.storeDispatchActionLisitenerDispose = emitter.addListener('dispatchAction', function (action, next) { 175 | actionSubscriber(action, next); 176 | }); 177 | } 178 | }; 179 | 180 | Store.prototype.subscribeAction = function subscribeAction(actionSubscriber) { 181 | var emitter = this.$emitter; 182 | var originViewInstance = getCurrentPages().pop() || {}; 183 | if (actionSubscriber) { 184 | emitter.addListener('dispatchAction', function (action, next) { 185 | var currentPageInstance = getCurrentPages().pop() || {}; 186 | var instanceView = originViewInstance.$viewId || -1; 187 | var currentView = currentPageInstance.$viewId || -1; 188 | if (instanceView === currentView) { 189 | return actionSubscriber(action, next); 190 | } 191 | }); 192 | } 193 | }; 194 | 195 | Store.prototype.use = function use() { 196 | var option = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _default2.default; 197 | 198 | if ((0, _is.isFunc)(option)) { 199 | return option.call(this, this.register, _global2.default); 200 | } else { 201 | return this.register(option); 202 | } 203 | }; 204 | 205 | Store.prototype.register = function register() { 206 | var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 207 | 208 | var that = this; 209 | config.data = config.data || {}; 210 | (0, _assign2.default)(config.data, this.stateConfig, config.state); 211 | var initialState = _extends({}, config.data); 212 | var originOnLoad = config.onLoad; 213 | var originOnUnload = config.onUnload; 214 | var originOnShow = config.onShow; 215 | var originOnHide = config.onHide; 216 | var emitter = this.$emitter; 217 | // mappers 218 | if (config.mapActionsToMethod) { 219 | (0, _mapHelpersToMethod.mapActionsToMethod)(config.mapActionsToMethod, this.actions, config); 220 | } 221 | if (config.methods) { 222 | (0, _mapHelpersToMethod.mapMutationsToMethod)(config.methods, config); 223 | } 224 | if (config.mapMutationsToMethod) { 225 | (0, _mapHelpersToMethod.mapMutationsToMethod)(config.mapMutationsToMethod, config); 226 | } 227 | config.onHide = function () { 228 | var currentPageInstance = getCurrentPages().pop() || {}; 229 | _global2.default.emitter.emitEvent('updateCurrentPath', { 230 | from: getPath(currentPageInstance.route), 231 | fromViewId: currentPageInstance.$viewId || -1 232 | }); 233 | originOnHide && originOnHide.apply(this, arguments); 234 | this._isHided = true; 235 | }; 236 | config.onUnload = function () { 237 | var currentPageInstance = getCurrentPages().pop() || {}; 238 | _global2.default.emitter.emitEvent('updateCurrentPath', { 239 | from: getPath(currentPageInstance.route) 240 | }); 241 | this.herculexUpdateLisitener && this.herculexUpdateLisitener(); 242 | this.herculexUpdateLisitenerGlobal && this.herculexUpdateLisitenerGlobal(); 243 | if (this.$store) { 244 | this.$store.storeUpdateLisitenerDispose && this.$store.storeUpdateLisitenerDispose(); 245 | this.$store.storeDispatchActionLisitenerDispose && this.$store.storeDispatchActionLisitenerDispose(); 246 | } 247 | originOnUnload && originOnUnload.apply(this, arguments); 248 | }; 249 | config.onShow = function (d) { 250 | var currentPageInstance = getCurrentPages().pop() || {}; 251 | // 消费 Resume 字段 252 | var resumeData = _global2.default.messageManager.pop('$RESUME') || {}; 253 | _global2.default.emitter.emitEvent('updateCurrentPath', (0, _assign2.default)(currentPageInstance.$routeConfig || {}, { 254 | currentPath: getPath(currentPageInstance.route), 255 | context: resumeData 256 | })); 257 | // 如果有开全局,先触发 258 | if (that.connectGlobal) { 259 | // sync global data 260 | emitter.emitEvent('updateState', { 261 | state: _extends({}, this.data, { 262 | $global: _extends({}, this.data.$global, _global2.default.getGlobalState(this.mapGlobals)) 263 | }), 264 | mutation: { 265 | type: 'sync_global_data' 266 | }, 267 | prevState: this.data 268 | }); 269 | } 270 | originOnShow && originOnShow.apply(this, arguments); 271 | if (this._isHided) { 272 | config.onResume && config.onResume.call(this, (0, _assign2.default)({}, d, resumeData)); 273 | this._isHided = false; 274 | } 275 | }; 276 | config.onLoad = function (query) { 277 | var _this3 = this; 278 | 279 | var onloadInstance = this; 280 | this.$emitter = emitter; 281 | this.$globalEmitter = _global2.default.emitter; 282 | this.$message = _global2.default.messageManager; 283 | this.$store = that; 284 | this.$when = that.when; 285 | // 先榜上更新 store 的 监听器 286 | this.herculexUpdateLisitener = emitter.addListener('updateState', function (_ref3) { 287 | var state = _ref3.state; 288 | 289 | var newData = (0, _dataTransform.setStoreDataByState)(_this3.data, state); 290 | var currentPageInstance = getCurrentPages().pop() || {}; 291 | var instanceView = onloadInstance.$viewId || -1; 292 | var currentView = currentPageInstance.$viewId || -1; 293 | // 已经不在当前页面的不再触发 294 | if (instanceView === currentView) { 295 | _this3.setData(newData); 296 | } 297 | }); 298 | if (that.connectGlobal) { 299 | // 立马触发同步 300 | emitter.emitEvent('updateState', { 301 | state: _extends({}, this.data, { 302 | $global: _extends({}, this.data.$global, _global2.default.getGlobalState(this.mapGlobals)) 303 | }), 304 | mutation: { 305 | type: 'sync_global_data' 306 | }, 307 | prevState: this.data 308 | }); 309 | 310 | // 增加nextprops的关联 311 | this.herculexUpdateLisitenerGlobal = _global2.default.emitter.addListener('updateGlobalStore', function () { 312 | var currentPageInstance = getCurrentPages().pop() || {}; 313 | var instanceView = onloadInstance.$viewId || -1; 314 | var currentView = currentPageInstance.$viewId || -1; 315 | // 已经不在当前页面的不再触发 316 | if (instanceView !== currentView) return; 317 | emitter.emitEvent('updateState', { 318 | state: _extends({}, _this3.data, { 319 | $global: _extends({}, _this3.data.$global, _global2.default.getGlobalState(_this3.mapGlobals)) 320 | }), 321 | mutation: { 322 | type: 'sync_global_data' 323 | }, 324 | prevState: _this3.data 325 | }); 326 | }); 327 | } 328 | this.subscribe = that.subscribe; 329 | this.subscribeAction = that.subscribeAction; 330 | // 设置页面 path 和 query 331 | var currentPageInstance = getCurrentPages().pop() || {}; 332 | var currentPath = getPath(currentPageInstance.route); 333 | // 外面携带的数据 334 | var contextData = _global2.default.messageManager.pop('$RESUME') || {}; 335 | var viewId = currentPageInstance.$viewId || -1; 336 | this.$routeConfig = { 337 | currentPath: currentPath, 338 | query: query, 339 | context: contextData, 340 | viewId: viewId 341 | }; 342 | _global2.default.emitter.emitEvent('updateCurrentPath', this.$routeConfig); 343 | // query.$context = loadData; 344 | that.storeInstance = this; 345 | var name = that.instanceName || currentPath || viewId || -1; 346 | // 把命名空间灌到实例 347 | this.instanceName = name; 348 | _global2.default.registerInstance(name, { 349 | config: { actions: that.actions, mutations: that.mutations, state: initialState }, 350 | store: that, 351 | name: name, 352 | currentPath: currentPath, 353 | viewId: viewId 354 | }); 355 | if (that.plugins) { 356 | that.plugins.forEach(function (element) { 357 | var pluginFunc = (0, _is.isString)(element) ? _innerPlugins3.default[element] : element; 358 | pluginFunc(that.storeInstance); 359 | }); 360 | } 361 | // 绑定属性关系 362 | Object.defineProperty(this, 'state', { 363 | get: function get() { 364 | return (0, _wrapDataInstance2.default)(this.data); 365 | } 366 | }); 367 | this.$getters = (0, _wrapDataInstance2.default)(this.state.$getters); 368 | // this.$global = wrapDataInstance({ ...this.state.$global }); 369 | // 获取其他 store 的只读数据 370 | this.$getState = function (name) { 371 | if (!name) return this.state; 372 | return _global2.default.getState(name); 373 | }; 374 | this.$getRef = function (name) { 375 | return _global2.default.getComponentRef(name); 376 | }; 377 | 378 | if (originOnLoad) { 379 | originOnLoad.call(this, query, contextData); 380 | } 381 | }; 382 | return _extends({}, config, _createHelpers2.default.call(this, that.actions, that.mutations, that.$emitter)); 383 | }; 384 | // connect(options) { 385 | // const { mapStateToProps = [], mapGettersToProps } = options; 386 | // const that = this; 387 | // return function (config) { 388 | // const _didMount = config.didMount; 389 | // Object.assign(that.mutations, config.mutations || {}); 390 | // return { 391 | // ...config, 392 | // methods: { 393 | // ...config.methods, 394 | // ...createConnectHelpers.call(this, that) 395 | // }, 396 | // didMount() { 397 | // const initialData = setDataByStateProps(mapStateToProps, that.getInstance().data, config, mapGettersToProps); 398 | // this.setData(initialData); 399 | // if (mapStateToProps) { 400 | // that.$emitter.addListener('updateState', ({state = {}}) => { 401 | // const nextData = setDataByStateProps(mapStateToProps, state, config, mapGettersToProps); 402 | // this.setData(nextData); 403 | // }); 404 | // } 405 | // if (typeof _didMount === 'function') { 406 | // _didMount.call(this); 407 | // } 408 | // } 409 | // }; 410 | // }; 411 | // } 412 | 413 | 414 | return Store; 415 | }(); 416 | 417 | exports.default = Store; 418 | exports.connect = _connect2.default; 419 | exports.GlobalStore = _provider2.default; -------------------------------------------------------------------------------- /dist/createHelpers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _assign = require('babel-runtime/core-js/object/assign'); 8 | 9 | var _assign2 = _interopRequireDefault(_assign); 10 | 11 | var _extends = _assign2.default || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; 12 | 13 | exports.createConnectHelpers = createConnectHelpers; 14 | exports.default = createHelpers; 15 | 16 | var _manipulate = require('./utils/manipulate'); 17 | 18 | var _is = require('./utils/is'); 19 | 20 | var _global = require('./global'); 21 | 22 | var _global2 = _interopRequireDefault(_global); 23 | 24 | var _wrapDataInstance = require('./wrapDataInstance'); 25 | 26 | var _wrapDataInstance2 = _interopRequireDefault(_wrapDataInstance); 27 | 28 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 29 | 30 | var _ExternalPromiseCached; 31 | 32 | function _ExternalPromise() { if (_ExternalPromiseCached) return _ExternalPromiseCached; if (typeof window !== 'undefined' && window.Promise && typeof window.Promise.resolve === 'function') { _ExternalPromiseCached = window.Promise; } else { _ExternalPromiseCached = require('babel-runtime/core-js/promise').default || require('babel-runtime/core-js/promise'); } return _ExternalPromiseCached; } 33 | 34 | // TODO: 这个页面需要重构! 35 | function startsWith(data, search, pos) { 36 | return data.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search; 37 | }; 38 | 39 | function dispatchActionPromise(instance, args) { 40 | return new (_ExternalPromise())(function (resolve, reject) { 41 | try { 42 | instance.emitEventChain('dispatchAction', args, function (d) { 43 | resolve(d); 44 | }); 45 | } catch (e) { 46 | reject(e); 47 | } 48 | }); 49 | } 50 | 51 | // 保证每次更改 store 是 immutable 的 52 | var innerMutation = { 53 | $setIn: function $setIn(s, d) { 54 | return (0, _manipulate.setIn)(s, d.path, d.value); 55 | }, 56 | $update: function $update(s, o) { 57 | return (0, _manipulate.update)(s, o); 58 | }, 59 | $deleteIn: function $deleteIn(s, d) { 60 | return (0, _manipulate.deleteIn)(s, d); 61 | }, 62 | $resetStore: function $resetStore() { 63 | var _global$getInstanceBy = _global2.default.getInstanceByViewId(_global2.default.getCurrentViewId()), 64 | config = _global$getInstanceBy.config; 65 | 66 | var next = _extends({}, config.state); 67 | return next; 68 | } 69 | }; 70 | function mutationHandler(func, state, payload, innerHelper) { 71 | if (innerHelper) { 72 | func = (0, _is.isFunc)(innerHelper) ? func || innerHelper : func || innerMutation[innerHelper]; 73 | } 74 | if (!func) { 75 | return payload; 76 | } 77 | var payloadWithHelper = (0, _wrapDataInstance2.default)(payload); 78 | if (func._shouldImmutable) { 79 | return (0, _manipulate.produce)(state, function (draftState) { 80 | func(draftState, payloadWithHelper); 81 | }); 82 | } 83 | var result = func(state, payloadWithHelper); 84 | // 确保return的值是一个新对象 85 | return result === state ? _extends({}, result) : result; 86 | } 87 | 88 | function commitGlobal(type, payload, innerHelper) { 89 | var _global$globalStoreCo = _global2.default.globalStoreConfig.mutations, 90 | mutations = _global$globalStoreCo === undefined ? {} : _global$globalStoreCo; 91 | 92 | if (!type) { 93 | throw new Error('not found ' + type + ' action'); 94 | } 95 | if ((0, _is.isObject)(type)) { 96 | payload = type; 97 | type = 'update'; 98 | } 99 | var finalMutation = mutationHandler(mutations[type], _global2.default.getGlobalState(), payload, innerHelper); 100 | var tmp = { state: finalMutation, mutation: { type: '$global:' + type, payload: payload } }; 101 | _global2.default.emitter.emitEvent('updateState', tmp); 102 | // commit 的结果是一个同步行为 103 | return _global2.default.getGlobalState(); 104 | } 105 | 106 | function dispatchGlobal(type, payload) { 107 | return new (_ExternalPromise())(function ($return, $error) { 108 | var _global$globalStoreCo2, actions, actionFunc, self, res; 109 | 110 | _global$globalStoreCo2 = _global2.default.globalStoreConfig.actions, actions = _global$globalStoreCo2 === undefined ? {} : _global$globalStoreCo2; 111 | 112 | actionFunc = actions[type]; 113 | self = this; 114 | res = {}; 115 | return _ExternalPromise().resolve(dispatchActionPromise(_global2.default.emitter, { type: type, payload: payload })).then(function ($await_2) { 116 | try { 117 | res = $await_2; 118 | if (!actionFunc) { 119 | console.warn('not found action', type, actions); 120 | return $return(_ExternalPromise().resolve(res)); 121 | } 122 | return _ExternalPromise().resolve(actionFunc.call(self, { 123 | commit: commitGlobal.bind(self), 124 | dispatch: dispatchGlobal.bind(self), 125 | message: _global2.default.messageManager, 126 | put: function put(type) { 127 | var func = actions[type]; 128 | if (!func) { 129 | throw new Error('not found ' + type + ' action'); 130 | } 131 | if (func) { 132 | for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { 133 | args[_key - 1] = arguments[_key]; 134 | } 135 | 136 | func.apply(self, args); 137 | } 138 | }, 139 | get state() { 140 | return (0, _wrapDataInstance2.default)(_global2.default.getGlobalState()); 141 | }, 142 | get getters() { 143 | return (0, _wrapDataInstance2.default)(_global2.default.getGlobalState().$getters); 144 | }, 145 | get global() { 146 | return (0, _wrapDataInstance2.default)(_global2.default.getGlobalState()); 147 | }, 148 | getRef: function getRef(name) { 149 | return _global2.default.getComponentRef(name); 150 | }, 151 | select: function select(filter) { 152 | return filter((0, _wrapDataInstance2.default)(_extends({}, _global2.default.getGlobalState()))); 153 | }, 154 | getState: function getState(instanceName) { 155 | if (!instanceName) { 156 | return (0, _wrapDataInstance2.default)(_global2.default.getGlobalState()); 157 | } 158 | return _global2.default.getState(instanceName); 159 | } 160 | }, (0, _wrapDataInstance2.default)(payload))).then(function ($await_3) { 161 | try { 162 | res = $await_3; 163 | // 保证结果为一个 promise 164 | if (res instanceof _ExternalPromise()) { 165 | return $return(res); 166 | } 167 | return $return(_ExternalPromise().resolve(res)); 168 | } catch ($boundEx) { 169 | return $error($boundEx); 170 | } 171 | }.bind(this), $error); 172 | } catch ($boundEx) { 173 | return $error($boundEx); 174 | } 175 | }.bind(this), $error); 176 | }.bind(this)); 177 | } 178 | 179 | function getConfigFromGlobal(global, key) { 180 | var targetInstanceObj = global.getInstance(key || global.getCurrentViewId()); 181 | var instance = targetInstanceObj ? targetInstanceObj.store.getInstance() : {}; 182 | return _extends({}, targetInstanceObj.config, { instance: instance }); 183 | } 184 | function getConfigFromInstance(target) { 185 | return { 186 | mutations: target.mutations, 187 | actions: target.actions, 188 | instance: target.getInstance() 189 | }; 190 | } 191 | function createConnectHelpers(global, key) { 192 | var config = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; 193 | var isInstance = arguments[3]; 194 | 195 | return { 196 | commitGlobal: commitGlobal.bind(this), 197 | dispatchGlobal: dispatchGlobal.bind(this), 198 | commit: function commit(type, payload, innerHelper) { 199 | var finalKey = key || global.getCurrentPath() || global.getCurrentViewId() || -1; 200 | 201 | var _ref = global.storeInstance ? getConfigFromInstance(global) : getConfigFromGlobal(global, finalKey), 202 | instance = _ref.instance, 203 | _ref$mutations = _ref.mutations, 204 | mutations = _ref$mutations === undefined ? {} : _ref$mutations; 205 | 206 | (0, _assign2.default)(mutations, config.mutations); 207 | if (!type) { 208 | throw new Error(type + ' not found'); 209 | } 210 | if ((0, _is.isObject)(type)) { 211 | payload = type; 212 | type = 'update'; 213 | } 214 | if ((0, _is.isString)(type) && startsWith(type, '$global:')) { 215 | var realType = type.split(':').pop(); 216 | return commitGlobal.call(instance, realType, payload); 217 | } 218 | var prevState = _extends({}, instance.data); 219 | var finalMutation = mutationHandler(mutations[type], (0, _wrapDataInstance2.default)(instance.data), payload, innerHelper); 220 | instance.$emitter.emitEvent('updateState', { state: finalMutation, mutation: { type: type, payload: payload }, prevState: prevState }); 221 | // commit 的结果是一个同步行为 222 | return instance.data; 223 | }, 224 | dispatch: function dispatch(type, payload) { 225 | return new (_ExternalPromise())(function ($return, $error) { 226 | var finalKey, _ref2, instance, _ref2$mutations, mutations, _ref2$actions, actions, realType, actionFunc, self, res; 227 | 228 | finalKey = key || global.getCurrentPath() || global.getCurrentViewId() || -1; 229 | _ref2 = global.storeInstance ? getConfigFromInstance(global) : getConfigFromGlobal(global, finalKey), instance = _ref2.instance, _ref2$mutations = _ref2.mutations, mutations = _ref2$mutations === undefined ? {} : _ref2$mutations, _ref2$actions = _ref2.actions, actions = _ref2$actions === undefined ? {} : _ref2$actions; 230 | 231 | if (!type) { 232 | return $error(new Error('action type not found')); 233 | } 234 | if ((0, _is.isString)(type) && startsWith(type, '$global:')) { 235 | realType = type.split(':').pop(); 236 | return $return(dispatchGlobal.call(this, realType, payload)); 237 | } 238 | // 获取目标 instance 的数据 239 | (0, _assign2.default)(mutations, config.mutations); 240 | (0, _assign2.default)(actions, config.actions); 241 | 242 | actionFunc = actions[type]; 243 | self = this; 244 | res = {}; 245 | return _ExternalPromise().resolve(dispatchActionPromise(instance.$emitter, { type: type, payload: payload })).then(function ($await_4) { 246 | try { 247 | res = $await_4; 248 | if (!actionFunc) { 249 | console.warn('not found action', type, actions); 250 | return $return(_ExternalPromise().resolve(res)); 251 | } 252 | return _ExternalPromise().resolve(actionFunc.call(self, { 253 | commit: this.commit.bind(self), 254 | dispatch: this.dispatch.bind(self), 255 | message: global.messageManager, 256 | dispatchGlobal: dispatchGlobal.bind(self), 257 | commitGlobal: commitGlobal.bind(self), 258 | put: function put(type) { 259 | var func = actions[type]; 260 | if (!func) { 261 | throw new Error('not found ' + type + ' action'); 262 | } 263 | if (func) { 264 | for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { 265 | args[_key2 - 1] = arguments[_key2]; 266 | } 267 | 268 | func.apply(self, args); 269 | } 270 | }, 271 | get state() { 272 | return (0, _wrapDataInstance2.default)(instance.data, self); 273 | }, 274 | get getters() { 275 | return (0, _wrapDataInstance2.default)(instance.data.$getters, self); 276 | }, 277 | get global() { 278 | return (0, _wrapDataInstance2.default)(instance.data.$global); 279 | }, 280 | getRef: function getRef(name) { 281 | return global.getComponentRef(name); 282 | }, 283 | getState: function getState(instanceName) { 284 | if (!instanceName) { 285 | return (0, _wrapDataInstance2.default)(instance.data, self); 286 | } 287 | return global.getState(instanceName); 288 | }, 289 | select: function select(filter) { 290 | return filter((0, _wrapDataInstance2.default)(_extends({}, instance.data))); 291 | } 292 | }, (0, _wrapDataInstance2.default)(payload))).then(function ($await_5) { 293 | try { 294 | res = $await_5; 295 | // 保证结果为一个 promise 296 | if (res instanceof _ExternalPromise()) { 297 | return $return(res); 298 | } 299 | return $return(_ExternalPromise().resolve(res)); 300 | } catch ($boundEx) { 301 | return $error($boundEx); 302 | } 303 | }.bind(this), $error); 304 | } catch ($boundEx) { 305 | return $error($boundEx); 306 | } 307 | }.bind(this), $error); 308 | }.bind(this)); 309 | } 310 | }; 311 | } 312 | // 创建 commit 和 dispatch instance 313 | function createHelpers(actions, mutationsObj, emitter, getInstance) { 314 | var mutations = (0, _assign2.default)({}, mutationsObj, innerMutation); 315 | return { 316 | commitGlobal: commitGlobal.bind(this), 317 | dispatchGlobal: dispatchGlobal.bind(this), 318 | commit: function commit(type, payload, innerHelper) { 319 | if (!type) { 320 | throw new Error('not found ' + type + ' action'); 321 | } 322 | if ((0, _is.isObject)(type)) { 323 | payload = type; 324 | type = 'update'; 325 | } 326 | if ((0, _is.isString)(type) && startsWith(type, '$global:')) { 327 | var realType = type.split(':').pop(); 328 | return commitGlobal.call(this, realType, payload); 329 | } 330 | var prevState = _extends({}, this.data); 331 | var finalMutation = mutationHandler(mutations[type], (0, _wrapDataInstance2.default)(this.data), payload, innerHelper); 332 | // 触发更新机制 333 | emitter.emitEvent('updateState', { state: finalMutation, mutation: { type: type, payload: payload }, prevState: prevState }); 334 | // commit 的结果是一个同步行为,返回值 335 | return this.data; 336 | }, 337 | dispatch: function dispatch(type, payload) { 338 | return new (_ExternalPromise())(function ($return, $error) { 339 | var actionCache, realType, actionFunc, self, res; 340 | 341 | actionCache = (0, _assign2.default)({}, actions, this); 342 | if (!type) { 343 | return $error(new Error('action type not found')); 344 | } 345 | if ((0, _is.isString)(type) && startsWith(type, '$global:')) { 346 | realType = type.split(':').pop(); 347 | return $return(dispatchGlobal.call(this, realType, payload)); 348 | } 349 | actionFunc = actionCache[type]; 350 | self = this; 351 | res = {}; 352 | return _ExternalPromise().resolve(dispatchActionPromise(emitter, { type: type, payload: payload })).then(function ($await_6) { 353 | try { 354 | res = $await_6; 355 | if (!actionFunc) { 356 | console.warn('not found action', type, actions); 357 | return $return(_ExternalPromise().resolve(res)); 358 | } 359 | return _ExternalPromise().resolve(actionFunc.call(self, { 360 | commit: this.commit.bind(self), 361 | dispatch: this.dispatch.bind(self), 362 | dispatchGlobal: dispatchGlobal.bind(self), 363 | commitGlobal: commitGlobal.bind(self), 364 | message: _global2.default.messageManager, 365 | put: function put(type) { 366 | var func = actionCache[type]; 367 | if (!func) { 368 | throw new Error('not found ' + type + ' action'); 369 | } 370 | if (func) { 371 | for (var _len3 = arguments.length, args = Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) { 372 | args[_key3 - 1] = arguments[_key3]; 373 | } 374 | 375 | func.apply(self, args); 376 | } 377 | }, 378 | get state() { 379 | return (0, _wrapDataInstance2.default)(self.data, self); 380 | }, 381 | get getters() { 382 | return (0, _wrapDataInstance2.default)(self.data.$getters, self); 383 | }, 384 | get global() { 385 | return (0, _wrapDataInstance2.default)(self.data.$global); 386 | }, 387 | getRef: function getRef(name) { 388 | return _global2.default.getComponentRef(name); 389 | }, 390 | getState: function getState(instanceName) { 391 | if (!instanceName) { 392 | return (0, _wrapDataInstance2.default)(self.data, self); 393 | } 394 | return _global2.default.getState(instanceName); 395 | }, 396 | select: function select(filter) { 397 | return filter((0, _wrapDataInstance2.default)(_extends({}, self.data))); 398 | } 399 | }, (0, _wrapDataInstance2.default)(payload))).then(function ($await_7) { 400 | try { 401 | res = $await_7; 402 | // 保证结果为一个 promise 403 | if (res instanceof _ExternalPromise()) { 404 | return $return(res); 405 | } 406 | return $return(_ExternalPromise().resolve(res)); 407 | } catch ($boundEx) { 408 | return $error($boundEx); 409 | } 410 | }.bind(this), $error); 411 | } catch ($boundEx) { 412 | return $error($boundEx); 413 | } 414 | }.bind(this), $error); 415 | }.bind(this)); 416 | } 417 | }; 418 | } --------------------------------------------------------------------------------