zoukankan      html  css  js  c++  java
  • vuex--状态管理

    由于状态零散地分布在许多组件和组件之间的交互中,大型应用复杂度也经常逐渐增长。
    如果多层组件嵌套使用,传递prop,和事件emit。都很不方便。
    不方便对数据的修改进行历史记录。影响后续的调试!
    为了解决这个问题,Vue 提供 vuex。
    vuex 甚至集成到 vue-devtools,无需配置即可进行时光旅行调试。
     
    数据传递问题
    前置知识:理解什么是component!
    组件就是函数。编程就是通过组织小的函数们来解决问题!类似组件!
    于是问题就成为:如何传递arguments, 组件是怎样和它的环境互动的?
    就像parameters和返回函数中的值。
     
    在Vue中我们使用props来传递data到组件和组件事件,来和周边环境交互。
    主要使用$emit方法和v-on
     
    一个问题:
    这是很好的移动数据的办法。只是有一个组件的问题:当嵌套3+层时,交互成了困难。
    你不得不传递数据从组件A到B,然后从B到C,等等。 反向events, C emits to B, B to A等等。
    于是使用state进行状态管理出现了,但这有缺陷,所以出现了Vue
     
    简单状态管理
    经常被忽略的是,Vue 应用中原始数据对象的实际来源 。
    当访问数据对象时,一个 Vue 实例只是简单的代理访问。
    所以,如果你有一处需要被多个实例间共享的状态,可以简单地通过维护一份数据来实现共享:
    const sourceOfTruth = {}
    
    const vmA = new Vue({
      data: sourceOfTruth
    })
    
    const vmB = new Vue({
      data: sourceOfTruth
    })
    子组件的实例可以通过this.$root.$data来访问sourceOfTruth.
    问题出现:任何对数据的改变,不会留下变更过的记录。这对调试是一个噩梦!!
    因此可以使用store模式:
    var store = {
      debug: true,
      state: {
        message: 'Hello'
      },
      setMessageAction(newVaule) {
        if (this.debug) console.log('setMessageAction triggered with', newValue)
        this.state.message = newValue
      },
      clearMessageAction() {
        if (this.debug) console.log('clearMessageAction triggered')
        this.state.message = ''
      }
    }
    store中的state的改变,都放置在store自身的函数去管理!这就是集中方式的状态管理!
    当错误出现时, 就有了log来记录bug出现之前发生了什么。
    此外,每个实例/组件,可以有自己的私有状态:
    var vma = new Vue({
      data: {
        privateState: {},
        sharedState: store.state
      }
    })
    
    var vmb = new Vue({
      data: {
        privateState: {},
        sharedState: store.state
      }
    })
     
    接下来的延伸约定:
    组件不应该直接修改属于store实例的state ,而应该执行store实例内的action函数来dispatch事件通知store去改变。
    这样约定的好处:
    可以记录所有store中发生的state的改变,同时实现能做到记录变更mutation, 保存状态快照,历史回滚/时光旅行的先进的调试工具:vuex.
     
    Vuex
    Vuex用于集中管理state。或者说是集中式的状态管理Vue依赖库。
    Vuex所做的不是在组件内协调state的数据(coordinate state's data in components),
    而是,在一个巨大的state object内,协调coordinate它们(data)。
    任何组件需要一个portion of the state, 就传递它们。
    这个state或大或小由程序决定,不过最开始就是一个空对象{ }。
     
    安装
    npm install vuex
    //或者进入ui,在网页上手动添加。
     
    //然后配置到entry point,或自建一个store.js
    import Vue from 'vue'
    import Vuex from 'vuex'
    Vue.use(Vuex)
     
    //如果自建store.js, 加上:
    export default new Vuex.Store({
      state: {},
      mutations: {},
      actions: {}
    });
     
    Vuex是一个全局对象,还有2个额外的功能:
    Vuex的state储存是响应式的。
    不可以直接改变store中的state。需要使用(commit)mutation.
     
    简单计数:
    const store = new Vuex.Store({
      state: {
        count: 0
      },
      mutations: {
        increment (state) {
          state.count++
        }
      }
    })
    
    store.commit('increment')
    store.commit('increment')
    
    store.state.count  //输出2
    核心概念:
    State: 保存组件的共享的状态。
    Getter:用于state的延伸,就是computed计算属性的功能。
    Mutation: 唯一可以改变store的状态的方法:(commit)mutation
    Action: 只能提交Mutation,可以包括async操作。
    Module: 解决单一状态树,导致对象过于庞大的问题。将store分割为多个module模块。相当于是嵌套子模块。
    State
     
    单一状态树,一个对象包含全部应用层级状态。 每个应用只有一个store实例。
    Vuex通过store选项,把状态从根组件‘注入’到子组件中。
     
    //辅助函数mapState的用法
    //在子组件中,无需每个state都声明computed属性,使用辅助函数,帮助我们生成计算属性!
     
    //计算属性?
    //如果想要在子组件得到store实例中的状态,最简单的方法就是在计算属性中返回某个状态
    const Counter = {
      template: `<div>{{ count }}</div>`,
      computed: {
        count () {
          return store.state.count
        }
      }
    }
    store.state.count变化的时候,会从新求取计算属性, 并触发更新相关的DOM。
    但是,这种模式有个缺陷!
    即简单使用store实例,集中管理的缺陷
    vue使用的是模块化的构建方法,每个需要用到state的组件都会频繁的导入store.state。过于频繁降低效能。
    Vuex通过store选项,提供了一种机制将state从根组件‘注入’到每个子组件中(调用Vue.use(Vuex))
    const app = new Vue({
      el: '#app',
      // 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件
      store,
    })
    子组件可以通过this.$store来访问state。
    例子:
    根实例vm, 子组件Counter。 v
    m中注册了store选项,获得了store对象,然后把store实例注入到Counter子组件内。
    Counter使用this.$store.state.count访问到store实例。 (我的理解是每个子组件,都有一个store实例,这样子组件内部可以反复调用这个实例状态,无需再从store对象那里取数据)
    Counter还可以使用this.$store.commit("increment")来修改store对象的state。 (如果是修改state,则调用store对象的action修改它的state)
    const store = new Vuex.Store({
      state: {
        count: 0
      },
      mutations: {
        increment (state) {
          state.count++
        }
      }
    })
    
    const  Counter = {
      template: `<div>count: {{ count }}</div>`,
      computed: {
        count() {
          return this.$store.state.count
        }
      }
    }
    
    var vm = new Vue({
      el: "#app",
      store,
      components: { Counter },
    })
     
    mapState 辅助函数
    当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性,让你少按几次键:
    import { mapState} from 'vuex'
    export default{
      computed: mapState({
         count: state => state.count
         countAlias: 'count'
    
         //为了能够使用 `this` 获取局部状态,必须使用常规函数
         countPlusLocalState(state) {
            return state.count + this.localCount
         }
    
     })
    }
    如果映射的计算属性的名字和state的子节点名字相同时,可以给mapState传递一个string数组
    We can also pass a string array to mapState when the name of a mapped computed property is the same as a state sub tree name.
    computed: mapState([
      // 映射 this.count 为 store.state.count
      // this是指子组件实例
      'count'
    ])
     
    //对象展开符号...
    //使用..., 简化了上面的代码
    computed: {
      localComputed() { /* ... */},
      ...mapState({
        // ...
      })
    }
     
    Getter
    针对state的派生的状态的操作:
    (store的计算属性),getter返回值根据它的依赖被缓存起来,只当它的依赖值改变了才会被重新计算。
    Getter接受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)
        }
      }
    })
    通过属性访问:
    store.getters对象中的函数可以访问:
    store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]
    Getter可以接受其他getter作为第二个参数:我的理解,就是store.getters对象被当作第二个参数
    const mystore = new Vuex.Store({
      //略。。
      getters: {
        doneTodos: state => {
          return state.todos.filter(todo => todo.done)
        },
        doneTodosCount: (state, getters) => {
          return getters.doneTodos.length
        }
        
      }
    })
    我们可以在任何组件中使用getters中的函数:
    const  Counter = {
      template: `<div>count: {{ count }}</div>`,
      computed: {
        count() {
          return this.$store.getters.doneTodosCount
        }
      }
    }
    
    var vm = new Vue({
      el: "#app",
      store: mystore
      components: { Counter },
    })
    通过方法访问:返回的是一个函数 (state) => { return function(){} }
    getters: {
        //..略
        getTodoById: (state) => (id) => {
          return state.todos.find(todo => todo.id === id)
        }
      },
    
    mystore.getters.getTodoById(2)   
    //->返回todo对象一个。{ id: 2, text: '...', done: false }
     
    Mutation
    mutations属性中的函数可以接受2个参数, 第一个参数是state. 第2个参数最好是一个包含record的对象
     mutations: {//添加参数payload,约定它是对象。
        incrementPayload (state, payload) {
          state.count += payload.amount
        }
      }
    Mutation必须是同步函数,不能说异步函数,因为当mutation触发时,回调函数没能被调用,那么就不能记录追踪回调函数中进行的状态的改变。
    使用action处理异步操作:
    Action 类似于 mutation,不同在于:Action 提交的是 mutation,而不是直接变更状态。
    Action 可以包含任意异步操作
    const store = new Vuex.Store({
      state: {
        count: 0
      },
      mutations: {
        increment (state) {
          state.count++
        }
      },
      actions: {
        increment (context) {
          setTimeout( () => {
            context.commit('increment')
          }, 1000)
        }
      }
    }) 
     
    Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 :
    context.commit 提交一个 mutation。
    context.state
    context.getters
    当我们在之后介绍到 Modules 时,你就知道 context 对象为什么不是 store 实例本身了。
    dispatch Action
    不受必须同步执行的限制。
    store.dispatch('increment')
    同样可以使用载荷,对象的方式分发:
    我的理解:action中异步调用mutations中的函数。actions和mutations中的函数名字一一对应。
    const store = new Vuex.Store({
      state: {
        count: 0
      },
      mutations: {
       //...略...
        incrementPayload (state, payload) {
          state.count += payload.amount 
        }
      },
      actions: {
       //...略...
        incrementPayload (context, payload) {
          setTimeout( () => {
            context.commit('incrementPayload', payload)
          }, 1000)
        }
      }
    }) 
     
    在组件中分发Action(未看)
    组合 Action(未看)
    项目结构:
    ├── index.html
    ├── main.js
    ├── api
    │   └── ... # 抽取出API请求
    ├── components
    │   ├── App.vue
    │   └── ...
    └── store
        ├── index.js          # 我们组装模块并导出 store 的地方
        ├── actions.js        # 根级别的 action
        ├── mutations.js      # 根级别的 mutation
        └── modules
            ├── cart.js       # 购物车模块
            └── products.js   # 产品模块
     
    插件
    Vuex.Store的实例方法:
    subscribe(handler: Function): Function
    hander会在每个mutation完成后调用,接收mutation和经过mutation后的state作为参数
     
     

  • 相关阅读:
    DotLucene 之 Bugs 小记[转]
    商业搜索引擎都需要哪些技术
    开源搜索引擎资源
    关于笔记本的CPU和显卡性能比较[转]
    上传图片方法大全 [网摘]
    Lucene 中文分词的 highlight 显示
    JS调用服务器端方法
    在线免费图书馆
    用dotLucene为数据库内容建立索引
    mybatis 数据库.xml中方法参考
  • 原文地址:https://www.cnblogs.com/absoluteli/p/14052220.html
Copyright © 2011-2022 走看看