[vue]初探vue生态核心插件Vuex
rencoo 人气:0为什么会有 Vuex 这个东西 ?
一个应用内部运行的机制,事件 -> 状态 -> UI,我们的前端常常会因为这两个过程而产生大量代码,从而变得难以维护。
vue的声明式渲染,解决了从 状态 和 UI 的同步问题,从而使我们不需要由于状态发生改变去写大量的命令式改变 dom 的代码。
而类似于 vuex 这类状态管理的库,则解决了 事件 -> 状态 这个过程的维护问题。这类库所做的事情就是管理从 事件源映射到状态变化 这个过程(将这个映射过程从视图组件中剥离出来,组织好这一部分的代码,在组件外部进行状态的管理)
Vuex与全局对象的区别
其实,vuex
与全局对象有一定的共同之处,那就是状态会被全局共享,无论是嵌套多少组件…
每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同:
- Vuex 的状态存储是 响应式 的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到 **高效更新 **。
- 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
Vuex常见的应用场景
管理状态与共享状态
应用简单时,可以使用 prop
与 event
来完成 父子组件 的通信。使用 global event bus(event bus)
来实现 简单的非父子组件之间的跨组件 通信。但对于 **多层级组件嵌套 **等较为复杂的场景,使用 vuex
能更好地应对。(使用 event bus 的缺点是当状态较复杂,调用组件非常多,要挨个依次通知所有组件更新;每个组件对这个组件进行的状态更新都要通知所有组件;这样会变得非常复杂)
vuex
状态管理模型,拥有一个统一的数据中心Store,Store用来维护状态数据;每个组件进行更新的时候就通知数据中心,数据中心改变后,再去触发每一个调用它的组件进行更新(相当于由数据中心来统筹状态变化以及状态变化的分发,而不是由每个vue
组件直接去操作state)
vuex
是通过将 state
作为数据中心,各个组件 共享 state 来实现跨组件通信的, 此时的数据完全独立于组件。(点击不同模块的操作,不再需要去发送乱七八糟的事件, 只是去调用事件中心里的 mutation
动作, 从而实现模块间共享状态的功能)
需要构建一个中大型单页应用时,很可能会考虑如何更好地 在组件外部 管理状态
vuex更多地用于解决 **跨组件通信 **(多层嵌套组件之间的通信问题)以及作为 数据中心集中式存储数据 (管理应用中错综复杂的状态关系)
vuex 作为数据存储中心
vuex
的 state
在单页应用的开发中本身具有一个 数据库 的作用,可以将组件用到的数据存储在 state
中,并在 actions
中封装数据读写的逻辑。目前主要有两种数据会使用 vuex
进行管理
- 组件之间 全局共享 的数据
- 通过后端异步请求的数据
实际项目开发中更多的是用到第二种,即把通过后端异步请求的数据都纳入 vuex
状态管理,在 actions
中封装数据的增删改查等逻辑,这样可以在一定程度上对前端的逻辑代码进行分层,使组件中的代码更多地关注页面交互与数据渲染等 视图层 的逻辑,而异步请求与状态数据的持久化等则交由 vuex
管理
一般全局数据,会使用到 vuex
来管理。比如 用户数据,系统数据 等,这些数据很多组件中都会使用,我们当然可以每次使用的时候都去请求,但是出于程序员的“洁癖”、“抠”等等优点,还是希望一次请求,到处使用。
这时候很自然的想到存储在 localStorage
中,但是有个问题是,这些数据可能会变,如果没能及时 同步 的话,就会用到不正确的数据,即使做了数据同步,但是 localStorage
中的数据不是响应式的,不能自动更新使用到这些数据的地方。这时候就想要开始使用 vuex
了。
Vuex代码的组织方式
与 vue-router
类似,有非模块化写法与模块化写法(其实无论何种写法本质上是一样的,目的就是导出一份 router
或者 store
的配置数据)
以管理 count
与 用户信息 userinfo
为例,介绍 vuex
代码的组织方式
核心概念 state,getter,mutation,action,module
vuex
需要遵守的规则
应用层级的状态应该集中到 单个 store
对象中
mutation
是直接操作state的方法(唯一能改变状态的方法),过程要求必须同步
action
通过commit
去触发mutation
,从而间接修改状态,优点是允许异步逻辑,
非模块化写法
// 1.安装 vuex
// 2.在入口文件中引入
// main.js
import Vuex from 'vuex'
// 3.Vue使用 vuex 插件
Vue.use(Vuex)
// 4.生成数据管理中心 store
const store = new Vuex.Store({
state: {
userinfo: null, // 需要给定初始值
count: 0
},
// 直接通过mutation方法来mutate操作state; 只能以同步的方式; mutation方法需要通过commit来触发
mutations: {
userinfo: function (state, payload) {
state.userinfo = options
localStorage.userinfo = JSON.stringify(state.userinfo)
},
increment: function (state, payload) {
state.count += payload.amount
}
},
// 通过commit触发mutations里的mutation方法, 以此间接修改 state; 允许异步操作;action方法需要通过dispatch来触发
actions: {
increment: function (context, payload) {
context.commit('increment', payload)
},
incrementAsync: function (context) {
// 异步请求数据
setTimeout(() => {
var amount = 10; // 模拟异步请求得到数据
context.commit('increment', { amount })
}, 1000)
},
async userinfo (context) {
let response = await getUserInfo() // 异步请求后端数据 方法需要 import
if (response.ok) {
let json = await response.json()
context.commit('userinfo', json)
}
}
},
getters: {// 处理、过滤数据
}
})
// 5.通过 store 配置参数注入状态管理,从而任何组件可通过 this.$store 访问状态中心
new Vue({
el: '#app',
store, // (*)
render: function (h) {
return h(App)
}
})
模块化写法
// 1.安装vuex
// 2.引入
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
// import ...
// 3.Vue使用 vuex 插件
Vue.use(Vuex)
// 4.分模块生成数据管理中心
// 配置 userinfo 状态模块 (还能再单独拆出一个文件,然后import进来)
const moduleA = {
state: {
userinfo: null // 需要初始化响应式数据
},
mutations: {
userinfo: function (state, options) {
state.userinfo = options
localstorage.userinfo = JSON.stringify(state.userinfo)
}
},
actions: {
async userinfo (context) {
async userinfo (context) {
let response = await response.json()
if (response.ok) {
let json = await response.json()
context.commit('userinfo', json)
}
}
},
getters: {// 处理、过滤数据
}
}
}
// 配置 count 状态模块 (还能再单独拆出一个文件,然后import进来)
const moduleB = {
state: {
count: 1,
},
mutations: {
increment:function (state, payload) {
state.count += payload
},
},
actions: {
increment: function (context, payload) {
context.comit('increment', payload)
},
incrementAsync: function (context) {
// 异步请求数据
setTimeout(() => {
var amount = 10; // 模拟异步请求得到数据
context.commit('increment', { amount })
}, 1000)
},
},
getters: {
doubleCount (state) {
return state.count * 2
}
}
}
export default new Vuex.Store({
modules: { // 这里与非模块化写法有点不一样;原来整个对象是一份配置...
a: moduleA,
b: moduleB,
}
})
// store.state.a // -> moduleA 的状态
// store.state.b // -> moduleB 的状态
在不同组件中使用或操作状态
// 根组件
// App.vue
// 在生命周期中触发全局共享数据的获取
...
mounted () {
if (!this.$store.state.userinfo) { // this.$store.state.a.userinfo 模块化写法的话
this.$store.dispatch('userinfo')
}
}
// 使用 state 的数据
computed: {
userinfo () {
return this.$store.state.userinfo
// return this.$store.state.a.userinfo // 模块化写法的话
},
count () {
return this.$store.state.count
// return this.$store.state.b.count // 模块化写法的话
}
}
// 子组件
// NavBar.vue
...
// 改变 state 的数据
methods: {
// 使用 commit 触发 mutations 中的 mutation 方法,直接修改 state 中的数据
addOne () {
this.$store.commit('increment', { amount: this.price })
},
// 使用 dispatch 触发 actions 中的 action 方法;异步修改 state 中的数据
addTenAsync () {
this.$store.dispatch('incrementAsync')
}
}
// 使用 state 的数据
computed: {
userinfo () {
return this.$store.state.userinfo
// return this.$store.state.a.userinfo // 模块化写法的话
}
}
// 路由页面组件
// Manage.vue
...
// 改变 state 的数据
methods: {
addTwo () {
this.$store.commit('increment', { amount: this.price })
}
}
// 使用 state 的数据
computed: {
count () {
return this.$store.state.count
// return this.$store.state.b.count // 模块化写法的话
}
},
总之,使用了 vuex
来管理状态,点击不同模块的操作,不再需要去发送乱七八糟的事件, 只是去调用事件中心里的 mutation
动作, 从而实现模块间共享状态的功能;修改一处,全局共享(无论是组件还是路由页面组件都能同步)
加载全部内容