123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- import { mergeObjects, deepClone } from './util'
- import devtoolMiddleware from './middlewares/devtool'
- import createLogger from './middlewares/logger'
- import override from './override'
- let Vue
- export class Store {
- /**
- * @param {Object} options
- * - {Object} state
- * - {Object} actions
- * - {Object} mutations
- * - {Array} middlewares
- * - {Boolean} strict
- */
- constructor ({
- state = {},
- mutations = {},
- modules = {},
- middlewares = [],
- strict = false
- } = {}) {
- this._dispatching = false
- this._rootMutations = this._mutations = mutations
- this._modules = modules
- // bind dispatch to self
- const dispatch = this.dispatch
- this.dispatch = (...args) => {
- dispatch.apply(this, args)
- }
- // use a Vue instance to store the state tree
- this._vm = new Vue({
- data: state
- })
- this._setupModuleState(state, modules)
- this._setupModuleMutations(modules)
- this._setupMiddlewares(middlewares, state)
- // add extra warnings in strict mode
- if (strict) {
- this._setupMutationCheck()
- }
- }
- /**
- * Getter for the entire state tree.
- * Read only.
- *
- * @return {Object}
- */
- get state () {
- return this._vm._data
- }
- set state (v) {
- throw new Error('[vuex] Vuex root state is read only.')
- }
- /**
- * Dispatch an action.
- *
- * @param {String} type
- */
- dispatch (type, ...payload) {
- const mutation = this._mutations[type]
- const prevSnapshot = this._prevSnapshot
- const state = this.state
- let snapshot, clonedPayload
- if (mutation) {
- this._dispatching = true
- // apply the mutation
- if (Array.isArray(mutation)) {
- mutation.forEach(m => m(state, ...payload))
- } else {
- mutation(state, ...payload)
- }
- this._dispatching = false
- // invoke middlewares
- if (this._needSnapshots) {
- snapshot = this._prevSnapshot = deepClone(state)
- clonedPayload = deepClone(payload)
- }
- this._middlewares.forEach(m => {
- if (m.onMutation) {
- if (m.snapshot) {
- m.onMutation({ type, payload: clonedPayload }, snapshot, prevSnapshot)
- } else {
- m.onMutation({ type, payload }, state)
- }
- }
- })
- } else {
- console.warn(`[vuex] Unknown mutation: ${ type }`)
- }
- }
- /**
- * Hot update actions and mutations.
- *
- * @param {Object} options
- * - {Object} [mutations]
- * - {Object} [modules]
- */
- hotUpdate ({ mutations, modules } = {}) {
- this._rootMutations = this._mutations = mutations || this._rootMutations
- this._setupModuleMutations(modules || this._modules)
- }
- /**
- * Attach sub state tree of each module to the root tree.
- *
- * @param {Object} state
- * @param {Object} modules
- */
- _setupModuleState (state, modules) {
- const { setPath } = Vue.parsers.path
- Object.keys(modules).forEach(key => {
- setPath(state, key, modules[key].state)
- })
- }
- /**
- * Bind mutations for each module to its sub tree and
- * merge them all into one final mutations map.
- *
- * @param {Object} modules
- */
- _setupModuleMutations (modules) {
- this._modules = modules
- const { getPath } = Vue.parsers.path
- const allMutations = [this._rootMutations]
- Object.keys(modules).forEach(key => {
- const module = modules[key]
- // bind mutations to sub state tree
- const mutations = {}
- Object.keys(module.mutations).forEach(name => {
- const original = module.mutations[name]
- mutations[name] = (state, ...args) => {
- original(getPath(state, key), ...args)
- }
- })
- allMutations.push(mutations)
- })
- this._mutations = mergeObjects(allMutations)
- }
- /**
- * Setup mutation check: if the vuex instance's state is mutated
- * outside of a mutation handler, we throw en error. This effectively
- * enforces all mutations to the state to be trackable and hot-reloadble.
- * However, this comes at a run time cost since we are doing a deep
- * watch on the entire state tree, so it is only enalbed with the
- * strict option is set to true.
- */
- _setupMutationCheck () {
- // a hack to get the watcher constructor from older versions of Vue
- // mainly because the public $watch method does not allow sync
- // watchers.
- const unwatch = this._vm.$watch('__vuex__', a => a)
- const Watcher = this._vm._watchers[0].constructor
- unwatch()
- new Watcher(this._vm, '$data', () => {
- if (!this._dispatching) {
- throw new Error(
- '[vuex] Do not mutate vuex store state outside mutation handlers.'
- )
- }
- }, { deep: true, sync: true })
- }
- /**
- * Setup the middlewares. The devtools middleware is always
- * included, since it does nothing if no devtool is detected.
- *
- * A middleware can demand the state it receives to be
- * "snapshots", i.e. deep clones of the actual state tree.
- *
- * @param {Array} middlewares
- * @param {Object} state
- */
- _setupMiddlewares (middlewares, state) {
- this._middlewares = [devtoolMiddleware].concat(middlewares)
- this._needSnapshots = middlewares.some(m => m.snapshot)
- if (this._needSnapshots) {
- console.log(
- '[vuex] One or more of your middlewares are taking state snapshots ' +
- 'for each mutation. Make sure to use them only during development.'
- )
- }
- const initialSnapshot = this._prevSnapshot = this._needSnapshots
- ? deepClone(state)
- : null
- // call init hooks
- this._middlewares.forEach(m => {
- if (m.onInit) {
- m.onInit(m.snapshot ? initialSnapshot : state)
- }
- })
- }
- }
- function install (_Vue) {
- Vue = _Vue
- override(Vue)
- }
- export {
- install,
- createLogger
- }
- // also export the default
- export default {
- Store,
- install,
- createLogger
- }
|