简谈vuex

Vuex 是什么?

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

 

Vuex原理

resetStoreVM(this, state) 他就是整个vuex的关键

// src/store.js
function resetStoreVM (store, state, hot) {
  // 省略无关代码
  Vue.config.silent = true
  store._vm = new Vue({
    data: {
      $$state: state
    },
    computed
  })
}

去除了一些无关代码后我们发现,其本质就是将我们传入的state作为一个隐藏的vue组件的data,也就是说,我们的commit操作,本质上其实是修改这个组件的data值,结合computed,修改被defineReactive代理的对象值后,会将其收集到的依赖的watcher中的dirty设置为true,等到下一次访问该watcher中的值后重新获取最新值。

这样就能解释了为什么vuex中的state的对象属性必须提前定义好,如果该state中途增加一个属性,因为该属性没有被defineReactive,所以其依赖系统没有检测到,自然不能更新。

vuex中的store本质就是没有template的隐藏着的vue组件;

vuex核心流程

  • Vue Components:Vue组件。HTML页面上,负责接收用户操作等交互行为,执行dispatch方法触发对应action进行回应。
  • dispatch:操作行为触发方法,是唯一能执行action的方法。
  • actions:操作行为处理模块。负责处理Vue Components接收到的所有交互行为。包含同步/异步操作,支持多个同名方法,按照注册的顺序依次触发。向后台API请求的操作就在这个模块中进行,包括触发其他action以及提交mutation的操作。该模块提供了Promise的封装,以支持action的链式触发。
  • commit:状态改变提交操作方法。对mutation进行提交,是唯一能执行mutation的方法。
  • mutations:状态改变操作方法。是Vuex修改state的唯一推荐方法,其他修改方式在严格模式下将会报错。该方法只能进行同步操作,且方法名只能全局唯一。操作之中会有一些hook暴露出来,以进行state的监控等。
  • state:页面状态管理容器对象。集中存储Vue components中data对象的零散数据,全局唯一,以进行统一的状态管理。页面显示所需的数据从该对象中进行读取,利用Vue的细粒度数据响应机制来进行高效的状态更新。
  • getters:state对象读取方法。图中没有单独列出该模块,应该被包含在了render中,Vue Components通过该方法读取全局state对象。

Vue组件接收交互行为,调用dispatch方法触发action相关处理,若页面状态需要改变,则调用commit方法提交mutation修改state,通过getters获取到state新值,重新渲染Vue Components,界面随之更新。

vuex目录

这是取自vuex官方文档的目录展示,对于初次接触或者仅限于了解的来说,没有必要照搬文档所给的目录结构,按自己的需求灵活修改,当然文档给的目录结构是合理的,层次感和逻辑感更好。

vuex实现

      遇到问题时,可以查看store实例,有可能会给你灵感。

    1.装载实例

            main.js

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import store from './store/' // 引入store

Vue.config.productionTip = false

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

  store.js

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


Vue.use(Vuex)

export default new Vuex.Store({
})

   1.1  挂载跟级别数据/事件

         最简单的实现方式,就是把所需state,getter,mutations,actions等直接挂在根级别上,当然这样会导致根级别特别混乱,这个时候就引出了modules,下面会讲到。

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


Vue.use(Vuex)

// 根state
const state = {
	testField: "444"
}

// 根getter
const getters = {
	getTestField(state) {
		return state.testField;
	}
}

export default new Vuex.Store({
	state,
	getters
})

这里仅仅做一个举例,并未写出所有,actions、mutations等和getter的实现方式是一样的。

1.2 引入modules

     模块化的思维在项目中已经被用烂了,或者也已经听烦了,当然vuex里也有模块,也会模块化。

   创建module_1.js

vuex中的store分模块管理,需要在store的index.js中引入各个模块,为了解决不同模块命名冲突的问题,将不同模块的namespaced:true,之后在不同页面中引入getter、actions、mutations时,需要加上所属的模块名,实际上如果你能确保不会冲突,namespaced:true的作用可有可无。

export default {
    namespaced: true, 
	state: {
		testField_1: "11",
	},
	getters: {
		getTestField(state) {
			return state.testField_1;
		},
	},
	mutations: {
		setTestFieldMutation(state, testField_1) {
			state.testField_1 = testField_1;
		},
	},
	actions: {
		setTestFieldAction(context, testField_1) {
			context.commit("setTestFieldMutation", testField_1);
		},
	},
};

挂在store上,挂载方式很简单,store提供了modules方法。

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

import module_1 from './modules/module_1'

Vue.use(Vuex)

// 根state
const state = {
	testField: "444"
}

// 根getter
const getters = {
	getTestField(state) {
		return state.testField;
	}
}

export default new Vuex.Store({
	state,
	getters,
	modules: {
		modules_1
	}
})

1.3 使用

当然,使用的方式也是多种多样的,看你喜欢哪一种,推荐使用辅助函数mapGetters等。

模块中的使用,和根级别的还是有一点区别,当然如果把模块当作根级别下的一个属性就不难理解了。

<template>
  <div class="hello">
    <h1>{{ getTestFields }}eeeee</h1>
    <h1>{{ getModulesTestField }}eeeee</h1>
    <h1>{{ getTestField_2 }}eeeee</h1>
    // 简单粗暴型,直接获取
    <h1>{{ $store.state.testField }}eeeee</h1>
  </div>
</template>

<script>
import {mapGetters} from 'vuex'
// import { createNamespacedHelpers } from 'vuex'
// const { mapGetters } = createNamespacedHelpers('modules_2')
export default {
  name: 'HelloWorld',
  data () {
    return {
    }
  },
  computed: {
    // 方式一: 直接引入所需module + module下的方法
    ...mapGetters({
      getTestField_2: 'modules_2/getTestField'
    }),

    // 方式二:先声明module, 引入该module下的方法
    // ...mapGetters('modules_2', ['getTestField']),

    /*
    * 方式三:和方式二是同样的原理
    * import { createNamespacedHelpers } from 'vuex'
    * const { mapGetters } = createNamespacedHelpers('modules_2')
    */
    // ...mapGetters(['getTestField']),

    /*
    * 默认直接获取根级别state下的数据
    */
    getTestFields() {
      console.log(this.$store)
      /*
      * 如果想调用$store的getter/action,需要是根级别,或者module存在namespaced属性
      */
      return this.$store.getters['modules_2/getTestField']
      // return this.$store.state.testField
    },

    // 直接获取模块内的state数据
    getModulesTestField() {
      return this.$store.state.modules_1.testField_1
    }
  },
  methods: {
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1 {
  font-weight: normal;
}
</style>

1.4 dispatch

        下面的前提都是在命名不规范,所有action全部一样的情况下(极端情况),如果命名规范,那就正常调用即可。

    // 默认查找所有不存在namespaced: true,属性的module和根级别下的action
    this.$store.dispatch("setTestFieldAction", "I did");
    // 如果想调用module下的action,需要有namespaced: true,属性
    this.$store.dispatch("modules_2/setTestFieldAction", "I did 2 ");
    // unknown action type: modules_1/setTestFieldAction
    this.$store.dispatch("modules_1/setTestFieldAction", "I did 1 ");

当然,actions的使用和getter一样,方法异曲同工,在此列举一种方式,其他方式,可以私下试一下,也是完全可行的。

methods: {
    // 使用actions的辅助函数,方式和mapGetters同理
    ...mapActions({
      mdu_2: "modules_2/setTestFieldAction"
    })
  },
mounted() {
    // 使用辅助函数mapActions
    this.mdu_2("I do 2");
  }

1.5 模块间调用

    vuex中各个modules间的交互,当然,是可以直接使用的,毕竟都是挂载在store上的。

    但,说的是另一种,rootState, rootGetters,rootMutations,rootActions这四种。

    1.5.1 rootGetters和rootState

        使用方式很简单,就把它当成对象调用即可。

getTestField(state, getters, rootState, rootGetters) {
			console.log(state, "state");
			console.log(getters, "getters");
			console.log(rootState, "rootState");
			console.log(rootGetters, "rootGetters");
			return state.testField_1 + rootGetters["modules_2/getTestField"];
		},

   1.5.2 commit 的 dispatch

  

// modules_1.js
actions: {
		setTestFieldAction({ dispatch, commit, getters, rootGetters }) {
			console.log(commit, "commit");
			dispatch("modules_2/setTestFieldAction2", "rrrrrr", { root: true })
			commit("setTestFieldMutation", "testField_1");
			commit("setTestFieldMutation3", "dddddd", { root: true });
		},
	},

到此就可以了,vuex基本可以在各个模块中交互,引用其他模块的state和action,

对于vuex的工作原理可以参考这篇文章

把自己所知所结做一个记录,当然,这篇文章,还够不上入门,以后回继续补充,有不同见解,欢迎打扰。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值