vuex.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  1. /*!
  2. * Vuex v0.6.3
  3. * (c) 2016 Evan You
  4. * Released under the MIT License.
  5. */
  6. (function (global, factory) {
  7. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  8. typeof define === 'function' && define.amd ? define(factory) :
  9. (global.Vuex = factory());
  10. }(this, function () { '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.toConsumableArray = function (arr) {
  39. if (Array.isArray(arr)) {
  40. for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
  41. return arr2;
  42. } else {
  43. return Array.from(arr);
  44. }
  45. };
  46. babelHelpers;
  47. /**
  48. * Merge an array of objects into one.
  49. *
  50. * @param {Array<Object>} arr
  51. * @return {Object}
  52. */
  53. function mergeObjects(arr) {
  54. return arr.reduce(function (prev, obj) {
  55. Object.keys(obj).forEach(function (key) {
  56. var existing = prev[key];
  57. if (existing) {
  58. // allow multiple mutation objects to contain duplicate
  59. // handlers for the same mutation type
  60. if (Array.isArray(existing)) {
  61. existing.push(obj[key]);
  62. } else {
  63. prev[key] = [prev[key], obj[key]];
  64. }
  65. } else {
  66. prev[key] = obj[key];
  67. }
  68. });
  69. return prev;
  70. }, {});
  71. }
  72. /**
  73. * Deep clone an object. Faster than JSON.parse(JSON.stringify()).
  74. *
  75. * @param {*} obj
  76. * @return {*}
  77. */
  78. function deepClone(obj) {
  79. if (Array.isArray(obj)) {
  80. return obj.map(deepClone);
  81. } else if (obj && (typeof obj === 'undefined' ? 'undefined' : babelHelpers.typeof(obj)) === 'object') {
  82. var cloned = {};
  83. var keys = Object.keys(obj);
  84. for (var i = 0, l = keys.length; i < l; i++) {
  85. var key = keys[i];
  86. cloned[key] = deepClone(obj[key]);
  87. }
  88. return cloned;
  89. } else {
  90. return obj;
  91. }
  92. }
  93. /**
  94. * Hacks to get access to Vue internals.
  95. * Maybe we should expose these...
  96. */
  97. var Watcher = void 0;
  98. function getWatcher(vm) {
  99. if (!Watcher) {
  100. var unwatch = vm.$watch('__vuex__', function (a) {
  101. return a;
  102. });
  103. Watcher = vm._watchers[0].constructor;
  104. unwatch();
  105. }
  106. return Watcher;
  107. }
  108. var Dep = void 0;
  109. function getDep(vm) {
  110. if (!Dep) {
  111. Dep = vm._data.__ob__.dep.constructor;
  112. }
  113. return Dep;
  114. }
  115. var hook = typeof window !== 'undefined' && window.__VUE_DEVTOOLS_GLOBAL_HOOK__;
  116. var devtoolMiddleware = {
  117. onInit: function onInit(state, store) {
  118. if (!hook) return;
  119. hook.emit('vuex:init', store);
  120. hook.on('vuex:travel-to-state', function (targetState) {
  121. var currentState = store._vm._data;
  122. store._dispatching = true;
  123. Object.keys(targetState).forEach(function (key) {
  124. currentState[key] = targetState[key];
  125. });
  126. store._dispatching = false;
  127. });
  128. },
  129. onMutation: function onMutation(mutation, state) {
  130. if (!hook) return;
  131. hook.emit('vuex:mutation', mutation, state);
  132. }
  133. };
  134. function override (Vue) {
  135. // override init and inject vuex init procedure
  136. var _init = Vue.prototype._init;
  137. Vue.prototype._init = function () {
  138. var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
  139. options.init = options.init ? [vuexInit].concat(options.init) : vuexInit;
  140. _init.call(this, options);
  141. };
  142. /**
  143. * Vuex init hook, injected into each instances init hooks list.
  144. */
  145. function vuexInit() {
  146. var options = this.$options;
  147. var store = options.store;
  148. var vuex = options.vuex;
  149. // store injection
  150. if (store) {
  151. this.$store = store;
  152. } else if (options.parent && options.parent.$store) {
  153. this.$store = options.parent.$store;
  154. }
  155. // vuex option handling
  156. if (vuex) {
  157. if (!this.$store) {
  158. console.warn('[vuex] store not injected. make sure to ' + 'provide the store option in your root component.');
  159. }
  160. var state = vuex.state;
  161. var getters = vuex.getters;
  162. var actions = vuex.actions;
  163. // handle deprecated state option
  164. if (state && !getters) {
  165. console.warn('[vuex] vuex.state option will been deprecated in 1.0. ' + 'Use vuex.getters instead.');
  166. getters = state;
  167. }
  168. // getters
  169. if (getters) {
  170. options.computed = options.computed || {};
  171. for (var key in getters) {
  172. defineVuexGetter(this, key, getters[key]);
  173. }
  174. }
  175. // actions
  176. if (actions) {
  177. options.methods = options.methods || {};
  178. for (var _key in actions) {
  179. options.methods[_key] = makeBoundAction(this.$store, actions[_key], _key);
  180. }
  181. }
  182. }
  183. }
  184. /**
  185. * Setter for all getter properties.
  186. */
  187. function setter() {
  188. throw new Error('vuex getter properties are read-only.');
  189. }
  190. /**
  191. * Define a Vuex getter on an instance.
  192. *
  193. * @param {Vue} vm
  194. * @param {String} key
  195. * @param {Function} getter
  196. */
  197. function defineVuexGetter(vm, key, getter) {
  198. if (typeof getter !== 'function') {
  199. console.warn('[vuex] Getter bound to key \'vuex.getters.' + key + '\' is not a function.');
  200. } else {
  201. Object.defineProperty(vm, key, {
  202. enumerable: true,
  203. configurable: true,
  204. get: makeComputedGetter(vm.$store, getter),
  205. set: setter
  206. });
  207. }
  208. }
  209. /**
  210. * Make a computed getter, using the same caching mechanism of computed
  211. * properties. In addition, it is cached on the raw getter function using
  212. * the store's unique cache id. This makes the same getter shared
  213. * across all components use the same underlying watcher, and makes
  214. * the getter evaluated only once during every flush.
  215. *
  216. * @param {Store} store
  217. * @param {Function} getter
  218. */
  219. function makeComputedGetter(store, getter) {
  220. var id = store._getterCacheId;
  221. // cached
  222. if (getter[id]) {
  223. return getter[id];
  224. }
  225. var vm = store._vm;
  226. var Watcher = getWatcher(vm);
  227. var Dep = getDep(vm);
  228. var watcher = new Watcher(vm, function (state) {
  229. return getter(state);
  230. }, null, { lazy: true });
  231. var computedGetter = function computedGetter() {
  232. if (watcher.dirty) {
  233. watcher.evaluate();
  234. }
  235. if (Dep.target) {
  236. watcher.depend();
  237. }
  238. return watcher.value;
  239. };
  240. getter[id] = computedGetter;
  241. return computedGetter;
  242. }
  243. /**
  244. * Make a bound-to-store version of a raw action function.
  245. *
  246. * @param {Store} store
  247. * @param {Function} action
  248. * @param {String} key
  249. */
  250. function makeBoundAction(store, action, key) {
  251. if (typeof action !== 'function') {
  252. console.warn('[vuex] Action bound to key \'vuex.actions.' + key + '\' is not a function.');
  253. }
  254. return function vuexBoundAction() {
  255. for (var _len = arguments.length, args = Array(_len), _key2 = 0; _key2 < _len; _key2++) {
  256. args[_key2] = arguments[_key2];
  257. }
  258. return action.call.apply(action, [this, store].concat(args));
  259. };
  260. }
  261. // option merging
  262. var merge = Vue.config.optionMergeStrategies.computed;
  263. Vue.config.optionMergeStrategies.vuex = function (toVal, fromVal) {
  264. if (!toVal) return fromVal;
  265. if (!fromVal) return toVal;
  266. return {
  267. getters: merge(toVal.getters, fromVal.getters),
  268. state: merge(toVal.state, fromVal.state),
  269. actions: merge(toVal.actions, fromVal.actions)
  270. };
  271. };
  272. }
  273. var Vue = void 0;
  274. var uid = 0;
  275. var Store = function () {
  276. /**
  277. * @param {Object} options
  278. * - {Object} state
  279. * - {Object} actions
  280. * - {Object} mutations
  281. * - {Array} middlewares
  282. * - {Boolean} strict
  283. */
  284. function Store() {
  285. var _this = this;
  286. var _ref = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
  287. var _ref$state = _ref.state;
  288. var state = _ref$state === undefined ? {} : _ref$state;
  289. var _ref$mutations = _ref.mutations;
  290. var mutations = _ref$mutations === undefined ? {} : _ref$mutations;
  291. var _ref$modules = _ref.modules;
  292. var modules = _ref$modules === undefined ? {} : _ref$modules;
  293. var _ref$middlewares = _ref.middlewares;
  294. var middlewares = _ref$middlewares === undefined ? [] : _ref$middlewares;
  295. var _ref$strict = _ref.strict;
  296. var strict = _ref$strict === undefined ? false : _ref$strict;
  297. babelHelpers.classCallCheck(this, Store);
  298. this._getterCacheId = 'vuex_store_' + uid++;
  299. this._dispatching = false;
  300. this._rootMutations = this._mutations = mutations;
  301. this._modules = modules;
  302. // bind dispatch to self
  303. var dispatch = this.dispatch;
  304. this.dispatch = function () {
  305. for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
  306. args[_key] = arguments[_key];
  307. }
  308. dispatch.apply(_this, args);
  309. };
  310. // use a Vue instance to store the state tree
  311. // suppress warnings just in case the user has added
  312. // some funky global mixins
  313. if (!Vue) {
  314. throw new Error('[vuex] must call Vue.use(Vuex) before creating a store instance.');
  315. }
  316. var silent = Vue.config.silent;
  317. Vue.config.silent = true;
  318. this._vm = new Vue({
  319. data: state
  320. });
  321. Vue.config.silent = silent;
  322. this._setupModuleState(state, modules);
  323. this._setupModuleMutations(modules);
  324. this._setupMiddlewares(middlewares, state);
  325. // add extra warnings in strict mode
  326. if (strict) {
  327. this._setupMutationCheck();
  328. }
  329. }
  330. /**
  331. * Getter for the entire state tree.
  332. * Read only.
  333. *
  334. * @return {Object}
  335. */
  336. babelHelpers.createClass(Store, [{
  337. key: 'dispatch',
  338. /**
  339. * Dispatch an action.
  340. *
  341. * @param {String} type
  342. */
  343. value: function dispatch(type) {
  344. for (var _len2 = arguments.length, payload = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
  345. payload[_key2 - 1] = arguments[_key2];
  346. }
  347. var silent = false;
  348. // compatibility for object actions, e.g. FSA
  349. if ((typeof type === 'undefined' ? 'undefined' : babelHelpers.typeof(type)) === 'object' && type.type && arguments.length === 1) {
  350. payload = [type.payload];
  351. if (type.silent) silent = true;
  352. type = type.type;
  353. }
  354. var mutation = this._mutations[type];
  355. var state = this.state;
  356. if (mutation) {
  357. this._dispatching = true;
  358. // apply the mutation
  359. if (Array.isArray(mutation)) {
  360. mutation.forEach(function (m) {
  361. return m.apply(undefined, [state].concat(babelHelpers.toConsumableArray(payload)));
  362. });
  363. } else {
  364. mutation.apply(undefined, [state].concat(babelHelpers.toConsumableArray(payload)));
  365. }
  366. this._dispatching = false;
  367. if (!silent) this._applyMiddlewares(type, payload);
  368. } else {
  369. console.warn('[vuex] Unknown mutation: ' + type);
  370. }
  371. }
  372. /**
  373. * Watch state changes on the store.
  374. * Same API as Vue's $watch, except when watching a function,
  375. * the function gets the state as the first argument.
  376. *
  377. * @param {String|Function} expOrFn
  378. * @param {Function} cb
  379. * @param {Object} [options]
  380. */
  381. }, {
  382. key: 'watch',
  383. value: function watch(expOrFn, cb, options) {
  384. var _this2 = this;
  385. return this._vm.$watch(function () {
  386. return typeof expOrFn === 'function' ? expOrFn(_this2.state) : _this2._vm.$get(expOrFn);
  387. }, cb, options);
  388. }
  389. /**
  390. * Hot update mutations & modules.
  391. *
  392. * @param {Object} options
  393. * - {Object} [mutations]
  394. * - {Object} [modules]
  395. */
  396. }, {
  397. key: 'hotUpdate',
  398. value: function hotUpdate() {
  399. var _ref2 = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
  400. var mutations = _ref2.mutations;
  401. var modules = _ref2.modules;
  402. this._rootMutations = this._mutations = mutations || this._rootMutations;
  403. this._setupModuleMutations(modules || this._modules);
  404. }
  405. /**
  406. * Attach sub state tree of each module to the root tree.
  407. *
  408. * @param {Object} state
  409. * @param {Object} modules
  410. */
  411. }, {
  412. key: '_setupModuleState',
  413. value: function _setupModuleState(state, modules) {
  414. Object.keys(modules).forEach(function (key) {
  415. Vue.set(state, key, modules[key].state || {});
  416. });
  417. }
  418. /**
  419. * Bind mutations for each module to its sub tree and
  420. * merge them all into one final mutations map.
  421. *
  422. * @param {Object} updatedModules
  423. */
  424. }, {
  425. key: '_setupModuleMutations',
  426. value: function _setupModuleMutations(updatedModules) {
  427. var modules = this._modules;
  428. var allMutations = [this._rootMutations];
  429. Object.keys(updatedModules).forEach(function (key) {
  430. modules[key] = updatedModules[key];
  431. });
  432. Object.keys(modules).forEach(function (key) {
  433. var module = modules[key];
  434. if (!module || !module.mutations) return;
  435. // bind mutations to sub state tree
  436. var mutations = {};
  437. Object.keys(module.mutations).forEach(function (name) {
  438. var original = module.mutations[name];
  439. mutations[name] = function (state) {
  440. for (var _len3 = arguments.length, args = Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
  441. args[_key3 - 1] = arguments[_key3];
  442. }
  443. original.apply(undefined, [state[key]].concat(args));
  444. };
  445. });
  446. allMutations.push(mutations);
  447. });
  448. this._mutations = mergeObjects(allMutations);
  449. }
  450. /**
  451. * Setup mutation check: if the vuex instance's state is mutated
  452. * outside of a mutation handler, we throw en error. This effectively
  453. * enforces all mutations to the state to be trackable and hot-reloadble.
  454. * However, this comes at a run time cost since we are doing a deep
  455. * watch on the entire state tree, so it is only enalbed with the
  456. * strict option is set to true.
  457. */
  458. }, {
  459. key: '_setupMutationCheck',
  460. value: function _setupMutationCheck() {
  461. var _this3 = this;
  462. var Watcher = getWatcher(this._vm);
  463. /* eslint-disable no-new */
  464. new Watcher(this._vm, '$data', function () {
  465. if (!_this3._dispatching) {
  466. throw new Error('[vuex] Do not mutate vuex store state outside mutation handlers.');
  467. }
  468. }, { deep: true, sync: true });
  469. /* eslint-enable no-new */
  470. }
  471. /**
  472. * Setup the middlewares. The devtools middleware is always
  473. * included, since it does nothing if no devtool is detected.
  474. *
  475. * A middleware can demand the state it receives to be
  476. * "snapshots", i.e. deep clones of the actual state tree.
  477. *
  478. * @param {Array} middlewares
  479. * @param {Object} state
  480. */
  481. }, {
  482. key: '_setupMiddlewares',
  483. value: function _setupMiddlewares(middlewares, state) {
  484. var _this4 = this;
  485. this._middlewares = [devtoolMiddleware].concat(middlewares);
  486. this._needSnapshots = middlewares.some(function (m) {
  487. return m.snapshot;
  488. });
  489. if (this._needSnapshots) {
  490. console.log('[vuex] One or more of your middlewares are taking state snapshots ' + 'for each mutation. Make sure to use them only during development.');
  491. }
  492. var initialSnapshot = this._prevSnapshot = this._needSnapshots ? deepClone(state) : null;
  493. // call init hooks
  494. this._middlewares.forEach(function (m) {
  495. if (m.onInit) {
  496. m.onInit(m.snapshot ? initialSnapshot : state, _this4);
  497. }
  498. });
  499. }
  500. /**
  501. * Apply the middlewares on a given mutation.
  502. *
  503. * @param {String} type
  504. * @param {Array} payload
  505. */
  506. }, {
  507. key: '_applyMiddlewares',
  508. value: function _applyMiddlewares(type, payload) {
  509. var _this5 = this;
  510. var state = this.state;
  511. var prevSnapshot = this._prevSnapshot;
  512. var snapshot = void 0,
  513. clonedPayload = void 0;
  514. if (this._needSnapshots) {
  515. snapshot = this._prevSnapshot = deepClone(state);
  516. clonedPayload = deepClone(payload);
  517. }
  518. this._middlewares.forEach(function (m) {
  519. if (m.onMutation) {
  520. if (m.snapshot) {
  521. m.onMutation({ type: type, payload: clonedPayload }, snapshot, prevSnapshot, _this5);
  522. } else {
  523. m.onMutation({ type: type, payload: payload }, state, _this5);
  524. }
  525. }
  526. });
  527. }
  528. }, {
  529. key: 'state',
  530. get: function get() {
  531. return this._vm._data;
  532. },
  533. set: function set(v) {
  534. throw new Error('[vuex] Vuex root state is read only.');
  535. }
  536. }]);
  537. return Store;
  538. }();
  539. function install(_Vue) {
  540. if (Vue) {
  541. console.warn('[vuex] already installed. Vue.use(Vuex) should be called only once.');
  542. return;
  543. }
  544. Vue = _Vue;
  545. override(Vue);
  546. }
  547. // auto install in dist mode
  548. if (typeof window !== 'undefined' && window.Vue) {
  549. install(window.Vue);
  550. }
  551. function createLogger() {
  552. console.warn('[vuex] Vuex.createLogger has been deprecated.' + 'Use `import createLogger from \'vuex/logger\' instead.');
  553. }
  554. var index = {
  555. Store: Store,
  556. install: install,
  557. createLogger: createLogger
  558. };
  559. return index;
  560. }));