Vue学习第七天—Vuex

237 阅读4分钟

vuex

1. 是什么

vuex是一个vue的状态管理工具,状态就是数据

简单来说,vuex是一个插件,可以帮我们管理vue通用的数据(多组件共享数据

2. 场景:

  • 某个状态在很多个组件来使用(个人信息)
  • 多个组件共同维护一份数据(购物车)

优势

  • 共同维护一份数据,数据集中化管理
  • 响应式变化
  • 操作简洁(vuex提供了一些辅助函数)
  • 任意一个组件都可以修改数据
  • 三个组件的数据是同步的

vuex的核心概念:....


构建vuex多组件数据共享环境

  1. 创建项目
 vue create vuex-demo
  1. 选自定义
  2. 选css、linter、babel
  3. vue2
  4. less
  5. standard
  6. lint on save
  7. In dedicated config files
  8. n

创建一个空仓库

目标:安装vuex插件,初始化一个空仓库 ​

  1. yarn add vuex@3,装包
  2. 在src文件夹中新建store仓库文件夹,再在里面新建index.js文件
  3. 插件安装
Vue.use(Vuex)
  1. 创建空仓库
const store = new Vuex.Store()
  1. 导出给main.js使用
export default store
  1. 在main.js中导入挂载
import store from './store'
//再把store丢到实例中
new Vue({
  store,
  render: h => h(App)
}).$mount('#app')

仓库构建完之后,所有组件都可以去访问它

访问路由是this.$router,所以访问仓库就是this.$store

1.🤯state状态(只能获取,使用,不能修改)

如何给仓库提供数据,并使用

  1. 提供数据
const store = new Vuex.Store({
  state: {
    title: '仓库大标题',
    count: 100,
    list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  }
  })

State提供唯一的公共数据源,所有共享的数据都要统一 放到Store仓库中State中 存储,在state对象中可以添加 我们要共享的数据

state状态,即数据,类似于vue组件中的data

与data区别

  • data是组件自己的数据
  • state是所有组件共享的数据
  1. 使用数据
  • 通过store直接访问(也就是打点的方式)
  • 通过辅助函数(更简化)

1. 访问使用里面数据要按层次顺序去打点

//获取store
1.store.$store
2.import导入store
//模板中
{{$store.state.xxx}}
//组件逻辑中
this.$store.state.xxx
//js模块中
store.state.xxx

2. 通过辅助函数mapState去简化

  • 原本思路是:把这个state里的这个你需要的数据封装在计算属性之中,然后就可以在界面中直接{{属性}}调用,无需打点 ​
  • 但是如果每一个属性都这样封装,反而不简便了,所以我们引入辅助函数来
  • 比如mapState,它可以帮助我们把store中的数据 自动 映射到组件的计算属性之中 ​

2.🤯mutations(修改)

明确vuex同样遵循单项数据流,组件中不能直接修改仓库的数据

但是如果你比如写了this.$store.state.count++,这样的错误代码(修改数据),系统是不会给你提示的。

但是你如果希望系统提醒你,你就开启 严格模式

  • 即把这个放在仓库里:strict: true

state数据的修改只能通过mutation

  1. 在store仓库中定义mutations对象对象中存放修改state的方法
//提供
const store = new Vuex.Store({
  mutations: {
    // 所有mutation函数,第一个参数,都是 state
    //————————————也可以传参
    // 注意点:mutation参数有且只能有一个
    //如果需要多个参数,包装成一个对象
    addCount (state, obj) {
      console.log(obj)
      // 修改数据
      state.count += obj.count
    },
    changeTitle (state, newTitle) {
      state.title = newTitle
    }
  }
  })
//调用
 //在之后调用的时候就是:用commit调用
  methods: {
   handleAdd () {
      this.$store.commit('addCount')
    }
  }

TIP:通过e形参,然后e.target.value可以拿到输入框的值,+e.target.value可以转数字

辅助函数mapMutations

mapMutations和mapState很像,它是把位于mutations中的方法提取了出来,映射到组件methods中

流程图: ​

3.🤯actions(处理异步操作,不能修改)

需求1:一秒钟之后,修改state的count成666

说明:mutations必须是同步的(便于监测数据变化,记录调试)

  1. 在store仓库中提供action方法
  2. 页面中dispatch调用(在绑定的事件的methods里写)
  • 注意:不能直接操作 state,操作 state,还是需要 commit mutation
  • context上下文,相当于store
 mutations: {
    changeCount (state, newCount) {
      state.count = newCount
    }
  }
 actions: {
    // context 上下文 (此处未分模块,可以当成store仓库)
    // 有store就说明有commit
    // context.commit('mutation名字', 额外参数)
    changeCountAction (context, num) {
      // 这里是setTimeout模拟异步,以后大部分场景是发请求
      setTimeout(() => {
        context.commit('changeCount', num)
      }, 1000)
    }
  },
methods:{
  handleChange () {
        // 调用action
        //   this.$store.dispatch('action名字', 额外参数)
        this.$store.dispatch('changeCountAction', 666)
    }
 }

辅助函数mapActions

mapActions是把位于actions中的方法提取了出来,映射到组件methods中

流程图: ​

4. 🤯getters(类似于计算属性,但是不能修改)

说明:除了state之外,有时我们还需要从state中派生处一些状态,这些状态是依赖state的,此时会用到getters

需求1:state中定义了list为一个1-10 的数组,现在在组件中需要显示所有大于5的数据

  1. 在store仓库中定义getters
 getters: {
    // 注意点:
    // 1. 形参第一个参数,就是state
    // 2. 必须有返回值return,返回值就是getters的值
    filterList (state) {
      return state.list.filter(item => item > 5)
    }
  },
  1. 访问getters
  • 通过store访问getters(打点)
{{$store.getters.filterList}}

- 通过辅助函数mapGetters映射(在computed中)

 computed: {
    ...mapFetters(['filterList'])
  },
  
  {{filterList}}

上面几种辅助函数分别映射的是什么

  • mapState 和 mapGetters 都是映射属性
  • mapMutations 和 mapActions 都是映射方法
 computed: {
    // mapState 和 mapGetters 都是映射属性
    ...mapState(['count', 'user', 'setting']),
    ...mapGetters(['filterList']),
  },
  methods: {
    // mapMutations 和 mapActions 都是映射方法
    ...mapMutations(['subCount', 'changeTitle']),
    ...mapActions(['changeCountAction'])
    }

5.🤯模块model(进阶语法)

由于vuex使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store对象就有可能变得相当臃肿。(当项目越来越大时,vuex会变得越来越难以维护) ​

创建子模块

  • 模块拆分(在modules里再写其他js,每个js都是一个模块)

  • 格式 ​
const state = {}
const mutations = {}
const actions = {}
const getters = {}

export default {
  state,
  mutations,
  actions,
  getters
}

如何访问子模块中的state

问题:尽管已经分模块了,但其实子模块的状态,还是会挂到根级别的state中,属性名就是模块名

就像这样:

使用模块中的数据

  1. 直接通过模块名访问$store.state.模块名.xxx
  2. 通过mapState映射
    • 默认根级别的映射:mapState(['xxx'])
    • 子模块的映射:mapState('模块名',['xxx']),需要开启命名空间namespaced: true,也就是给每个模块加上了自己的名字
export default {
  namespaced: true,
  state,
  mutations,
  actions,
  getters
}

    //默认根级别映射的访问 
    <div>{{ user.userInfo}}</div>
    //子模块的映射的访问
    <div>{{userInfo}}</div>

computed: {
    // 默认根级别的映射
    ...mapState(['count', 'user', 'setting']),
    //子模块的映射
    ...mapState('user', ['userInfo']),
  },

如何访问使用子模块中的getters

  1. 直接通过模块名访问$store.getters['模块名/xxx']
  2. 通过mapGetters映射
    • 默认根级别的映射:mapGetters(['xxx'])
    • 子模块的映射:mapGetters('模块名',['xxx']),需要开启命名空间

如何访问使用子模块中的mutations

注意:默认模块中的mutation和actions会被挂载到全局,需要开启命名空间,才会挂载到子模块

调用子模块中的mutations:

  1. 直接通过store调用$store.commit('模块名/xxx',额外参数)
  2. 通过mapMutations映射
    • 默认根级别的映射:mapMutations(['xxx'])
    • 子模块的映射:mapMutations('模块名',['xxx']),需要开启命名空间

如何访问使用子模块中的actions

注意:默认模块中的mutation和actions会被挂载到全局,需要开启命名空间,才会挂载到子模块

  1. 直接通过store调用$store.dispatch('模块名/xxx',额外参数)
  2. 通过mapActions映射
    • 默认根级别的映射:mapActions(['xxx'])
    • 子模块的映射:mapActions('模块名',['xxx']),需要开启命名空间