vuex.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. /*!
  2. * Vuex v0.4.1
  3. * (c) 2016 Evan You
  4. * Released under the MIT License.
  5. */
  6. (function (global, factory) {
  7. typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
  8. typeof define === 'function' && define.amd ? define(['exports'], factory) :
  9. (factory((global.Vuex = global.Vuex || {})));
  10. }(this, function (exports) { 'use strict';
  11. var babelHelpers = {};
  12. babelHelpers.typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
  13. return typeof obj;
  14. } : function (obj) {
  15. return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj;
  16. };
  17. babelHelpers.classCallCheck = function (instance, Constructor) {
  18. if (!(instance instanceof Constructor)) {
  19. throw new TypeError("Cannot call a class as a function");
  20. }
  21. };
  22. babelHelpers.createClass = function () {
  23. function defineProperties(target, props) {
  24. for (var i = 0; i < props.length; i++) {
  25. var descriptor = props[i];
  26. descriptor.enumerable = descriptor.enumerable || false;
  27. descriptor.configurable = true;
  28. if ("value" in descriptor) descriptor.writable = true;
  29. Object.defineProperty(target, descriptor.key, descriptor);
  30. }
  31. }
  32. return function (Constructor, protoProps, staticProps) {
  33. if (protoProps) defineProperties(Constructor.prototype, protoProps);
  34. if (staticProps) defineProperties(Constructor, staticProps);
  35. return Constructor;
  36. };
  37. }();
  38. babelHelpers;
  39. /**
  40. * Merge an array of objects into one.
  41. *
  42. * @param {Array<Object>} arr
  43. * @return {Object}
  44. */
  45. function mergeObjects(arr) {
  46. return arr.reduce(function (prev, obj) {
  47. Object.keys(obj).forEach(function (key) {
  48. var existing = prev[key];
  49. if (existing) {
  50. // allow multiple mutation objects to contain duplicate
  51. // handlers for the same mutation type
  52. if (Array.isArray(existing)) {
  53. existing.push(obj[key]);
  54. } else {
  55. prev[key] = [prev[key], obj[key]];
  56. }
  57. } else {
  58. prev[key] = obj[key];
  59. }
  60. });
  61. return prev;
  62. }, {});
  63. }
  64. /**
  65. * Deep clone an object. Faster than JSON.parse(JSON.stringify()).
  66. *
  67. * @param {*} obj
  68. * @return {*}
  69. */
  70. function deepClone(obj) {
  71. if (Array.isArray(obj)) {
  72. return obj.map(deepClone);
  73. } else if (obj && (typeof obj === 'undefined' ? 'undefined' : babelHelpers.typeof(obj)) === 'object') {
  74. var cloned = {};
  75. var keys = Object.keys(obj);
  76. for (var i = 0, l = keys.length; i < l; i++) {
  77. var key = keys[i];
  78. cloned[key] = deepClone(obj[key]);
  79. }
  80. return cloned;
  81. } else {
  82. return obj;
  83. }
  84. }
  85. var devtoolMiddleware = {
  86. onInit: function onInit(state) {
  87. // TODO
  88. },
  89. onMutation: function onMutation(mutation, state) {
  90. // TODO
  91. }
  92. };
  93. // Credits: borrowed code from fcomb/redux-logger
  94. function createLogger() {
  95. var _ref = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
  96. var _ref$collapsed = _ref.collapsed;
  97. var collapsed = _ref$collapsed === undefined ? true : _ref$collapsed;
  98. var _ref$transformer = _ref.transformer;
  99. var transformer = _ref$transformer === undefined ? function (state) {
  100. return state;
  101. } : _ref$transformer;
  102. var _ref$mutationTransfor = _ref.mutationTransformer;
  103. var mutationTransformer = _ref$mutationTransfor === undefined ? function (mut) {
  104. return mut;
  105. } : _ref$mutationTransfor;
  106. return {
  107. snapshot: true,
  108. onMutation: function onMutation(mutation, nextState, prevState) {
  109. if (typeof console === 'undefined') {
  110. return;
  111. }
  112. var time = new Date();
  113. var formattedTime = ' @ ' + pad(time.getHours(), 2) + ':' + pad(time.getMinutes(), 2) + ':' + pad(time.getSeconds(), 2) + '.' + pad(time.getMilliseconds(), 3);
  114. var formattedMutation = mutationTransformer(mutation);
  115. var message = 'mutation ' + mutation.type + formattedTime;
  116. var startMessage = collapsed ? console.groupCollapsed : console.group;
  117. // render
  118. try {
  119. startMessage.call(console, message);
  120. } catch (e) {
  121. console.log(message);
  122. }
  123. console.log('%c prev state', 'color: #9E9E9E; font-weight: bold', prevState);
  124. console.log('%c mutation', 'color: #03A9F4; font-weight: bold', formattedMutation);
  125. console.log('%c next state', 'color: #4CAF50; font-weight: bold', nextState);
  126. try {
  127. console.groupEnd();
  128. } catch (e) {
  129. console.log('—— log end ——');
  130. }
  131. }
  132. };
  133. }
  134. function repeat(str, times) {
  135. return new Array(times + 1).join(str);
  136. }
  137. function pad(num, maxLength) {
  138. return repeat('0', maxLength - num.toString().length) + num;
  139. }
  140. // export install function
  141. function override (Vue) {
  142. var _init = Vue.prototype._init;
  143. Vue.prototype._init = function (options) {
  144. var _this = this;
  145. options = options || {};
  146. var componentOptions = this.constructor.options;
  147. // store injection
  148. var store = options.store || componentOptions.store;
  149. if (store) {
  150. this.$store = store;
  151. } else if (options.parent && options.parent.$store) {
  152. this.$store = options.parent.$store;
  153. }
  154. // vuex option handling
  155. var vuex = options.vuex || componentOptions.vuex;
  156. if (vuex) {
  157. (function () {
  158. if (!_this.$store) {
  159. console.warn('[vuex] store not injected. make sure to ' + 'provide the store option in your root component.');
  160. }
  161. var state = vuex.state;
  162. var actions = vuex.actions;
  163. // state
  164. if (state) {
  165. options.computed = options.computed || {};
  166. Object.keys(state).forEach(function (key) {
  167. options.computed[key] = function vuexBoundGetter() {
  168. return state[key].call(this, this.$store.state);
  169. };
  170. });
  171. }
  172. // actions
  173. if (actions) {
  174. options.methods = options.methods || {};
  175. Object.keys(actions).forEach(function (key) {
  176. options.methods[key] = function vuexBoundAction() {
  177. var _actions$key;
  178. for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
  179. args[_key] = arguments[_key];
  180. }
  181. return (_actions$key = actions[key]).call.apply(_actions$key, [this, this.$store].concat(args));
  182. };
  183. });
  184. }
  185. })();
  186. }
  187. _init.call(this, options);
  188. };
  189. }
  190. var Vue = undefined;
  191. var Store = function () {
  192. /**
  193. * @param {Object} options
  194. * - {Object} state
  195. * - {Object} actions
  196. * - {Object} mutations
  197. * - {Array} middlewares
  198. * - {Boolean} strict
  199. */
  200. function Store() {
  201. var _this = this;
  202. var _ref = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
  203. var _ref$state = _ref.state;
  204. var state = _ref$state === undefined ? {} : _ref$state;
  205. var _ref$mutations = _ref.mutations;
  206. var mutations = _ref$mutations === undefined ? {} : _ref$mutations;
  207. var _ref$modules = _ref.modules;
  208. var modules = _ref$modules === undefined ? {} : _ref$modules;
  209. var _ref$middlewares = _ref.middlewares;
  210. var middlewares = _ref$middlewares === undefined ? [] : _ref$middlewares;
  211. var _ref$strict = _ref.strict;
  212. var strict = _ref$strict === undefined ? false : _ref$strict;
  213. babelHelpers.classCallCheck(this, Store);
  214. this._dispatching = false;
  215. this._rootMutations = this._mutations = mutations;
  216. this._modules = modules;
  217. // bind dispatch to self
  218. var dispatch = this.dispatch;
  219. this.dispatch = function () {
  220. for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
  221. args[_key] = arguments[_key];
  222. }
  223. dispatch.apply(_this, args);
  224. };
  225. // use a Vue instance to store the state tree
  226. this._vm = new Vue({
  227. data: state
  228. });
  229. this._setupModuleState(state, modules);
  230. this._setupModuleMutations(modules);
  231. this._setupMiddlewares(middlewares, state);
  232. // add extra warnings in strict mode
  233. if (strict) {
  234. this._setupMutationCheck();
  235. }
  236. }
  237. /**
  238. * Getter for the entire state tree.
  239. * Read only.
  240. *
  241. * @return {Object}
  242. */
  243. babelHelpers.createClass(Store, [{
  244. key: 'dispatch',
  245. /**
  246. * Dispatch an action.
  247. *
  248. * @param {String} type
  249. */
  250. value: function dispatch(type) {
  251. for (var _len2 = arguments.length, payload = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
  252. payload[_key2 - 1] = arguments[_key2];
  253. }
  254. var mutation = this._mutations[type];
  255. var prevSnapshot = this._prevSnapshot;
  256. var state = this.state;
  257. var snapshot = undefined,
  258. clonedPayload = undefined;
  259. if (mutation) {
  260. this._dispatching = true;
  261. // apply the mutation
  262. if (Array.isArray(mutation)) {
  263. mutation.forEach(function (m) {
  264. return m.apply(undefined, [state].concat(payload));
  265. });
  266. } else {
  267. mutation.apply(undefined, [state].concat(payload));
  268. }
  269. this._dispatching = false;
  270. // invoke middlewares
  271. if (this._needSnapshots) {
  272. snapshot = this._prevSnapshot = deepClone(state);
  273. clonedPayload = deepClone(payload);
  274. }
  275. this._middlewares.forEach(function (m) {
  276. if (m.onMutation) {
  277. if (m.snapshot) {
  278. m.onMutation({ type: type, payload: clonedPayload }, snapshot, prevSnapshot);
  279. } else {
  280. m.onMutation({ type: type, payload: payload }, state);
  281. }
  282. }
  283. });
  284. } else {
  285. console.warn('[vuex] Unknown mutation: ' + type);
  286. }
  287. }
  288. /**
  289. * Watch state changes on the store.
  290. * Same API as Vue's $watch, except when watching a function,
  291. * the function gets the state as the first argument.
  292. *
  293. * @param {String|Function} expOrFn
  294. * @param {Function} cb
  295. * @param {Object} [options]
  296. */
  297. }, {
  298. key: 'watch',
  299. value: function watch(expOrFn, cb, options) {
  300. var _this2 = this;
  301. return this._vm.$watch(function () {
  302. return typeof expOrFn === 'function' ? expOrFn(_this2.state) : _this2._vm.$get(expOrFn);
  303. }, cb, options);
  304. }
  305. /**
  306. * Hot update actions and mutations.
  307. *
  308. * @param {Object} options
  309. * - {Object} [mutations]
  310. * - {Object} [modules]
  311. */
  312. }, {
  313. key: 'hotUpdate',
  314. value: function hotUpdate() {
  315. var _ref2 = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
  316. var mutations = _ref2.mutations;
  317. var modules = _ref2.modules;
  318. this._rootMutations = this._mutations = mutations || this._rootMutations;
  319. this._setupModuleMutations(modules || this._modules);
  320. }
  321. /**
  322. * Attach sub state tree of each module to the root tree.
  323. *
  324. * @param {Object} state
  325. * @param {Object} modules
  326. */
  327. }, {
  328. key: '_setupModuleState',
  329. value: function _setupModuleState(state, modules) {
  330. var setPath = Vue.parsers.path.setPath;
  331. Object.keys(modules).forEach(function (key) {
  332. setPath(state, key, modules[key].state);
  333. });
  334. }
  335. /**
  336. * Bind mutations for each module to its sub tree and
  337. * merge them all into one final mutations map.
  338. *
  339. * @param {Object} modules
  340. */
  341. }, {
  342. key: '_setupModuleMutations',
  343. value: function _setupModuleMutations(modules) {
  344. this._modules = modules;
  345. var getPath = Vue.parsers.path.getPath;
  346. var allMutations = [this._rootMutations];
  347. Object.keys(modules).forEach(function (key) {
  348. var module = modules[key];
  349. // bind mutations to sub state tree
  350. var mutations = {};
  351. Object.keys(module.mutations).forEach(function (name) {
  352. var original = module.mutations[name];
  353. mutations[name] = function (state) {
  354. for (var _len3 = arguments.length, args = Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
  355. args[_key3 - 1] = arguments[_key3];
  356. }
  357. original.apply(undefined, [getPath(state, key)].concat(args));
  358. };
  359. });
  360. allMutations.push(mutations);
  361. });
  362. this._mutations = mergeObjects(allMutations);
  363. }
  364. /**
  365. * Setup mutation check: if the vuex instance's state is mutated
  366. * outside of a mutation handler, we throw en error. This effectively
  367. * enforces all mutations to the state to be trackable and hot-reloadble.
  368. * However, this comes at a run time cost since we are doing a deep
  369. * watch on the entire state tree, so it is only enalbed with the
  370. * strict option is set to true.
  371. */
  372. }, {
  373. key: '_setupMutationCheck',
  374. value: function _setupMutationCheck() {
  375. var _this3 = this;
  376. // a hack to get the watcher constructor from older versions of Vue
  377. // mainly because the public $watch method does not allow sync
  378. // watchers.
  379. var unwatch = this._vm.$watch('__vuex__', function (a) {
  380. return a;
  381. });
  382. var Watcher = this._vm._watchers[0].constructor;
  383. unwatch();
  384. /* eslint-disable no-new */
  385. new Watcher(this._vm, '$data', function () {
  386. if (!_this3._dispatching) {
  387. throw new Error('[vuex] Do not mutate vuex store state outside mutation handlers.');
  388. }
  389. }, { deep: true, sync: true });
  390. /* eslint-enable no-new */
  391. }
  392. /**
  393. * Setup the middlewares. The devtools middleware is always
  394. * included, since it does nothing if no devtool is detected.
  395. *
  396. * A middleware can demand the state it receives to be
  397. * "snapshots", i.e. deep clones of the actual state tree.
  398. *
  399. * @param {Array} middlewares
  400. * @param {Object} state
  401. */
  402. }, {
  403. key: '_setupMiddlewares',
  404. value: function _setupMiddlewares(middlewares, state) {
  405. this._middlewares = [devtoolMiddleware].concat(middlewares);
  406. this._needSnapshots = middlewares.some(function (m) {
  407. return m.snapshot;
  408. });
  409. if (this._needSnapshots) {
  410. console.log('[vuex] One or more of your middlewares are taking state snapshots ' + 'for each mutation. Make sure to use them only during development.');
  411. }
  412. var initialSnapshot = this._prevSnapshot = this._needSnapshots ? deepClone(state) : null;
  413. // call init hooks
  414. this._middlewares.forEach(function (m) {
  415. if (m.onInit) {
  416. m.onInit(m.snapshot ? initialSnapshot : state);
  417. }
  418. });
  419. }
  420. }, {
  421. key: 'state',
  422. get: function get() {
  423. return this._vm._data;
  424. },
  425. set: function set(v) {
  426. throw new Error('[vuex] Vuex root state is read only.');
  427. }
  428. }]);
  429. return Store;
  430. }();
  431. function install(_Vue) {
  432. Vue = _Vue;
  433. override(Vue);
  434. }
  435. // also export the default
  436. var index = {
  437. Store: Store,
  438. install: install,
  439. createLogger: createLogger
  440. };
  441. exports.Store = Store;
  442. exports.install = install;
  443. exports.createLogger = createLogger;
  444. exports['default'] = index;
  445. }));