1
0

vuex.js 18 KB

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