Vue-4-VUE状态管理


VUE状态管理

状态就是响应式的数据和呈现它的视图

组件的状态由它的数据和视图决定的

状态管理 = 数据和视图管理

如何实现多个组件共用一个状态?

  1. 把数据和修改数据的逻辑都放在某个祖先组件上,子组件触发祖先组件的修改逻辑
  2. Vuex(vue的插件 = Vue的全局状态管理)

Vuex的插件4xx版本是为了配合Vue3xx版本

Vue的版本如果是2xx,则Vuex的版本应该是用3xx

Vuex

Vuex的数据流

组件按钮 => actions(dispatch) => mutations(commit) => state => 自动通知对应的视图更新.

实例化Vuex数据仓库

实例化

const store = new Vuex.Store({
  // 多个组件共用的状态.(数据)
  state: {
    count: 0
  }
})

挂载,如果挂载到new Vue上,就是全局状态管理,所有组件都可以访问state的数据

如果挂载到组件A,则A和其子孙组件可以使用

new Vue({
      el: '#app',
      components: {App},
      template: `<App />`,
      // store: store
      // 挂载到new Vue上
      store
    });

获取Vuex数据

挂载完成后,vue组件内,可以通过this.$store.state来获取这个state的实例

const box = {
  template: `
    <div>
      <h3>box组件---{{count}}</h3>
      <button>box--count++</button>
    </div>
  `,
  computed: {
      // 第一种取名字的写法
    count() {
      return this.$store.state.count
    }
  },
}
const son = {
  template: `
    <div>
        // 第二种写法,这没有this
      <h3>son组件---{{$store.state.count}}</h3>
      <button>son--count++</button>
    </div>
  `,
}

修改Vuex数据

同步

修改state的数据,需要通过mutations选项写在数据仓库中,可以写多个方法

mutations内的方法的第一个参数是state对象,第二个参数是组件传递的参数。

组件触发mutations内的方法,需要通过this.$store.commit触发

commit("mutations的方法名",组件实参)

commit内是不能有异步操作

const store = new Vuex.Store({
  // 多个组件共用的状态.(数据)
  state: {
    count: 10000
  },
  // 修改state内数据的逻辑.
  mutations: {
    // count+1
    plus(state, num) {
      state.count += num;
    },
    // count-1
    reduce(state, num) {
      state.count -= num
    }
  }
});
const box = {
  template: `
    <div>
      <h3>box组件---{{count}}</h3>
      <button @click='$store.commit("plus", 1)'>box--count++</button>
      <button @click='$store.commit("reduce", 1)'>box--count--</button>
    </div>
  `,
  computed: {
    count() {
      return this.$store.state.count
    }
  },
}

异步

通过actions来实现

actions的方法内触发mutations方法

actions触发方式是dispatch

dispatch(‘actions方法名’,组件实参),

actions内的方法,第一个参数是store本身,第二个是实参

const store = new Vuex.Store({
  // 严格模式.禁止在mutations之外修改state.
  // 如果在mutations之外修改state,会报这个错[vuex] do not mutate vuex store state outside mutation handlers.
  strict: true,
  // 多个组件共用的状态.(数据)
  state: {
    count: 10000
  },
  // 修改state内数据的逻辑.
  mutations: {
    // count+1
    plus(state, num) {
      state.count += num
    },
    // count-1
    reduce(state, num) {
      state.count -= num
    }
  },
  // 异步触发mutations
  actions: {
    asyncPlus(store, num) {
      // 2秒后触发mutations的plus方法修改state。
      setTimeout(() => {
        store.commit("plus", num);
      }, 2000);
    }
  }
});

const box = {
  template: `
    <div>
      <h3>box组件---{{count}}</h3>
      <button @click='$store.dispatch("asyncPlus", 1)'>box--count++</button>
      <button @click='$store.commit("reduce", 1)'>box--count--</button>
    </div>
  `,
  computed: {
    count() {
      return this.$store.state.count
    }
  },
}

把Vuex的数据和逻辑映射成组件的数据和逻辑

map映射:

  1. 把state的数据变成组件数据
  2. 把mutations和actions的方法变成组件方法

const { mapState, mapMutations, mapActions } = Vuex;

mapState

const { mapState, mapMutations, mapActions }  = Vuex;
// 以上方法返回纯对象
mapState(['count', 'msg']) => { count() {} , msg() {}}

const box = {
  template: `
    <div>
      <h3>box组件---{{num}}---{{str}}</h3>
      <button @click='$store.dispatch("asyncPlus", 1)'>box--count++</button>
      <button @click='$store.commit("reduce", 1)'>box--count--</button>
    </div>
  `,
  computed: {
    count() {
      return this.$store.state.count
    },
    msg() {
      return this.$store.state.msg
    },
    // 直接写
    ...mapState(['count', 'msg']),
    // 换名字
    ...mapState({
      num: 'count',
      str: 'msg'
    })
  },
}

mapMutations,mapActions

const box = {
  template: `
    <div>
      <h3>box组件---{{count}}</h3>
      <button @click='asyncPlus(1)'>box--count++</button>
      <button @click='reduce(1)'>box--count--</button>
    </div>
  `,
  computed: {
    ...mapState(['count'])
  },
  methods: {
    ...mapActions(['asyncPlus']),
    ...mapMutations(['reduce'])
  }
}

组件混合

组件公共选项部分

需要通过mixins选项引入

如果组件本身就包含mx内的选项,则以组件的选项为准

    const mx = {
      computed: {
        ...mapState(['count'])
      },
      methods: {
        ...mapActions(['asyncPlus']),
        ...mapMutations(['reduce'])
      }
    }

    const box = {
      template: `
        <div>
          <h3>box组件---{{count}}</h3>
          <button @click='asyncPlus(1)'>box--count++</button>
          <button @click='reduce(1)'>box--count--</button>
        </div>
      `,
      mixins: [mx],
    }

Vuex的计算属性

      computed: {
        // total() {
        //   通过$store来获取Vuex的计算属性.
        //   return this.$store.getters.total
        // }

        // 通过映射方法快速引入Vuex的计算属性total
        ...mapGetters(['total'])
      }
const { mapState, mapMutations, mapGetters } = Vuex;

const store = new Vuex.Store({
  state: {
    count: 1,
    price: 10,
  },
  // Vuex的计算属性total
  getters: {
    total({count, price}) {
      return count * price
    }
  },
  mutations: {
    setCount(state, val) {
      state.count = val
    },
    setPrice(state, val) {
      state.price = val
    }
  }
});

const mx = {
  computed: {
    ...mapState(['count', 'price'])
  },
  methods: {
    ...mapMutations(['setCount', 'setPrice'])
  }
}

const count = {
  template: `<input type='text' placeholder='数量' :value='count' @input='setCount($event.target.value)' />`,
  mixins: [mx]
}

const price = {
  template: `<input type='text' placeholder='单价' :value='price' @input='setPrice($event.target.value)' />`,
  mixins: [mx]
}

const total = {
  template: `<div>总价:{{total}}</div>`,
  computed: {
    // total() {
    //   通过$store来获取Vuex的计算属性.
    //   return this.$store.getters.total
    // }

    // 通过映射方法快速引入Vuex的计算属性total
    ...mapGetters(['total'])
  }
}

const App = {
  template: `
    <div>
      <count />
      <price />
      <total />
    </div>
  `,
  components: {count, price, total},
  store
}

new Vue({
  el: '#app',
  components: { App },
  template: `<App />`
})

组件的data为什么必须是函数,因为组件的状态应该是相互独立,组件复用时,函数返回。新的对象,新的对象对应新的状态。

$refs

用于获取视图标签

方法一,写在标签里:

  1. 在标签里 ref='一个名字'
  2. 使用的时候this.$refs.一个名字.style.backgroundColor = 'red'

方法二,写在组件里

  1. <组件名 ref='一个名字' />
  2. this.$refs.box返回一个vue实例

$forceUpdate

强制更新当前组件,应急性api,不推荐滥用

$children和$parent

$children[i].xxx 修改当前组件的子组件的属性(方法),i表示下标

$parent .xxx 修改当前组件的父组件的属性(方法)