vue学习之vuex基础详解及源码解读(一)

概念

Vuex是一个专为Vue.js应用程序开发的状态管理模式。当项目比较庞大的时候,每个组件的状态很多,为了方便管理,需要把组件中的状态抽取出来,放入Vuex中进行统一管理。

每一个 Vuex 应用的核心就是store(仓库)。"store"基本上就是一个容器,它包含着你的应用中大部分的状态(state)。Vuex 和单纯的全局对象有以下两点不同:

Vuex的状态存储是响应式的。当 Vue组件从store中读取状态的时候,若store中的状态发生变化,那么相应的组件也会相应地得到高效更新。

你不能直接改变store中的状态。改变store中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。

简单应用

构建vue工程
vue init webpack vuexStudy
构建后目录结构
vue学习之vuex基础详解及源码解读(一)
其中:
index.js

import Vue from 'vue'
import Vuex from 'vuex'

//如果在模块化构建系统中,请确保在开头调用了 Vue.use(Vuex)
Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    //存放组件之间共享的数据
    //在组件通过this.$store.state.count获取
    count: 0
  },
  mutations: {
    //显式的更改state里的数据,不能用于处理异步事件
    //组件中通过this.$store.commit('incrementByStep');调用
    incrementByStep(state) {
      state.count++;
    }
  },
  getters:{
    //如vue中的计算属性一样,基于state数据的二次包装,常用于数据的筛选和多个数据的相关性计算
  },
  actions:{
    //类似于mutation,用于提交mutation来改变状态,而不直接变更状态,可以包含任意异步操作
  }
});

new Vuex.Store({}) 表示创建一个Vuex实例,通常情况下,他需要注入到Vue实例里。 Store是Vuex的一个核心方法,字面上理解为“仓库”的意思。Vuex Store是响应式的,当Vue组件从store中读取状态(state选项)时,若store中的状态发生更新时,他会及时的响应给其他的组件而且不能直接改变store的状态,改变状态的唯一方法是显式地提交更改。

main.js引入vuex

import Vue from 'vue'
import App from './App'
//vuex文件
import store from './store'

Vue.config.productionTip = false;

/* eslint-disable no-new */
new Vue({
  el: '#app',
  //引入
  store,
  components: { App },
  template: '<App/>'
})

APP.vue引用了counter这个组件

<div id="app">
    <!--<img src="./assets/logo.png">-->
    <!--<HelloWorld/>-->
    <counter/>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld'
import counter from './components/counter'

export default {
  name: 'App',
  components: {
    //HelloWorld
    counter
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

counter.vue定义counter组件

<template>
    <div id="counterContent">
      <div>{{ count }}</div>
      <button v-on:click="addCount">请点击</button>
    </div>
</template>

<script>
    export default {
      name: "counter",
      computed: {
        count () {
          return this.$store.state.count
        }
      },
      methods:{
          addCount(){
            debugger;
            this.$store.commit('incrementByStep');
          }
      }
    }
</script>

<style scoped>
  #counterContent{
    background-color: blue;
    width:200px;
    height:50px;
  }
</style>

通过npm run dev启动项目,最终的结果如图:
vue学习之vuex基础详解及源码解读(一)

源码解读

node添加Vuex依赖下载的vuex文件(node_modules目录下)如下:
vue学习之vuex基础详解及源码解读(一)
其中vuex.common.js在预编译调试时,CommonJS规范的格式,可以使用require("")引用的NODEJS格式。
vuex.esm.js在预编译调试时, EcmaScript Module(ES MODULE),支持import from 最新标准的。
vuex.js直接用在<script>标签中的,完整版本,直接就可以通过script引用。
而vuex的源码托管在https://github.com/vuejs/vuex,这里研究git上的源码。

入口

Vuex 源码的入口是 src/index.js。

import { Store, install } from './store'
import { mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers } from './helpers'

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

这是Vuex对外暴露的API。其中, Store是Vuex提供的状态存储类,通常我们使用Vuex就是通过创建 Store的实例。接着是install方法,这个方法通常是我们编写第三方Vue插件的时候使用。

install

install是在store.js内暴露的方法
当Vue通过npm安装到项目中的时候,我们在代码中引入第三方Vue插件需要添加下列代码

import Vue from 'vue'
import Vuex from 'vuex'

//如果在模块化构建系统中,请确保在开头调用了 Vue.use(Vuex)
Vue.use(Vuex);

执行Vue.use(Vuex)的时候,其实就是调用了install的方法并传入Vue的引用。
Vue.use

Vue.use = function (plugin) {
    var installedPlugins = (this._installedPlugins || (this._installedPlugins = []));
    if (installedPlugins.indexOf(plugin) > -1) {
      return this
    }

    // additional parameters
    var args = toArray(arguments, 1);
    args.unshift(this);
    if (typeof plugin.install === 'function') {
      //调用vuex的install
      plugin.install.apply(plugin, args);
    } else if (typeof plugin === 'function') {
      plugin.apply(null, args);
    }
    installedPlugins.push(plugin);
    return this
  };
}

install

//判断Vue是否已存在,保证install方法只执行一次
  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变量,index.js文件的其它地方使用Vue这个变量
  Vue = _Vue
  //调用了 applyMixin 方法,给 Vue 的实例注入一个 $store 的属性
  applyMixin(Vue)
}

plugin参数:
vue学习之vuex基础详解及源码解读(一)
args参数:
vue学习之vuex基础详解及源码解读(一)

var applyMixin = function (Vue) {
  //获取版本信息,这里是2
  var version = Number(Vue.version.split('.')[0]);

  if (version >= 2) {
    //调用vuexInit
    Vue.mixin({ beforeCreate: vuexInit });
  } else {
    var _init = Vue.prototype._init;
    Vue.prototype._init = function (options) {
      if ( options === void 0 ) options = {};

      options.init = options.init
        ? [vuexInit].concat(options.init)
        : vuexInit;
      _init.call(this, options);
    };
  }
  //给Vue实例注入$store 的属性,可以通过this.$store.xxx访问
  function vuexInit () {
    var options = this.$options;
    // store injection
    if (options.store) {
      this.$store = typeof options.store === 'function'
        ? options.store()
        : options.store;
    } else if (options.parent && options.parent.$store) {
      this.$store = options.parent.$store;
    }
  }
};

相关推荐