vuex.js 18 KB

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