用vue-cli 与vuex一步一步搭建一个笔记应用(三)
前面我们仅仅只是搭建了一个简单的界面,并没有使用vuex来进行数据管理。下面我们开始怎么使用vuex,因为我也是初学,所以一边学一边写吧。vuex有中文文档:其实讲得很清除 https://vuex.vuejs.org/zh-cn第一步 安装vuexcnpm install vuex -g --save-dev 为什么要使用vuex?在vuex文档里写得很清楚,一个vue就是这样的,有dat
前面我们仅仅只是搭建了一个简单的界面,并没有使用vuex来进行数据管理。
下面我们开始怎么使用vuex,因为我也是初学,所以一边学一边写吧。
vuex有中文文档:其实讲得很清除 https://vuex.vuejs.org/zh-cn
第一步 安装vuex
cnpm install vuex -g --save-dev
为什么要使用vuex?
在vuex文档里写得很清楚,一个vue就是这样的,有data()(状态),有模板template(视图),有方法methods(状态变化)
new Vue({
// state
data () {
return {
count: 0
}
},
// view
template: `
<div>{{ count }}</div>
`,
// actions
methods: {
increment () {
this.count++
}
}
})
这个状态自管理应用包含以下几个部分:
state,驱动应用的数据源;
view,以声明方式将state映射到视图;
actions,响应在view上的用户输入导致的状态变化。
以下是一个表示“单向数据流”理念的极简示意:
但是这个仅仅是vue组件内部的状态。
那么多个组件共享状态又该怎么办呢?于是vue提出了vuex。
但是,当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:
- 多个视图依赖于同一状态。
- 来自不同视图的行为需要变更同一状态。
对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。
因此,我们为什么不把组件的共享状态抽取出来,以一个全局单例模式管理呢?在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为!
Vuex 把状态分成组件内部状态和应用级别状态:
- 组件内部状态:仅在一个组件内使用的状态(data 字段)
- 应用级别状态:多个组件共用的状态
Vuex 要解决的就是这些问题,Vuex 背后有四个核心的概念:
- 状态树: 包含所有应用级别状态的对象
- Getters: 在组件内部获取 store 中状态的函数
- Mutations: 修改状态的事件回调函数
- Actions: 组件内部用来分发 mutations 事件的函数
Vuex流图:
对这张图的理解
- 数据流动是单向的
- 组件可以调用 actions
- Actions 是用来分发 mutations 的
- 只有 mutations 可以修改状态
- store 是反应式的,即,状态的变化会在组件内部得到反映
测试
vuex应用的核心是仓库store,有两点值得说明,
1、vuex状态存储是响应式的;
2、不能直接改变store中的状态,唯一途经是显式的提交mutation(mutation的中文翻译是突变)
按照vuex文档中的代码测试了一下,写在main.js中,这里定义了一个vuex 的store,有状态state,和mutation,这里的mutation我的理解是方法,可以去改变state的方法。在外部通过调用mutation来改变state存储的数据。
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++
}
}
});
store.commit('increment')
console.log(store.state.count) // -> 1
vuex概念解析
还是原来的store
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++
},
decrement: state => state.count--
}
});
Vuex 通过 store 选项,提供了一种机制将状态从根组件『注入』到每一个子组件中(需调用 Vue.use(Vuex)):
在根目录注入store
new Vue({
el: '#app',
template: '<App/>',
store,
components: {
App
}
})
在我们的app.vue中通过this.$store调用,可以看到这里并没有import store,而且如果写成了store.commit(‘increment’),会报错说store没有定义
app.vue代码:
template部分
<button @click="increment">+</button>
<button @click="decrement">-</button>
<p>{{count}}</p>
script部分:
<script>
import Hello from './components/Hello'
import Toolbar from './components/Toolbar.vue'
import NoteList from './components/NoteList.vue'
import Editor from './components/Editor.vue'
export default {
name: 'app',
components: {
Toolbar,
NoteList,
Editor
},
computed:{
count(){
return this.$store.state.count
}
},
methods: {
increment () {
this.$store.commit('increment')
},
decrement () {
this.$store.commit('decrement')
}
}
}
</script>
state
如上面的例子,从 store 实例中读取状态最简单的方法就是在计算属性computed中返回某个状态
getters
有时候我们需要从 store 中的 state 中派生出一些状态,例如对列表进行过滤并计数:
const store = new Vuex.Store({
state: {
count: 0,
todos: [{
id: 1,
text: '...',
done: true
}, {
id: 2,
text: '...',
done: false
}]
},
比如store是这么写的,我们只取done为true的todos,如果在每个组件中获取状态,上面已经提过了通过computed获取,这就需要在每个组件中都写到这个doneTodosCount()
computed: {
doneTodosCount () {
return this.$store.state.todos.filter(todo => todo.done).length
}
}
Vue提供getters,用于专门处理数据,并提供数据的,就可以这么写,过滤函数写在store里面。
const store = new Vuex.Store({
state: {
count: 0,
todos: [{
id: 1,
text: '...',
done: true
}, {
id: 2,
text: '...',
done: false
}]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
},
mutations: {
increment(state) {
state.count++
},
decrement: state => state.count--
}
});
console.log(store.getters.doneTodos);
getters也可以把getters作为参数,读取其他的getters,这里我们可以理解为getters返回的其实是一个对象
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
},
doneTodosCount: (state, getters) => {
return getters.doneTodos.length;
}
},
在其他组件的使用,不外乎就是把store变为 this.$store
computed: {
doneTodosCount () {
return this.$store.getters.doneTodosCount
}
}
mutations
更改 Vuex 的 store 中的状态的唯一方法是提交 mutations,前面的例子也展示increment和decrement。
Vuex 中的 mutations 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。
可以理解为mutation监听store中的state,并处理返回;
还是应该叫做通过mutation处理store中的state。
你不能直接调用一个 mutation handler。这个选项更像是事件注册:“当触发一个类型为 increment 的 mutation 时,调用此函数。”要唤醒一个 mutation handler,你需要以相应的 type 调用 store.commit 方法:
也就是说一个mutation相当于事件名,写在store里面,就相当于注册了一个事件,至于什么时候调用这个事件,由store.commit决定
另外文档中特别强调:在 Vuex 中,mutation 都是同步事务:
Actions
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
感觉像是对mutations的再封装呢,就像getters对state的重新封装一样。
Action 通过 store.dispatch 方法触发
store.dispatch('increment')
同时actions支持异步
这里写代码片
moudules
使用单一状态树,导致应用的所有状态集中到一个很大的对象。但是,当应用变得很大时,store 对象会变得臃肿不堪。
为了解决以上问题,Vuex 允许我们将 store 分割到模块(module)。每个模块拥有自己的 state、mutation、action、getters、甚至是嵌套子模块——从上至下进行类似的分割:
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
这样每个store模块维持一些状态和函数
对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态。
const moduleA = {
state: { count: 0 },
mutations: {
increment (state) {
// state 模块的局部状态
state.count++
}
},
getters: {
doubleCount (state) {
return state.count * 2
}
}
}
同样,对于模块内部的 action,context.state 是局部状态,根节点的状态是 context.rootState,
对于模块内部的 getter,根节点状态会作为第三个参数:
模块动态注册
在 store 创建之后,你可以使用 store.registerModule 方法注册模块:
store.registerModule('myModule', {
// ...
})
嗯,感觉这样很方便呢
以上就是对Vuex的一些简单知识的学习,下一步就是在项目中运用进去了。
更多推荐
所有评论(0)