乐趣区

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

概念
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。当项目比较庞大的时候,每个组件的状态很多,为了方便管理,需要把组件中的状态抽取出来,放入 Vuex 中进行统一管理。
每一个 Vuex 应用的核心就是 store(仓库)。”store” 基本上就是一个容器,它包含着你的应用中大部分的状态(state)。Vuex 和单纯的全局对象有以下两点不同:
Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
简单应用
构建 vue 工程 vue init webpack vuexStudy 构建后目录结构其中: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 启动项目,最终的结果如图:
源码解读
node 添加 Vuex 依赖下载的 vuex 文件 (node_modules 目录下) 如下:其中 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 参数:args 参数:
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;
}
}
};

退出移动版