vuex笔记

675635708 4年前
   <p>前言:在使用vue开发的时候数据一版通过事件分发的方式进行,在vue1.x中组件中数据交互主要包含有:</p>    <ol>     <li> <p>子组件事件派发:$emit(向父级),$dispatch(沿着父级向上冒泡)</p> </li>     <li> <p>父组件通过$on监听到之后进行相应的操作</p> </li>     <li> <p>当有兄弟组件需要监听事件,父组件通过$broadcast向下广播。</p> </li>    </ol>    <p>vue2.x中取消了$dispatch,$broadcast,要实现组件之前的交互就非常蛋疼了,首先要不停的通过$emit派发到需要获取到这个事件的父级,然后父级通过$ref来调用相应的子组件的方法,单想想就容易心态爆炸。解决办法有2:</p>    <ul>     <li> <p>在根目录下data里新建一个vue对象作为eventHander,所有的事件通过这个新建的vue对象进行监听派发,具体就不进行描述了送上关键字:this.$root.eventHander.$on、this.$root.eventHander.$emit;</p> </li>     <li> <p>全局状态管理工具vuex,什么是vuex?借助官网的一句话:采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。;</p> </li>    </ul>    <h2>vuex简介</h2>    <pre>  <code class="language-javascript">借助一张图片,简短的对vuex的状态管理进行一个描述</code></pre>    <p style="text-align:center"><img src="https://simg.open-open.com/show/e1da1afa1bca6269e8d223393bb0e753.png"></p>    <ul>     <li> <p>从上图可以看出vuex主要包含actions、mutations、state</p> </li>     <li> <p>交互过程中,渲染:vue components根据state进行渲染,响应:通过触发actions相关事件调用mutations相关函数,mutations对state进行改变,state的改变引发vue components的重绘</p> </li>    </ul>    <p>状态的统一管理对大型项目提供了相当多的便利,数据的操作都可以通过vuex来进行,小型项目组件并不复杂,数据的交互层级不高,因此并不建议使用vuex进行数据交互</p>    <p>在对vuex进行描述之前送上vuex官网 飞机票 一张</p>    <h2>核心概念之state</h2>    <p>state里存储所有应用层级的信息。是作为唯一的一个数据源存在.</p>    <h3>state定义</h3>    <pre>  <code class="language-javascript">const store = new Vuex.Store({          state: {              count: 0          }      })      //上面的例子就定义了一个state,state存有一个变量count</code></pre>    <h3>state获取</h3>    <pre>  <code class="language-javascript">//网页直接通过script标签引入的方式不需要进行注册,模块化构建一定要进行Vue.use(Vuex)组件注册        //...state定义      //ps:注意,state相关数据是写在computed内,不是写在data内      new Vue({              el: '#app',              template: `<div class="app">{{count}}</div>`              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({              // ...          })      }</code></pre>    <h2>核心概念之mutations</h2>    <p>更改store中的state的唯一方法就是通过mutation,每个mutation都像是一个事件监听,等待调用的观察者。其由一个事件类型和一个回调函数构成,回调函数对state进行修改,事件类型名称作为事件调用的名称;</p>    <pre>  <code class="language-javascript">//声明      const store = new Vuex.Store({          //...          mutations: {              increment (state) {              // 变更状态              state.count++              }          }      })      //唤起      store.commit('increment')</code></pre>    <h3>提交荷载</h3>    <p>提交荷载可以理解为store.commit传递的额外参数</p>    <pre>  <code class="language-javascript">// ...      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      })</code></pre>    <h3>相应规则</h3>    <ol>     <li> <p>最好提前在你的 store 中初始化好所有所需属性。</p> </li>     <li> <p>当需要在对象上添加新属性时,你应该</p>      <ul>       <li> <p>使用Vue.set(state.obj,'newProp','xxx')</p> </li>       <li> <p>state.obj = { ...state.obj, newProp: 123 }</p> </li>      </ul> </li>    </ol>    <h3>使用常量代替mutation事件类型</h3>    <p>这么做的好处是对state的操作接口一目了然,全部展现在mutation-types文件夹中,便于大项目的协作。当然这不是必须的。</p>    <pre>  <code class="language-javascript">// 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              }          }      })</code></pre>    <h3>mutation必须是同步函数</h3>    <p>因为异步函数会导致状态的不确定性,造成运行和调试的出路,这里就不进行展开了。</p>    <h3>组件的mapMutations</h3>    <pre>  <code class="language-javascript">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')              })          }      }</code></pre>    <h2>核心概念之actions</h2>    <p>上一章我们有提到mutation是没有异步操作的,因此异步操作需要用到action,注意action提交的mutation而不是直接处理state</p>    <pre>  <code class="language-javascript">const store = new Vuex.Store({          //...          actions: {              //context与store对象具有相同的方法和参数,但。。不是store本身              increment (context) {                  context.commit('increment')              }          },          //也可以写成          actions: {              increment ({ commit }) {                  commit('increment')              }          }      })</code></pre>    <h3>分发 Action</h3>    <pre>  <code class="language-javascript">store.dispatch('increment');        //action同样支持荷载方式,和mutation差不多        // 以载荷形式分发      store.dispatch('incrementAsync', {          amount: 10      })        // 以对象形式分发      store.dispatch({          type: 'incrementAsync',          amount: 10      })</code></pre>    <h3>组件中分发 Action</h3>    <pre>  <code class="language-javascript">import { mapActions } from 'vuex'        export default {          // ...          methods: {              ...mapActions([                  'increment' // 映射 this.increment() 为 this.$store.dispatch('increment')              ]),              ...mapActions({                  add: 'increment' // 映射 this.add() 为 this.$store.dispatch('increment')              })          }      }</code></pre>    <h3>组合 Actions</h3>    <p>因为action是异步的,因此组合多个action将是一个大问题,要解决这个问题这里引用了promise对象。通过dispath返回的promise对象进行操作,达成顺序执行</p>    <pre>  <code class="language-javascript">//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())    }  }</code></pre>    <h2>核心概念之getter</h2>    <p>有时候我们需要从 store 中的 state 中派生出一些状态(即对state进行一些操作过后得到的状态),例如对列表进行过滤并计数:</p>    <pre>  <code class="language-javascript">//常规做法,当其他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          }      }</code></pre>    <h3>mapGetters 辅助函数</h3>    <p>和之前一样的辅助映射函数一毛一样</p>    <pre>  <code class="language-javascript">import { mapGetters } from 'vuex'        export default {          // ...          computed: {          // 使用对象展开运算符将 getters 混入 computed 对象中              ...mapGetters([              'doneTodosCount',              'anotherGetter',              // ...              ])                mapGetters({              // 映射 this.doneCount 为 store.getters.doneTodosCount              doneCount: 'doneTodosCount'              })          }      }</code></pre>    <h2>核心概念之module</h2>    <p>使用单一状态树,导致应用的所有状态集中到一个很大的对象。但是,当应用变得很大时,store 对象会变得臃肿不堪。</p>    <p>Vuex 允许我们将 store 分割到模块(module)。每个模块拥有自己的 state、mutation、action、getters、甚至是嵌套子模块。</p>    <h3>module定义</h3>    <pre>  <code class="language-javascript">//状态篇        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 的状态</code></pre>    <h3>module局部状态</h3>    <p>对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态。</p>    <pre>  <code class="language-javascript">//即此状态为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')              }          }      }</code></pre>    <h3>命名空间</h3>    <p>模块内部的 action、mutation、和 getter 现在仍然注册在全局命名空间——这样保证了多个模块能够响应同一 mutation 或 action。你可以通过添加前缀或后缀的方式隔离各模块,以避免名称冲突。你也可能希望写出一个可复用的模块,其使用环境不可控。例如,我们想创建一个 todos 模块:</p>    <pre>  <code class="language-javascript">// 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) {          // ...          }      }      }</code></pre>    <p> </p>    <p>来自:https://segmentfault.com/a/1190000009108259</p>    <p> </p>