[vue]初探vue生态核心插件Vuex

为什么会有 Vuex 这个东西 ?

现代前端框架主要解决的是 事件 -> 状态 -> UI 将传统前端在两个过程的代码剥离出来,变得更加容易维护;

vue的声明式渲染,解决了 状态 和 UI 的同步问题,从而使我们不需要由于状态发生改变去写大量的命令式改变 dom 的代码。

而类似于 vuex 这类状态管理的库,则解决了 事件 -> 状态 这个过程的维护问题。这类库所做的事情就是管理从 事件源映射到状态变化 这个过程(将这个映射过程从视图组件中剥离出来,组织好这一部分的代码,在组件外部进行状态的管理)

具体表现就是一个全局的数据中心 store 配置,每个组件进行更新的时候就通知数据中心,数据中心改变后,再去触发每个调用它的组件进行更新(这种更新是响应式的);几个核心概念就是 mutations里的方法可以直接 mutatestore 中的状态,并且 mutation 过程必须同步的,需要通过 commit 去触发;而 actions 则允许异步的操作,通过 commit 去触发 mutation ,达到间接修改 store的目的,action 本身需要通过 disptch去触发。

Vuex与全局对象的区别

其实,vuex 与全局对象有一定的共同之处,那就是状态会被全局共享,无论是嵌套多少组件…

每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同:

  1. Vuex 的状态存储是 响应式 的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到 高效更新
  2. 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。

Vuex常见的应用场景

管理状态与共享状态

应用简单时,可以使用 propevent 来完成 父子组件 的通信。使用 global event bus(event bus)来实现 简单的非父子组件之间的跨组件 通信。但对于 多层级组件嵌套 等较为复杂的场景,使用 vuex 能更好地应对。(使用 event bus 的缺点是当状态较复杂,调用组件非常多,要挨个依次通知所有组件更新;每个组件对这个组件进行的状态更新都要通知监听该事件的所有组件;这样会变得非常复杂)(你通知我, 我通知你, 你通知他, 他通知我 ... 大家之间是直接相互作用的)

vuex 状态管理模型,拥有一个统一的数据中心Store,Store用来维护状态数据;每个组件进行更新的时候就通知数据中心,数据中心改变后,再去统一触发每一个调用它的组件进行更新(相当于由数据中心来统筹状态变化以及状态变化的分发,而不是由每个vue组件直接去操作state) (有点类似于中介者模式, 大家把所有各种类型的信息都反馈给中介, 然后再由中介分发给订阅了某个类型信息的用户)

图解图解

vuex 是通过将 state 作为数据中心,各个组件 共享 state 来实现跨组件通信的, 此时的数据完全独立于组件。(点击不同模块的操作,不再需要去发送乱七八糟的事件, 只是去调用事件中心里的 mutation 动作, 从而实现模块间共享状态的功能)

需要构建一个中大型单页应用时,很可能会考虑如何更好地 在组件外部 管理状态

vuex更多地用于解决 跨组件通信 (多层嵌套组件之间的通信问题)以及作为 数据中心集中式存储数据 (管理应用中错综复杂的状态关系)

vuex 作为数据存储中心

vuexstate在单页应用的开发中本身具有一个 数据库 的作用,可以将组件用到的数据存储在 state中,并在 actions中封装数据读写的逻辑。目前主要有两种数据会使用 vuex进行管理

  1. 组件之间 全局共享 的数据
  2. 通过后端异步请求的数据

实际项目开发中更多的是用到第二种,即把通过后端异步请求的数据都纳入 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: {
        userinfonull// 需要给定初始值
        count: 0
    },
    // 直接通过mutation方法来mutate操作state; 只能以同步的方式; mutation方法需要通过commit来触发
    mutations: {
        userinfofunction (state, payload{
              state.userinfo = options
            localStorage.userinfo = JSON.stringify(state.userinfo)
        },
        incrementfunction (state, payload{
            state.count += payload.amount
        }
    },
    // 通过commit触发mutations里的mutation方法, 以此间接修改 state; 允许异步操作;action方法需要通过dispatch来触发
    actions: {
        incrementfunction (context, payload{
            context.commit('increment', payload)
        },
        incrementAsyncfunction (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: {
        userinfonull // 需要初始化响应式数据
    },
    mutations: {
        userinfofunction (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: {
        count1,
    },
    mutations: {
        increment:function (state, payload{
            state.count += payload
        },
    },
    actions: {
        incrementfunction (context, payload{
              context.comit('increment', payload)  
        },
          incrementAsyncfunction (context{
            // 异步请求数据
            setTimeout(() => {
                var amount = 10// 模拟异步请求得到数据
                context.commit('increment', { amount })
            }, 1000)
        },  
    },
    getters: {
        doubleCount (state) {
            return state.count * 2
        }
    }
}

// 5.导出一份 store 的配置
export default new Vuex.Store({
    modules: { // 这里与非模块化写法有点不一样;原来整个对象是一份配置...
        a: moduleA,
        b: moduleB,
    }
})

// store.state.a // -> moduleA 的状态
// store.state.b // -> moduleB 的状态

// 6.在根脚本 main.js 中引入 store 配置
import store from './store'
...
// 并通过 store 配置参数注入状态管理,从而任何组件可通过 this.$store 访问状态中心
new Vue({
    el'#app',
    router,
    store,
    renderfunction (h{
        return h(App);
    }
});

在不同组件中使用或操作状态

// 根组件
// 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', { amountthis.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', { amountthis.price })
    }
}

// 使用 state 的数据
computed: {
    count () {
        return this.$store.state.count
        // return this.$store.state.b.count // 模块化写法的话
    }
},

总之,使用了 vuex 来管理状态,点击不同模块的操作,不再需要去发送乱七八糟的事件, 只是去调用事件中心里的 mutation 动作, 从而实现模块间共享状态的功能;修改一处,全局共享(无论是组件还是路由页面组件都能同步)

最后,再看看 vuex 官网的这张说明图,是不是更加清晰了呢!

虚线绿框里即vuex做的事虚线绿框里即vuex做的事

-项目地址- vuex-demo

posted @ 2019-12-16 16:35  rencoo  阅读(...)  评论(...编辑  收藏