vuex
vuex是一个专门为vue.js应用程序开发的状态管理模式,它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
vueX有几个核心的概念:
State
Getters
Mutations
Actions
Modules
首先我们用vue-cli构建一个vue目录,如图所示
]
其中有三个vue组件,app.vue,hello.vue,content.vue,在router目录的index.js定义了路由关系。
import Vue from 'vue' import Router from 'vue-router' import Hello from '@/components/Hello' Vue.use(Router); export default new Router({ routes: [ { path: '/', name: 'Hello', component: Hello } ] })
APP.vue
{ { msg }}
hello.vue
{ { msg }}
content.vue
{ {msg}}
通过上面的三个组件和路由我们可以看出,访问跟路径,app组件引入hello组件,hello.vue嵌套content.vue组件,跟main.js同级建一个store.js,定义vuex的store和简单的mutations
import vue from 'vue'; import vuex from 'vuex'; vue.use(vuex); export default new vuex.Store({ state:{ time:new Data().getTime(), names:["张三"], } })
修改main.js,添加vuex的使用
import Vue from 'vue' import App from './App' import router from './router' import store from './store.js' new Vue({ el: '#app', router, store, template: '', components: { App } });
这个时候这个项目已经把 store 的实例注入所有的子组件,里面有一个state对象和mutations对象,state包括了全部的状态,作为一个唯一数据源存在,vue项目里面可以在任意组件里面引用state里面的属性,
如何在vue组件引入state,几种常用的办法
通过
js { { this.$store.state.time }}
可以直接访问store定义的state对象-
在计算属性中返回某个状态
computed:{ count(){return this.$store.state.time; } }
-
当一个组件获取多个状态,上面两种使用方式比较重复和冗余,为了解决这个问题,我们可以使用mapState辅助函数
import {mapState} from 'vuex' export default { computed:mapState([ 'time' ]) }
-
mapState返回一个对象,映射this.count为this.$store.state.time,mapState里面参数是数组形式,参数名和state同名
import {mapState} from 'vuex'computed:mapState({ time:state=>state.time, nameArr:'names'})
这种方式的mapState形参是一个对象,第一个count使用箭头函数,返回state.count,第二个修改了names的别名变成nameArr
Mutations
我们修改store.js,通过添加mutations,来改变store里面的state。更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutations 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数
export default new vuex.Store({ state:{ time:new Date().getTime(), names:["张三"] }, mutations:{ pushName (state,thisName){ state.names.push(thisName.name) } } })
我们修改app组件,
姓名: { { name }}
export default { name: 'app', data(){ return { msg: 'Welcome to Your App.vue components', addName:'' } }, computed:mapState({ nameArr:'names' }), methods:{ addNameFn(){ var that=this; this.$store.commit('pushName',{name:that.addName}) } } }
Mutations必须是同步函数,因为每一条 mutation 被记录,devtools 都需要捕捉到前一状态和后一状态的快照。然而 mutation 在异步函数中的回调让这不可能完成
调用方式有三种-
使用this.$store.commit调用,第一个参数是Mutations里面定义的方法名,第二个参数是传入的形参,建议应该是一个对象,可以包含多个字段,并且会更容易阅读代码
addNameFn(){ this.$store.commit('pushName',{ name:this.addName }) }
-
使用 type属性对象,commit调用的参数只有一个是一个对象,type是Mutations里面定义的方法名,其他字段是形参
addNameFn(){ this.$store.commit({ type:'pushName', name:this.addName }) }
-
使用mapMutations,引入 mapMutations ,将methods 映射为 store.commit 调用
import {mapMutations} from 'vuex' methods:{ ...mapMutations([ 'pushName' ]), addNameFn(){ this.pushName({name:that.addName}) } }
Getters
有时候我们需要根据store中的state中的属性派生一些值,例如统计添加的姓名总数,如果有多个组件需要用到统计姓名总数这个值,我们要么是在每个组件中重复写一个方法,要么是
写一个公用的方法,每个组件引入,这两种方法都不是很理想,所以引入了Getters,可以认为是store的计算属性,首先添加getters,添加一个统计添加的姓名总数的方法,并且在每个组件打印这个总数修改store.js,添加getters属性,添加getNameLen方法,参数是state
export default new vuex.Store({ state:{ time:new Date().getTime(), names:["张三"] }, mutations:{ pushName (state,thisName){ state.names.push(thisName.name) } }, getters:{ getNameLen:state=>{ return state.names.length } } })
修改App.vue的template,直接打印添加姓名的个数
姓名: { { name }}
人数:{
{ this.$store.getters.getNameLen }}
修改完这些,我们发现当我们添加一个姓名,每个组件都会更新添加的姓名的总条数,getters调用就是这么简单,上面只是一种调用方式,除此外还有两种调用方式
1.在计算属性中引入
人数:{
{ getNameLen }}
computed: { nameCount(){ return this.$store.getters.getNameLen } }
2.使用 mapGetters,引入mapGetters,使用个结构赋值的方法,将getters里面的方法映射到局部计算属性上,如果是数组则跟getters里面的方法名同名,对象可以修改名称,同mapState一样
人数:{
{ getNameLen }}
import {mapGetters} from 'vuex' computed: { ...mapGetters([ "getNameLen" ]), ...mapState({ nameArr: 'names', }) }
Actions
Action类似于mutation,不同在于
action可以包含异步操作,而mutation只能是同步的
mutation是直接修改state里面的数据状态,而且action是commit一个mutation来记录修改状态。
让我们通过一个简单例子来实现一个简单的action:
1.修改 config index.js,引入url模块,修改dev配置里面的proxyTable,建一个与src同级的static目录,里面放一个
getName-time0.json文件,模拟本地http请求返回的json数据,重启服务, npm run dev
访问 http://localhost:8080/dataweb/getName?time=0
就能请求到static里面的json文件 url=require('url'); proxyTable: { '/dataweb': { target: 'http://localhost:8080', changeOrigin: true, pathRewrite: function(path, req) { var urlParsed = url.parse(req.url, true), query = urlParsed.query, pathname = urlParsed.pathname.replace(/\/*$/g,''); pathname = pathname.substring(pathname.lastIndexOf('/')); Object.keys(query).forEach((key) => { pathname += ('-' + key + query[key]); }); pathname = '/static' + pathname + '.json'; console.log('proxy request ' + path + ' to ' + pathname); return pathname; } } }
{ "data":["赵钱","王五","孙刘"] }
-
创建actions,修改store.js,引入vue-resource,actions是通过提交mutations来修改状态,所以在mutations增加一个getNames方法
state里面增加一个data字段记录数据,actions增加一个getData方法。getDate有一个默认的context参数,这个参数具有跟store实例相同的属性和方法通过调用context.commit提交一个mutation,或者我们可以通过es6里面的结构赋值传入一个{commit},第二个参数 _param是我们分发action传入的参数import Vue from 'vue';import vuex from 'vuex';import VueResource from 'vue-resource';Vue.use(VueResource);Vue.use(vuex);export default new vuex.Store({ state:{ time:new Date().getTime(), data:[], names:["张三"] }, mutations:{ pushName (state,thisName){ state.names.push(thisName.name) }, getNames(state,data){ state.data=data.list; } }, getters:{ getNameLen:state=>{ return state.names.length } }, actions:{ getData({ commit },_param){ return Vue.http.get('/dataweb/getName',{ params:_param.data }).then((respons)=>{ commit({ type:'getNames', list:respons.data }); }) } } })
-
修改app.vue组件,template里面增加一个按钮
{ { values }}
data数据里面增加一个_data存储数据,methods里面增加一个searchName方法,派发getDate事件,并且传入data参数,actions
的方法返回的promise,可以被store.dispatch
处理,并且store.dispatch
仍旧返回一个promise
data(){ return { msg: 'Welcome to Your App.vue components', addName: '', nameData:[], } }, methods: { searchName(){ this.$store.dispatch({ type:'getData', data:{ time:0 } }).then(()=>{ this.nameData=this.$store.state.data },()=>{ console.log('获取数据失败') }) } }
从上面的例子我们了解到,点击一个按钮发生了这些操作;
定义一个getData的action 异步调用一个请求,请求成功,commit一个getNames的mutations,修改state的data数据
执行searchName事件,用
$store.dispatch
派发一个getData
的actions,并且传入data:{time:0}
的参数dispatch返回的promise成功,就表示getDate里面的commit已经执行完毕,state.data已经有数据,更新当前this._nameData,循环输出数据
在methods派发action除了实例的这种以对象形式派发,还有三种
-
以载荷方式
this.$store.dispatch('increment',{ data:{ time:1 } })
-
在methods里面使用 mapActions 方式 映射action
import {mapActions} from 'vuex'; //数组形式 调用直接this.getDate ...mapActions([ 'getData' ]), //对象形式 this.searchName 等同于 this.getData ...mapActions({ searchName:'getData' })
修改methods里面的searchName方法
searchName(){ this.getDate({ data:{ time:0 } }).then(()=>{ this.nameData=this.$store.state.data },()=>{ console.log('数据失败') }) }
vuex还有一个Modules概念,本篇文章就介绍这么多了,等学习完modules,在补充。。。。。