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
<template>
<div id="app">
<h1>{{ msg }}</h1>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'app',
data(){
return {
msg: 'Welcome to Your App.vue components'
}
},
}
</script>
hello.vue
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<contents></contents>
</div>
</template>
<script>
import contents from './Content';
export default {
name: 'hello',
data () {
return {
msg: 'Welcome to Your Hello.vue components'
}
},
components:{
contents
}
}
</script>
content.vue
<template>
<div id="inner">
<h2>{{msg}}</h2>
</div>
</template>
<script>
export default{
name:'content',
data(){
return {
msg:'Welcome to Your Content.vue components'
}
}
}
</script>
通过上面的三个组件和路由我们可以看出,访问跟路径,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: '<App/>',
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组件,
<label>
<input type="text" v-model="addName" />
<button @click="addNameFn">添加</button>
</label>
<p>姓名:
<span v-for="name in nameArr">{{ name }} </span>
</p>
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,直接打印添加姓名的个数
<label>
<input type="text" v-model="addName"/>
<button @click="addNameFn">添加</button>
</label>
<p>姓名:
<span v-for="name in nameArr">{{ name }} </span>
</p>
<p>人数:{{ this.$store.getters.getNameLen }}</p>
修改完这些,我们发现当我们添加一个姓名,每个组件都会更新添加的姓名的总条数,getters调用就是这么简单,上面只是一种调用方式,除此外还有两种调用方式
1.在计算属性中引入
<p>人数:{{ getNameLen }}</p>
computed: {
nameCount(){
return this.$store.getters.getNameLen
}
}
2.使用 mapGetters,引入mapGetters,使用个结构赋值的方法,将getters里面的方法映射到局部计算属性上,如果是数组则跟getters里面的方法名同名,对象可以修改名称,同mapState一样
<p>人数:{{ getNameLen }}</p>
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里面增加一个按钮
<button @click="searchName">显示全部姓名</button> <p><span v-for="values in nameData">{{ values }} </span></p>
data数据里面增加一个_data存储数据,methods里面增加一个searchName方法,派发getDate事件,并且传入data参数,actions
的方法返回的promise,可以被store.dispatch
处理,并且store.dispatch
仍旧返回一个promise
getData里面的promise返回成功则更新this._data数据,失败则输出获取数据失败
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,在补充。。。。。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。