zus
Version:
> a lightweight front-end framework.
342 lines (276 loc) • 11 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = create;
var _redux = require("redux");
var _reduxSaga = _interopRequireDefault(require("redux-saga"));
var _logTips = _interopRequireWildcard(require("log-tips"));
var _checkModel = _interopRequireDefault(require("./checkModel"));
var _prefixNamespace = _interopRequireDefault(require("./prefixNamespace"));
var _Plugin = _interopRequireWildcard(require("./Plugin"));
var _createStore = _interopRequireDefault(require("./createStore"));
var _getSaga = _interopRequireDefault(require("./getSaga"));
var _getReducer = _interopRequireDefault(require("./getReducer"));
var _createPromiseMiddleware = _interopRequireDefault(require("./createPromiseMiddleware"));
var _subscription = require("./subscription");
var _utils = require("./utils");
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj["default"] = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
// Internal model to update global state when do unmodel
var zusModel = {
namespace: '@zus',
state: 0,
reducers: {
UPDATE: function UPDATE(state) {
return state + 1;
}
}
/**
* Create zus-core instance.
*
* @param hooksAndOpts
* @param createOpts
*/
};
function create() {
var hooksAndOpts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var createOpts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
// 解析了createOpts => set upApp
var _createOpts$setupApp = createOpts.setupApp,
setupApp = _createOpts$setupApp === void 0 ? _utils.noop : _createOpts$setupApp;
var plugin = new _Plugin["default"]();
plugin.use((0, _Plugin.filterHooks)(hooksAndOpts)); // 在定义的hooks中加入全局的配置
var app = {
_models: [(0, _prefixNamespace["default"])(_objectSpread({}, zusModel))],
_store: null,
_plugin: plugin,
use: plugin.use.bind(plugin),
model: model,
start: start
};
return app;
/**
* Register model before app is started.
*
* @param m {Object} model to register
*/
function model(m) {
if (process.env.NODE_ENV !== 'production') {
(0, _checkModel["default"])(m, app._models);
}
var prefixedModel = (0, _prefixNamespace["default"])(_objectSpread({}, m));
app._models.push(prefixedModel);
return prefixedModel;
}
/**
* Inject model after app is started.
*
* @param createReducer
* @param onError
* @param unlisteners
* @param m
*/
function injectModel(createReducer, onError, unlisteners, m) {
m = model(m);
var store = app._store;
store.asyncReducers[m.namespace] = (0, _getReducer["default"])(m.reducers, m.state, plugin._handleActions);
store.replaceReducer(createReducer());
if (m.effects) {
store.runSaga(app._getSaga(m.effects, m, onError, plugin.get('onEffect')));
}
if (m.subscriptions) {
unlisteners[m.namespace] = (0, _subscription.run)(m.subscriptions, m, app, onError);
}
}
/**
* Unregister model.
*
* @param createReducer
* @param reducers
* @param unlisteners
* @param namespace
*
* Unexpected key warn problem:
* https://github.com/reactjs/redux/issues/1636
*/
function unmodel(createReducer, reducers, unlisteners, namespace) {
var store = app._store; // Delete reducers
delete store.asyncReducers[namespace];
delete reducers[namespace];
store.replaceReducer(createReducer());
store.dispatch({
type: '@@zus/UPDATE'
}); // Cancel effects
store.dispatch({
type: "".concat(namespace, "/@@CANCEL_EFFECTS")
}); // Unlisten subscrioptions
(0, _subscription.unlisten)(unlisteners, namespace); // Delete model from app._models
app._models = app._models.filter(function (_model) {
return _model.namespace !== namespace;
});
}
/**
* Replace a model if it exsits, if not, add it to app
* Attention:
* - Only available after zus.start gets called
* - Will not check origin m is strict equal to the new one
* Useful for HMR
* @param createReducer
* @param reducers
* @param unlisteners
* @param onError
* @param m
*/
function replaceModel(createReducer, reducers, unlisteners, onError, m) {
var store = app._store;
var namespace = m.namespace;
var oldModelIdx = (0, _utils.findIndex)(app._models, function (model) {
return model.namespace === namespace;
});
if (oldModelIdx !== -1) {
// Cancel effects
store.dispatch({
type: "".concat(namespace, "/@@CANCEL_EFFECTS")
}); // Delete reducers
delete store.asyncReducers[namespace];
delete reducers[namespace]; // Unlisten subscrioptions
(0, _subscription.unlisten)(unlisteners, namespace); // Delete model from app._models
app._models.splice(oldModelIdx, 1);
} // add new version model to store
app.model(m);
store.dispatch({
type: '@@zus/UPDATE'
});
}
/**
* Start the app.
*
* @returns void
*/
function start() {
var onError = function onError(err, extension) {
if (err) {
if (typeof err === 'string') err = new Error(err);
err.preventDefault = function () {
err._dontReject = true;
};
plugin.apply('onError', function (_err) {
throw new Error(_err.stack || _err);
})(err, app._store.dispatch, extension);
}
};
var sagaMiddleware = (0, _reduxSaga["default"])(); // saga 中间件
var promiseMiddleware = (0, _createPromiseMiddleware["default"])(app); //
app._getSaga = _getSaga["default"].bind(null);
var sagas = [];
var reducers = {};
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = app._models[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var m = _step.value;
reducers[m.namespace] = (0, _getReducer["default"])(m.reducers, m.state, plugin._handleActions);
if (m.effects) sagas.push(app._getSaga(m.effects, m, onError, plugin.get('onEffect')));
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator["return"] != null) {
_iterator["return"]();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
var reducerEnhancer = plugin.get('onReducer');
var extraReducers = plugin.get('extraReducers');
(0, _logTips["default"])(Object.keys(extraReducers).every(function (key) {
return !(key in reducers);
}), "[app.start] extraReducers is conflict with other reducers, reducers list: ".concat(Object.keys(reducers).join(', '))); // Create store
var store = (0, _createStore["default"])({
reducers: createReducer(),
initialState: hooksAndOpts.initialState || {},
plugin: plugin,
createOpts: createOpts,
sagaMiddleware: sagaMiddleware,
promiseMiddleware: promiseMiddleware
});
app._store = store; // Extend store
store.runSaga = sagaMiddleware.run;
store.asyncReducers = {}; // Execute listeners when state is changed
var listeners = plugin.get('onStateChange');
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
var _loop = function _loop() {
var listener = _step2.value;
store.subscribe(function () {
listener(store.getState());
});
};
for (var _iterator2 = listeners[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
_loop();
} // Run sagas
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2["return"] != null) {
_iterator2["return"]();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
sagas.forEach(sagaMiddleware.run); // Setup app => 加入路由改变监听
setupApp(app); // Run subscriptions
var unlisteners = {};
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = this._models[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var _model2 = _step3.value;
if (_model2.subscriptions) {
unlisteners[_model2.namespace] = (0, _subscription.run)(_model2.subscriptions, _model2, app, onError);
}
} // Setup app.model and app.unmodel
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3["return"] != null) {
_iterator3["return"]();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
app.model = injectModel.bind(app, createReducer, onError, unlisteners);
app.unmodel = unmodel.bind(app, createReducer, reducers, unlisteners);
app.replaceModel = replaceModel.bind(app, createReducer, reducers, unlisteners, onError);
/**
* Create global reducer for redux.
*
* @returns {Object}
*/
function createReducer() {
return reducerEnhancer((0, _redux.combineReducers)(_objectSpread({}, reducers, extraReducers, app._store ? app._store.asyncReducers : {})));
}
}
}