Vuex

203 阅读5分钟

Vuex

安装

npm install vuex --save
或者
yarn add vuex

使用

在src目录下新建一个store的文件夹,在store里面创建一个index.js文件:

import Vue from 'vue'
import Vuex from 'vuex'  //引入vuex


Vue.use(Vuex) //注册插件

const store = new Vuex.Store({  //new 一个 Vuex仓库
    state: {    //state里面放置的是要共享的数据
        count: 5
    }, 
    actions: {   //actions 是异步操作时候用到的

    },
    mutations: {   //如果没有异步操作,就可以直接调用mutations更改数据
        increment(state) {
            state.count++
        },
        decrement(state) {
            state.count--
        }
    }
    
})


export default store

在mian.js中引入Vuex:

import Vue from 'vue'
import App from './App'
import store from './store'
Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
    el: '#app',
    store,
    render: h => h(App)
})

这样就可以在组件内使用Vuex共享的数据了:

App.vue:

<template>
  <div id="app">
    <h1>APP的内容</h1>
   <h1>父组件的count:{{$store.state.count}}</h1>  //获取vuex中的count
   <button @click="increment">++</button>
   <button @click="decrement">---</button>

    <HelloVuex/>
  </div>
</template>

<script>
import HelloVuex from './components/HelloVuex'

export default {
  name: 'App',
  components: {
    HelloVuex
  },
  methods:{
    increment(){   //点击的时候触发这个事件。用commit发送这个事件,这样 vuex中的mutations就能监听到这个事件,改变vuex中数据
      this.$store.commit('increment')
    },
    decrement(){
       this.$store.commit('decrement')
    }
  }
}
</script>

<style>

</style>

Getters基本使用

在store中加入一个对象:getters,getters相当于我们普通组件中的计算属性

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


Vue.use(Vuex)

const store = new Vuex.Store({
    state: {
        count: 5,
        students: [
            { name: '小红', age: 22 },
            { name: '小燕', age: 23 },
            { name: '小明', age: 12 }

        ]
    },
    actions: {

    },
    mutations: {
        increment(state) {
            state.count++
        },
        decrement(state) {
            state.count--
        }
    },
    getters: {
        powerCount(state) {  //定义计算函数,并把结果返回去
            return state.count * state.count
        },
        getStud20(state) {  //定义计算函数,并把结果返回去
            return state.students.filter(s => s.age > 20)
        }
    }
    
})


export default store

在组件中使用:

HelloVuex.vue:

<template>
  <div>
    <h2>hellow的内容----</h2>
    <h2>helloVuex的count:{{$store.state.count}}</h2>
    <h2>{{$store.getters.powerCount}}</h2>  //拿到计算属性
    <h2>大于20岁的学生{{$store.getters.getStud20}}</h2> //拿到计算属性
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',

}
</script>


<style scoped>

</style>

把Getters作为参数

getters: {
        powerCount(state) {
            return state.count * state.count
        },
        getStud20(state) {
            return state.students.filter(s => s.age > 20)
        },
        getStud20Length(state, getters) { //这段代码的意思就是,把getters作为参数传递进去,这样就可以在里面使用getters.xxx 方法,前提是这个方法必须存在getters里面,这样就不用重复计算了
            return getters.getStud20.length
        }
    },

如果想在Getters里面传递参数

如果要在Getters里面接收传递过来的参数,就必须要返回一个函数,这样才能传递自己的参数

getters: {
        powerCount(state) {
            return state.count * state.count
        },
        getStud20(state) {
            return state.students.filter(s => s.age > 20)
        },
        getStud20Length(state, getters) {
            return getters.getStud20.length
        },
        getAgeStu(state) {
            return (age) => {  //在这里,返回的是一个函数,接收参数
                return state.students.filter(s => s.age > age) //返回年龄大于传递过来参数的学生
            }
        }

在组件中调用的时候也有一点不一样:

 <h2>大于20岁的学生人数{{$store.getters.getStud20Length}}</h2> //普通的getters
    <h2>---------------------分割----------</h2>
    <h2>找到年龄大于13的学生{{$store.getters.getAgeStu(13)}}</h2>  //可以传递参数的getters,因为返回的是一个函数,所以要加圆括号调用,并传递参数

Mutation状态更新

Vuex的store状态的更新唯一方式:提交Mutation

Mutation主要包括两部分:

● 字符串的事件类型(type)

● 一个回调函数(handler),该回调函数的第一个参数就是state。

Mutation传递参数

store:

   mutations: {
        increment(state) {
            state.count++
        },
        decrement(state) {
            state.count--
        },
        incrementCount(state, count) { //定义一个函数,第一个参数是state,第二个是传递过来的参数
            state.count += count
        }
    }


参数的传递:

<template>
  <div id="app">
   <button @click="addCount(5)">++5</button>
   <button @click="addCount(10)">++10</button>

  </div>
</template>
<script>

export default {
  name: 'App',
  methods:{
    addCount(count){ //点击事件
      this.$store.commit('incrementCount',count) //发送事件,并带上参数
    }
  }
}
</script>

以对象的风格提交,并带上参数

组件提交部分:

 methods:{
    addCount(count){
      this.$store.commit({  //提交过去后,Mutation会自动识别对应的函数
       type: 'incrementCount',
       count
      })
    }
  }

store处理部分:

mutations: {
        incrementCount(state, payload) { //这个时候拿到的payload是一个对象了
            state.count += payload.count  //所以要打点获取参数
        }
    }

Mutation响应规则

Vuex的store中的state是响应式的, 当state中的数据发生改变时, Vue组件会自动更新

这就要求我们必须遵守一些Vuex对应的规则:

  • 提前在store中初始化好所需的属性.
  • 当给state中的对象添加新属性时, 使用下面的方式:
    1. 方式一: 使用Vue.set(obj, 'newProp', 123)
    2. 方式二: 用新对象给旧对象重新赋值
const store = new Vuex.Store({
    state: {
        count: 5,
        students: [
            { name: '小红', age: 22 },
            { name: '小燕', age: 23 },
            { name: '小明', age: 12 }

        ],
        info:{
            name:'小燕',
            age:22
        }
    }, 
    mutations: {
        updataInfo(state){
            //设置
    错误        state.info['sex'] = '女' //这种方式不是响应式的,设置了页面不会跟着改变
    正确        Vue.set(state.info,'sex','女') //这种办法是响应式的,页面会跟着改变
			//删除
    错误       delete state.info.age //这种方式不是响应式的,设置了页面不会跟着改变
    正确        Vue.delete(state.info,age) //这种方式不是响应式的,设置了页面不会跟着改变
        }
    }
})

把mutations的类型抽离成常量

在store文件夹下创建一个mutations-types.js文件:

export const INCREMENT = 'increment' //定义一个常量并导出

在store中使用:

import Vue from 'vue'
import Vuex from 'vuex'
import { INCREMENT } from './mutations-types'; //引入常量

Vue.use(Vuex)

const store = new Vuex.Store({
    state: {
        count: 5,
        students: [
            { name: '小红', age: 22 },
            { name: '小燕', age: 23 },
            { name: '小明', age: 12 }

        ],
        info: {
            name: '小燕',
            age: 22
        }
    },
    mutations: {
      
        [INCREMENT](state) {  //使用常量 用[]包裹,里面是常量,同样被解析成一个函数
            state.count++
        },
        decrement(state) {
            state.count--
        },
        incrementCount(state, payload) {
            state.count += payload.count
        }
    }
})


export default store

组件中使用:

<script>
import HelloVuex from './components/HelloVuex'
import {INCREMENT} from './store/mutations-types'; //导入
export default {
  name: 'App',
  components: {
    HelloVuex
  },
  methods:{
    increment(){
      this.$store.commit(INCREMENT) //使用
    }
}
</script>

Action

不要在Mutation中进行异步操作,所以要进行异步的话,就要用到Action

Action的普通用法:

context是和store对象具有相同方法和属性的对象. 也就是说, 我们可以通过context去进行commit相关的操作, 也可以获取context.state等.

const store = new Vuex.Store({
    state: {
        count: 5,
        students: [
            { name: '小红', age: 22 },
            { name: '小燕', age: 23 },
            { name: '小明', age: 12 }

        ],
        info: {
            name: '小燕',
            age: 22
        }
    },
    mutations: {
        updataInfo(state,data) { //调用参数之后,更改数据
           state.info = data
        }
    },
    actions: {
       getInfo(context,payload){
            $.ajax({url,success:(data)=>{ //发送请求,拿到数据
                context.commit('updataInfo',data) //拿到数据之后通过commit调用mutations里面的方法,并传入参数
                console.log(payload)
            }})
        }
    }
})

组件调用:

methods:{
    getInof(){
      this.$store.dispatch('getInfo','我是自带的参数')
    }
  }

Action配合Promise,拿到数据的用法

action:

actions: {
        getInfo(context, payload) {
            return new Promise((resolve) => { //返回一个Promise对象
                setTimeout(() => {  //这里进行异步操作
                    console.log('异步完成,返回参数')
                    resolve('11111') //拿到data后,resolve(data) 返回data
                }, 1000)
            })
        }
    }

组件调用事件:

methods:{
 getInof(){
      this.$store.dispatch('getInfo').then(res=>{ //调用dispatch触发事件,会返回一个Promise对象,用 .then方法就可以接收返回来的参数
        console.log(res)
      })
    }
}

Module

用法:

import Vue from 'vue'
import Vuex from 'vuex'
import { INCREMENT } from './mutations-types';

Vue.use(Vuex)

const moduleA = {  //新定义一个模块
    state: {
        name: '三哥'
    },
    mutations: {

    },
    getters: {

    }
}


const store = new Vuex.Store({
    state: {
        count: 5,
        students: [
            { name: '小红', age: 22 },
            { name: '小燕', age: 23 },
            { name: '小明', age: 12 }

        ],
        info: {
            name: '小燕',
            age: 22
        }
    },
    mutations: {
        updataInfo(state, data) {
            state.info = data
        },
        [INCREMENT](state) {
            state.count++
        },
        decrement(state) {
            state.count--
        },
        incrementCount(state, payload) {
            state.count += payload.count
        }
    },
    modules: {  //在modules这里把模块加进来
        a: moduleA
    }
})


export default store

组件使用:

 <h1>模块A的name:{{$store.state.a.name}}</h1> //在state.后面跟上模块名,然后就是内容了

模块中的mutations和getters用法是和原来的一样的:

模块A:

const moduleA = {
    state: {
        name: '三哥'
    },
    mutations: {
        updataName(state, data) {
            state.name = data
        }
    },
    getters: {
        fullname(state) {
            return state.name + '1111'
        }
    }
}

组件中调用和显示:

<template>
  <div id="app">
    <h1>模块A的name:{{$store.state.a.name}}</h1>
    <h1>模块A的getters:{{$store.getters.fullname}}</h1> //获取模块A的getters
    <button @click="updataName">模块A的mutations</button>

  </div>
</template>
<script>
import HelloVuex from './components/HelloVuex'
import {INCREMENT} from './store/mutations-types';
export default {
  name: 'App',
  components: {
    HelloVuex
  },
  methods:{
    updataName(){
      this.$store.commit('updataName','四哥') //调用模块A的Mutations
    }
  }
}
</script>

getters补充

getters的第二第三个参数

import Vue from 'vue'
import Vuex from 'vuex'
import { INCREMENT } from './mutations-types';

Vue.use(Vuex)

const moduleA = {
    state: {
        name: '三哥'
    },
    mutations: {
        updataName(state, data) {
            state.name = data
        }
    },
    getters: {
        fullname(state) {
            return state.name + '1111'
        },
        fullname2(state, getters) {  //第二个参数,getters,代表模块本身的getters,所以可以调用自己的方法
            return getters.fullname + '222'
        },
        fullname3(state, getters, rootState) { //第三个参数,rootState, 这个参数可以拿到根模块的state里面的参数
            return getters.fullname2 + rootState.count
        }
    }
}


const store = new Vuex.Store({
    state: {
        count: '纳尼',
        students: [
            { name: '小红', age: 22 },
            { name: '小燕', age: 23 },
            { name: '小明', age: 12 }

        ],
        info: {
            name: '小燕',
            age: 22
        }
    }
    modules: {
        a: moduleA
    }
})


export default store

子模块的actions补充

const moduleA = {
    state: {
        name: '三哥'
    },
    mutations: {
        updataName(state, data) {
            state.name = data
        }
    },
    getters: {
        fullname(state) {
            return state.name + '1111'
        },
        fullname2(state, getters) {
            return getters.fullname + '222'
        },
        fullname3(state, getters, rootState) {
            return getters.fullname2 + rootState.count
        }
    },
    actions: {
        aupdataName(context) {
            //这里的context.commit只能调用自己模块的方法,访问不到根模块的方法的
            //如果想用根模块的方法,就要:
            // context.rootGetters('xxx') //这样才能访问根的方法
            // 我们可以打印context来看一下
            console.log(context)
            /**
             *  dispatch: ƒ boundDispatch(type, payload)
                commit: ƒ boundCommit(type, payload, options)
                getters: {…}
                state: {__ob__: Observer}
                rootGetters: {…}
                rootState: {__ob__: Observer}
                __proto__: Object 
             */
            setTimeout(() => {
                context.commit('updataName')
            }, 2000)
        }
    }
}

Vuex的抽离

image-20191221142453753