├── 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 | 
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 | }
--------------------------------------------------------------------------------