资讯专栏INFORMATION COLUMN

vuex笔记

lavor / 2686人阅读

摘要:但是,当应用变得很大时,对象会变得臃肿不堪。允许我们将分割到模块。每个模块拥有自己的甚至是嵌套子模块。你可以通过添加前缀或后缀的方式隔离各模块,以避免名称冲突。

前言:在使用vue开发的时候数据一版通过事件分发的方式进行,在vue1.x中组件中数据交互主要包含有:

子组件事件派发:$emit(向父级),$dispatch(沿着父级向上冒泡)

父组件通过$on监听到之后进行相应的操作

当有兄弟组件需要监听事件,父组件通过$broadcast向下广播。

vue2.x中取消了$dispatch,$broadcast,要实现组件之前的交互就非常蛋疼了,首先要不停的通过$emit派发到需要获取到这个事件的父级,然后父级通过$ref来调用相应的子组件的方法,单想想就容易心态爆炸。解决办法有2:

在根目录下data里新建一个vue对象作为eventHander,所有的事件通过这个新建的vue对象进行监听派发,具体就不进行描述了送上关键字:this.$root.eventHander.$on、this.$root.eventHander.$emit;

全局状态管理工具vuex,什么是vuex?借助官网的一句话:采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。;

vuex简介
借助一张图片,简短的对vuex的状态管理进行一个描述

从上图可以看出vuex主要包含actions、mutations、state

交互过程中,渲染:vue components根据state进行渲染,响应:通过触发actions相关事件调用mutations相关函数,mutations对state进行改变,state的改变引发vue components的重绘

状态的统一管理对大型项目提供了相当多的便利,数据的操作都可以通过vuex来进行,小型项目组件并不复杂,数据的交互层级不高,因此并不建议使用vuex进行数据交互

在对vuex进行描述之前送上vuex官网飞机票一张

核心概念之state

state里存储所有应用层级的信息。是作为唯一的一个数据源存在.

state定义
    const store = new Vuex.Store({
        state: {
            count: 0
        }
    })
    //上面的例子就定义了一个state,state存有一个变量count  
state获取
    //网页直接通过script标签引入的方式不需要进行注册,模块化构建一定要进行Vue.use(Vuex)组件注册

    //...state定义
    //ps:注意,state相关数据是写在computed内,不是写在data内
    new Vue({
            el: "#app",
            template: `
{{count}}
` computed: { count() { return this.$store.state.count } }, store }) //首先将store注入到组件内部,调用可直接通过this.$store.state获取相应的值 //当数组需要获取多个状态值时this.$store.state前缀就需要写的很多,容易冗余,利用mapState辅助函数可以很好的解决这个问题 import { mapState } from "vuex" //在直接标签引入的情况下用mapState = vuex.mapState; computed: mapState({ count: state => state.count, countAlias: "count", // 为了能够使用 `this` 获取局部状态,必须使用常规函数,因为箭头函数会进行this绑定 countPlusLocalState (state) { return state.count + this.localCount } }) //当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState 传一个字符串数组 computed: mapState([ // 映射 this.count 为 store.state.count "count" ]) //当需要与当前组件属性共同使用的时候,可采用es6/7的对象展开符 computed: { localComputed () { /* ... */ }, // 使用对象展开运算符将此对象混入到外部对象中 ...mapState({ // ... }) }
核心概念之mutations

更改store中的state的唯一方法就是通过mutation,每个mutation都像是一个事件监听,等待调用的观察者。其由一个事件类型和一个回调函数构成,回调函数对state进行修改,事件类型名称作为事件调用的名称;

    //声明
    const store = new Vuex.Store({
        //...
        mutations: {
            increment (state) {
            // 变更状态
            state.count++
            }
        }
    })
    //唤起
    store.commit("increment")
提交荷载

提交荷载可以理解为store.commit传递的额外参数

    // ...
    mutations: {
        increment (state, n) {
            state.count += n
        }
    }

    store.commit("increment", 10)

    //在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读:

    // ...
    mutations: {
        increment (state, payload) {
            state.count += payload.amount
        }
    }
    store.commit("increment", {
        amount: 10
    })

    //对象风格的提交方式

    store.commit({
        type: "increment",
        amount: 10
    })
相应规则

最好提前在你的 store 中初始化好所有所需属性。

当需要在对象上添加新属性时,你应该

使用Vue.set(state.obj,"newProp","xxx")

state.obj = { ...state.obj, newProp: 123 }

使用常量代替mutation事件类型

这么做的好处是对state的操作接口一目了然,全部展现在mutation-types文件夹中,便于大项目的协作。当然这不是必须的。

    // mutation-types.js
    export const SOME_MUTATION = "SOME_MUTATION"
    // store.js
    import Vuex from "vuex"
    import { SOME_MUTATION } from "./mutation-types"

    const store = new Vuex.Store({
    state: { ... },
    mutations: {
        // 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名
        [SOME_MUTATION] (state) {
            // mutate state
            }
        }
    })
mutation必须是同步函数

因为异步函数会导致状态的不确定性,造成运行和调试的出路,这里就不进行展开了。

组件的mapMutations
    import { mapMutations } from "vuex"

    export default {
        // ...
        methods: {
            ...mapMutations([
                //type1
                "increment" // 映射 this.increment() 为 this.$store.commit("increment")
            ]),
                //type2
            ...mapMutations({
                add: "increment" // 映射 this.add() 为 this.$store.commit("increment")
            })
        }
    }
核心概念之actions

上一章我们有提到mutation是没有异步操作的,因此异步操作需要用到action,注意action提交的mutation而不是直接处理state

    const store = new Vuex.Store({
        //...
        actions: {
            //context与store对象具有相同的方法和参数,但。。不是store本身
            increment (context) {
                context.commit("increment")
            }
        },
        //也可以写成
        actions: {
            increment ({ commit }) {
                commit("increment")
            }
        }
    })
分发 Action
    store.dispatch("increment");

    //action同样支持荷载方式,和mutation差不多

    // 以载荷形式分发
    store.dispatch("incrementAsync", {
        amount: 10
    })

    // 以对象形式分发
    store.dispatch({
        type: "incrementAsync",
        amount: 10
    })
组件中分发 Action
    import { mapActions } from "vuex"

    export default {
        // ...
        methods: {
            ...mapActions([
                "increment" // 映射 this.increment() 为 this.$store.dispatch("increment")
            ]),
            ...mapActions({
                add: "increment" // 映射 this.add() 为 this.$store.dispatch("increment")
            })
        }
    }
组合 Actions

因为action是异步的,因此组合多个action将是一个大问题,要解决这个问题这里引用了promise对象。通过dispath返回的promise对象进行操作,达成顺序执行

//A进行promise对象返回
actions: {
  actionA ({ commit }) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        commit("someMutation")
        resolve()
      }, 1000)
    })
  }
}
//通过dispatch后的返回值执行异步操作
store.dispatch("actionA").then(() => {
  // ...
})

//actions之间的互相调用

actions: {
  // ...
  actionB ({ dispatch, commit }) {
    return dispatch("actionA").then(() => {
      commit("someOtherMutation")
    })
  }
}


//也可以通过async/await属性组合(await只能在async函数内部运行)action:
actions: {
  async actionA ({ commit }) {
    commit("gotData", await getData())
  },
  async actionB ({ dispatch, commit }) {
    await dispatch("actionA") // 等待 actionA 完成
    commit("gotOtherData", await getOtherData())
  }
}
核心概念之getter

有时候我们需要从 store 中的 state 中派生出一些状态(即对state进行一些操作过后得到的状态),例如对列表进行过滤并计数:

    //常规做法,当其他component需要用到时代码就会出现冗余
    computed: {
        doneTodosCount () {
            return this.$store.state.todos.filter(todo => todo.done).length
        }
    }
    //Vuex 允许我们在 store 中定义『getters』(可以认为是 store 的计算属性)。Getters 接受 state 作为其第一个参数

    const store = new Vuex.Store({
        state: {
            todos: [
            { id: 1, text: "...", done: true },
            { id: 2, text: "...", done: false }
            ]
        },
        getters: {
            doneTodos: state => {
            return state.todos.filter(todo => todo.done)
            }
        }
    })
    //Getters 也可以接受其他 getters 作为第二个参数
    getters: {
        // ...
        doneTodosCount: (state, getters) => {
            return getters.doneTodos.length
        }
    }
    store.getters.doneTodosCount // -> 1
    //组件中使用
    computed: {
        doneTodosCount () {
            return this.$store.getters.doneTodosCount
        }
    }
mapGetters 辅助函数

和之前一样的辅助映射函数一毛一样

    import { mapGetters } from "vuex"

    export default {
        // ...
        computed: {
        // 使用对象展开运算符将 getters 混入 computed 对象中
            ...mapGetters([
            "doneTodosCount",
            "anotherGetter",
            // ...
            ])

            mapGetters({
            // 映射 this.doneCount 为 store.getters.doneTodosCount
            doneCount: "doneTodosCount"
            })
        }
    }
核心概念之module

使用单一状态树,导致应用的所有状态集中到一个很大的对象。但是,当应用变得很大时,store 对象会变得臃肿不堪。

Vuex 允许我们将 store 分割到模块(module)。每个模块拥有自己的 state、mutation、action、getters、甚至是嵌套子模块。

module定义
    //状态篇

    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 的状态

module局部状态

对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态。

    //即此状态为moduleA的state
    const moduleA = {
    state: { count: 0 },
    mutations: {
        increment (state) {
        // state 模块的局部状态
        state.count++
        }
    },
    //对于模块内部的 getter,根节点状态会作为第三个参数:
    getters: {
        doubleCount (state, getters, rootState) {
            return state.count * 2
            }
        }
    },

    // 同样,对于模块内部的 action,context.state 是局部状态,根节点的状态是 context.rootState这就是之前action的context不等于当前store对象的原因
    actions: {
        incrementIfOddOnRootSum ({ state, commit, rootState }) {
            if ((state.count + rootState.count) % 2 === 1) {
                commit("increment")
            }
        }
    }
命名空间

模块内部的 action、mutation、和 getter 现在仍然注册在全局命名空间——这样保证了多个模块能够响应同一 mutation 或 action。你可以通过添加前缀或后缀的方式隔离各模块,以避免名称冲突。你也可能希望写出一个可复用的模块,其使用环境不可控。例如,我们想创建一个 todos 模块:

    // types.js

    // 定义 getter、action、和 mutation 的名称为常量,以模块名 `todos` 为前缀
    export const DONE_COUNT = "todos/DONE_COUNT"
    export const FETCH_ALL = "todos/FETCH_ALL"
    export const TOGGLE_DONE = "todos/TOGGLE_DONE"
    // modules/todos.js
    import * as types from "../types"

    // 使用添加了前缀的名称定义 getter、action 和 mutation
    const todosModule = {
    state: { todos: [] },

    getters: {
        [types.DONE_COUNT] (state) {
        // ...
        }
    },

    actions: {
        [types.FETCH_ALL] (context, payload) {
        // ...
        }
    },

    mutations: {
        [types.TOGGLE_DONE] (state, payload) {
        // ...
        }
    }
    }

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/82532.html

相关文章

  • Vuex 构建一个笔记应用

    摘要:如果不熟悉,在这个教程里面,我们会通过构建一个笔记应用来学习怎么用。这个是我们要构建的笔记应用的截图你可以从下载源码,这里是的地址。每当用户点击笔记列表中的某一条时,组件会调用来分发这个会把当前选中的笔记设为。 原文:Learn Vuex by Building a Notes App,有删改。 本文假设读者熟悉 Vuex 文档 的内容。如果不熟悉,you definitely sho...

    gggggggbong 评论0 收藏0
  • 使用 Vuex + Vue.js 构建单页应用

    摘要:鉴于该篇文章阅读量大,回复的同学也挺多的,特地抽空写了一篇下的使用方法,传送门使用构建单页应用新篇华丽的分割线原文地址前言在最近学习的时候,看到国外一篇讲述了如何使用和来构建一个简单笔记的单页应用的文章。 鉴于该篇文章阅读量大,回复的同学也挺多的,特地抽空写了一篇 vue2.0 下的 vuex 使用方法,传送门:使用 Vuex + Vue.js 构建单页应用【新篇】 ---------...

    tomorrowwu 评论0 收藏0
  • 使用 Vuex + Vue.js 构建单页应用

    摘要:鉴于该篇文章阅读量大,回复的同学也挺多的,特地抽空写了一篇下的使用方法,传送门使用构建单页应用新篇华丽的分割线原文地址前言在最近学习的时候,看到国外一篇讲述了如何使用和来构建一个简单笔记的单页应用的文章。 鉴于该篇文章阅读量大,回复的同学也挺多的,特地抽空写了一篇 vue2.0 下的 vuex 使用方法,传送门:使用 Vuex + Vue.js 构建单页应用【新篇】 ---------...

    cnsworder 评论0 收藏0
  • 使用 Vuex + Vue.js 构建单页应用

    摘要:鉴于该篇文章阅读量大,回复的同学也挺多的,特地抽空写了一篇下的使用方法,传送门使用构建单页应用新篇华丽的分割线原文地址前言在最近学习的时候,看到国外一篇讲述了如何使用和来构建一个简单笔记的单页应用的文章。 鉴于该篇文章阅读量大,回复的同学也挺多的,特地抽空写了一篇 vue2.0 下的 vuex 使用方法,传送门:使用 Vuex + Vue.js 构建单页应用【新篇】 ---------...

    levius 评论0 收藏0
  • 使用 Vuex + Vue.js 构建单页应用

    摘要:鉴于该篇文章阅读量大,回复的同学也挺多的,特地抽空写了一篇下的使用方法,传送门使用构建单页应用新篇华丽的分割线原文地址前言在最近学习的时候,看到国外一篇讲述了如何使用和来构建一个简单笔记的单页应用的文章。 鉴于该篇文章阅读量大,回复的同学也挺多的,特地抽空写了一篇 vue2.0 下的 vuex 使用方法,传送门:使用 Vuex + Vue.js 构建单页应用【新篇】 ---------...

    UsherChen 评论0 收藏0
  • 使用 Vuex + Vue.js 构建单页应用

    摘要:鉴于该篇文章阅读量大,回复的同学也挺多的,特地抽空写了一篇下的使用方法,传送门使用构建单页应用新篇华丽的分割线原文地址前言在最近学习的时候,看到国外一篇讲述了如何使用和来构建一个简单笔记的单页应用的文章。 鉴于该篇文章阅读量大,回复的同学也挺多的,特地抽空写了一篇 vue2.0 下的 vuex 使用方法,传送门:使用 Vuex + Vue.js 构建单页应用【新篇】 ---------...

    YJNldm 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<