# vue源码分析之vuex

## Vuex 初始化

安装

```js
export default {
    Store,
    install,
    version: '__VERSION__',
    mapState,
    mapMutations,
    mapGetters,
    mapActions,
    createNamespacedHelpers
}
```

```js
export function install(_Vue) {
    if (Vue && _Vue === Vue) {
        if (process.env.NODE_ENV !== 'production') {
            console.error(
                '[vuex] already installed. Vue.use(Vuex) should be called only once.'
            )
        }
        return
    }
    Vue = _Vue
    applyMixin(Vue)
}
```

applyMixin 就是这个 export default function，它还兼容了 Vue 1.0 的版本，这里我们只关注 Vue 2.0 以上版本的逻辑，它其实就全局混入了一个 beforeCreate 钩子函数，它的实现非常简单，就是把 options.store 保存在所有组件的 this.$store 中，这个 options.store 就是我们在实例化 Store 对象的实例，稍后我们会介绍，这也是为什么我们在组件中可以通过 this.$store 访问到这个实例。

## Store 实例化

```js
export class Store {
    constructor(options = {}) {
        // Auto install if it is not done yet and `window` has `Vue`.
        // To allow users to avoid auto-installation in some cases,
        // this code should be placed here. See #731
        if (!Vue && typeof window !== 'undefined' && window.Vue) {
            install(window.Vue)
        }

        if (process.env.NODE_ENV !== 'production') {
            assert(Vue, `must call Vue.use(Vuex) before creating a store instance.`)
            assert(typeof Promise !== 'undefined', `vuex requires a Promise polyfill in this browser.`)
            assert(this instanceof Store, `Store must be called with the new operator.`)
        }

        const {
            plugins = [],
                strict = false
        } = options

        // store internal state
        this._committing = false
        this._actions = Object.create(null)
        this._actionSubscribers = []
        this._mutations = Object.create(null)
        this._wrappedGetters = Object.create(null)
        this._modules = new ModuleCollection(options)
        this._modulesNamespaceMap = Object.create(null)
        this._subscribers = []
        this._watcherVM = new Vue()

        // bind commit and dispatch to self
        const store = this
        const {
            dispatch,
            commit
        } = this
        this.dispatch = function boundDispatch(type, payload) {
            return dispatch.call(store, type, payload)
        }
        this.commit = function boundCommit(type, payload, options) {
            return commit.call(store, type, payload, options)
        }

        // strict mode
        this.strict = strict

        const state = this._modules.root.state

        // init root module.
        // this also recursively registers all sub-modules
        // and collects all module getters inside this._wrappedGetters
        installModule(this, state, [], this._modules.root)

        // initialize the store vm, which is responsible for the reactivity
        // (also registers _wrappedGetters as computed properties)
        resetStoreVM(this, state)

        // apply plugins
        plugins.forEach(plugin => plugin(this))

        if (Vue.config.devtools) {
            devtoolPlugin(this)
        }
    }
}
```

我们把 Store 的实例化过程拆成 3 个部分，分别是初始化模块，安装模块和初始化 store.\_vm，接下来我们来分析这 3 部分的实现。

```js
function makeLocalContext(store, namespace, path) {
    const noNamespace = namespace === ''；

    const local = {
        dispatch: noNamespace ? store.dispatch : (_type, _payload, _options) => {
            let {
                type
            } = args;
            if (!options || !options.root) {
                type = namespace + type
            }
        }
    }
    return store.dispatch(type, payload)
}
```

```js
function makeLocalGetters(store， namespace) {
    const gettersProxy = {};

    const splitPos = namespace.length;

    const localType = type.slice(splicePos)

    Object.defineProperty(gettersProxy, localType, {
        get: () => store.getters[type],
        enumerable: true
    })

    return gettersProxy
}
```

Vuex 允许我们将 store 分割成模块（module）。每个模块拥有自己的 state、mutation、action、getter，甚至是嵌套子模块——从上至下进行同样方式的分割：

```js
function registerAction(store, type, handler, local) {
    const entry = store._actions[type] || (store._actions[type] = []);
    entry.push(function wrappedActionHandler(payload, cb) {
        let res = handler.call(store, {
            dispatch: local.dispatch,
            commit: local.commit,
            getters: local.getters,
            state: local.state,
            rootGetters: store.getters,
            rootState: store.state
        }, payload, cb)
    })
}
```

如果我们去访问state，

```js
get state(){
  return this._vm_data.$$data
}
```

api

```js
beforeCreate() {
  if (isDef(this.$options.router)) {
    // ...
    this._router = this.$options.router
    this._router.init(this)
    // ...
  }
}  
```

matcher 相关的实现都在 src/create-matcher.js 中，我们先来看一下 matcher 的数据结构：

```js
export type Matcher = {
  match: (raw: RawLocation, current?: Route, redirectedFrom?: Location) => Route;
  addRoutes: (routes: Array<RouteConfig>) => void;
};
```

Matcher 返回了 2 个方法，match 和 addRoutes，在上一节我们接触到了 match 方法，顾名思义它是做匹配，那么匹配的是什么，在介绍之前，我们先了解路由中重要的 2 个概念，Loaction 和 Route，它们的数据结构定义在 flow/declarations.js 中。

```js
export function getHash(){
  const href = window.location.href;
  const index = href.indexOf('#')
  return index === -1 ? '' : href.slice(index +1)
}
```

这里不能采用 window\.location.hash 因为火狐有 bug


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://shenjunhong.gitbook.io/blog/yuan-ma/vue/vue2/vue-yuan-ma-fen-xi-zhi-vuex.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
