重学vue(2, 3)及其生态+TypeScript 之 vuex4.x(下)

简介: 重学vue(2, 3)及其生态+TypeScript 之 vuex4.x

mutations


用于更改 Vuex 的 store 中的状态。每个 mutation 都有一个字符串的 事件类型 (type) 和一个 回调函数 (handler) 。并且回调函数中,可以有以下值。


  • state为了修改state中的状态,肯定要传入state啊。


  • payload(这个可以自己命名) 用于提供修改状态的数据,他是当我们调用commit函数传递的值。并且可以传入任意数据。


const store = createStore({
  mutations: {
    changeName (state, payload) {
      state.name = payload.name
    }
  }
})


上面定义了mutation,如何在项目中触发呢?


  • 在template中, 通过$store.commit(对应的mutation名, payload)即可。


  • 在optionsAPI中,通过this.$store.commit(对应的mutation名, payload)即可。


<button @click="changeName">改变state.name: {{$store.state.name}}</button>
 methods: {
    changeName() {
      // this.$store.commit('changeName', {name: 'llm'})
      this.$store.commit({
        type: 'changeName',
        name: 'llm',
      })
    },
  },


  • 在optionsAPI中,通过vuex提供的useStoreAPI即可。


<button @click="changeName1">改变state.name: {{$store.state.name}}</button>
  setup() {
    const store = useStore()
    const changeName1 = () => {
      store.commit('changeName', {
        name: 'llm',
      })
    }
    return {
      changeName1
    }
  },


从上面的代码示例中,我们可以看出,提交commit有两种方法。


  • 先传入对应的mutation名,在传入对应的数据


  • 直接传入一个对象,且对象中定义一个type属性来指定mutation的名相应的,vuex也提供了对应的map函数。mapMutations。 终于这次不需要我们自己封装hook,vue2和vue3中使用mapMutations行为是一样的。因为当我们结构mapMutations返回的对象,他们本身就是一个个函数,我们直接调用,并传入对应的payload。


// 我们只需要在template中触发对应的method时传入对应的payload
<button @click="updateName({name: 'llm'})">改变state.name: {{$store.state.name}}</button>
<button @click="updateName({type: 'changeName', name: 'jcl'})">改变state.name: {{$store.state.name}}</button>
    methods: {
      ...mapMutations(['changeName']),
      ...mapMutations({
        updateName: "changeName"
      })
    },
    setup() {
      const storeMutations = mapMutations(['changeName'])
      return {
        ...storeMutations
      }
    }


网络异常,图片无法展示
|


一条重要的原则就是要记住 mutation 必须是同步函数。这是因为devtool工具会记录mutation的日记。每一条mutation被记录,devtools都需要捕捉到前一状态和后一状态的快照。但是在mutation中执行异步操作,就无法追踪到数据的变化,所以Vuex的重要原则中要求 mutation必须是同步函数。


actions


Action 类似于 mutation,不同在于:


  • Action 提交的是 mutation,而不是直接变更状态。变更状态依旧是在mutations中。


  • Action 可以包含任意异步操作。 actions中的回调函数第一个参数不再是state,而是一个store对象并且增加了rootState和rootDetters属性,一般命名为context。它里面存在着以下属性。


  • commit, 用于提交mutation,触发mutations中对应的回调函数。


  • dispatch, 我们可能还会触发对应的actions回调函数。


  • getters, 为了获取当前模块中的getters。


  • rootGetters, 为了获取根模块的getters。当我们设置了modules选项时,就会有用。


  • rootState, 为了获取根模块的state。当我们设置了modules选项时,就会有用。


  • state, 获取当前模块的state。 actions回调函数还可以传入第二个参数,让用户调用dispatch时,传入的数据。


下面我们就来看看如何使用actions吧。


const store = createStore({
   mutations: { 
     changeAge (state, payload) {
       state.age = payload.age
     }
   }
   actions: {
    changeAge (context, payload) {
      // 异步提交commit
      setTimeout(() => {
        context.commit('changeAge', payload)
      }, 1000);
    },
})


上面定义了action,如何在项目中触发呢?


  • 在template中, 通过$store.dispatch(对应的action名, payload)即可。


  • 在optionsAPI中,通过this.$store.dispatch(对应的dispatch名, payload)即可。


<button @click="changeAge">异步改变state.age {{$store.state.age}}</button>
 methods: {
    changeAge() {
      this.$store.dispatch('changeAge', {
        age: 999,
      })
    },
  }


  • 在optionsAPI中,通过vuex提供的useStoreAPI即可。


<button @click="changeAge1">异步改变state.age {{$store.state.age}}</button>
  setup() {
    const store = useStore()
    const changeAge1 = () => {
      store.dispatch('changeAge', {
        age: 1000,
      })
    }
    return {
      changeAge1
    }
  }


同样,vuex也提供了对应的map方法,mapActions。使用同mapMutions


methods: {
      ...mapActions(['changeAge']),
      ...mapActions({
        updateAge: "changeAge"
      })
    }
    setup() {
      const actions = mapActions(['changeAge'])
      const actions2 = mapActions({
        updateAge: "changeAge"
      })
      return {
        ...actions,
        ...actions2
      }
    }


重点:当我们通过dispatch分发action时,我们可以结合async await 给action回调返回一个promise对象。用于在组件中知道异步请求的状态。


modules


什么是Module?


由于使用单一状态树,应用的所有状态会集中到一个比较大的对象,当应用变得非常复杂时,store 对象就有可能变得相当臃肿。


为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块。


具体用法请看官网:next.vuex.vuejs.org/zh/guide/mo…


下面介绍一下使用事项:


  • 获取子模块中的state数据$store.state.模块名.数据


$store.state.home.homeCounter


  • 模块中的getters中的回调可以接受4个参数,分别是state, getters, rootState, rootGetters。可以获取当前模块中的state, getters, 父模块中的state, getters。


网络异常,图片无法展示
|


  • 我们一般使用模块的时候,都需要在定义模块的时候加上namespaced: true。不然模块内部的 action 和 mutation 仍然是注册在全局命名空间的——这样使得多个模块能够对同一个 action 或 mutation 作出响应。


下面测试,如果我们不加命名空间时


<h1>{{$store.state.rootCounter}}</h1>
    <h1>{{$store.state.home.homeCounter}}</h1>
    <button @click="increment">触发increment commit函数</button>
      methods: {
        increment() {
          this.$store.commit('increment')
        }
      }


网络异常,图片无法展示
|


他会触发子模块和根模块中的对应的mutations回调函数。 但是我们加上命名空间时,就不会出现这样的问题。因为vuex内部会自动将触发回调的函数名改成子模块/对应的mutation名


<h1>rootCounter: {{$store.state.rootCounter}}</h1>
    <h1>homeCounter: {{$store.state.home.homeCounter}}</h1>
    <button @click="increment">触发increment commit函数</button>
    <button @click="homeIncrement">触发increment commit函数</button>
  methods: {
    homeIncrement() {
      this.$store.commit('home/increment')
    },
    increment() {
      this.$store.commit('increment')
    },
  },


网络异常,图片无法展示
|


  • 当我们触发action时,基本和mutation是一样的。只是我们在触发子模块对应的action的时候,可以指定也触发父模块同名的action。


<h1>rootCounter: {{$store.state.rootCounter}}</h1>
    <h1>homeCounter: {{$store.state.home.homeCounter}}</h1>
    <button @click="homeIncrementAction">触发increment dispatch函数</button>
actions: {
    incrementAction ({ commit, dispatch, state, rootState, getters, rootGetters }) {
      // 指定触发当前模块的increment mutation
      commit("increment")
      // 指定触发父模块的increment mutation
      commit("increment", null, { root: true })
    }
  }
  methods: {
    homeIncrementAction() {
      this.$store.dispatch('home/incrementAction')
    }
  }


网络异常,图片无法展示
|


  • 如果想要在项目中通过对应的map方法,来映射对应模块中的state, getters, mutations, actions我们有很多方法。但是这种方式是比较方便的。createNamespacedHelpers(namespace), 创建基于命名空间的组件绑定辅助函数。其返回一个包含 mapStatemapGettersmapActionsmapMutations 的对象。它们都已经绑定在了给定的命名空间上。


在optionsAPI中使用都是没有问题的。下面我们基于上面开发的hook函数来看在compositionAPI中的使用。


修改useState hook


import { useStore, mapState,  createNamespacedHelpers} from 'vuex'
 import { computed } from 'vue'
    const useState = function(mapper, moduleName) {
        // mapper: Array | Object
        const store = useStore()
        // 先将vuex提供的mapState赋值给mapperFn,它用于映射对应模块的store.state
        let mapperFn = mapState 
        // 看其是否传入了moduleName, 没有就是使用默认的mapState
        if (typeof moduleName === 'string' && moduleName.length > 0) {
          mapperFn = createNamespacedHelpers(moduleName).mapState 
        }
        //将返回一个对象
        const storeStateFns = mapperFn(mapper)
        // 用于存放获取到的state.属性: ref对象 键值对
        const storeState = {}
        Object.keys(storeStateFns).forEach(item => {
            // 这我们知道辅助函数的内部是通过this.$store来实现的
            // setup中没有this, 所以通过bind来改变this的指向
            const fn = storeStateFns[item].bind({$store: store})
            //将最后的值放在storeState中
            storeState[item] = computed(fn)
        })
        return storeState
    }
    export default useState


修改useGetters hook


import { useStore, mapGetters, createNamespacedHelpers } from 'vuex'
    import { computed } from 'vue'
    const useGetters = (mapper, moduleName) => {
      const store = useStore()
    // 先将vuex提供的mapState赋值给mapperFn,它用于映射对应模块的store.state
    let mapperFn = mapGetters 
    // 看其是否传入了moduleName, 没有就是使用默认的mapState
    if (typeof moduleName === 'string' && moduleName.length > 0) {
      mapperFn = createNamespacedHelpers(moduleName).mapGetters 
    }
      const storeGetterFns = mapperFn(mapper)
      const storeGetter = {}
      Object.keys(storeGetterFns).forEach((item) => {
        const fn = storeGetterFns[item].bind({ $store: store })
        storeGetter[item] = computed(fn)
      })
      return storeGetter
    }
    export default useGetters


从上面可以看出,两个hook实现逻辑基本一样,只是调用的内部API不同,如果有想法封装一下,自己发挥。


下面就来测试一下


<h1>rootCount_state: {{rootCounter}}</h1>
    <h1>homeCounter_state: {{homeCounter}}</h1>
    <h1>doubleHomeCounter_getters: {{doubleHomeCounter}}</h1>
    <h1>doubleRootCounter_getters: {{doubleRootCounter}}</h1>
    setup() {
        return {
          ...useState(['homeCounter'], 'home'),
          ...useState(['rootCounter']),
          ...useGetters(['doubleHomeCounter'], 'home'),
          ...useGetters(['doubleRootCounter'])
        }
    }


// homeModule.js
const homeModule = {
  namespaced: true,
  state () {
    return {
      homeCounter: 1
    }
  },
  getters: {
    doubleHomeCounter (state, getters, rootState, rootGetters) {
      return state.homeCounter * 2
    }
  }
}
export default homeModule


//store.js
    import { createStore } from "vuex"
    import home from './modules/home'
    const store = createStore({
      state () {
        return {
          rootCounter: 100
        }
      },
      getters: {
        doubleRootCounter (state) {
          return state.rootCounter * 2
        }
      }
      modules: {
        home
      }
    });
    export default store;


网络异常,图片无法展示
|


相关文章
|
3月前
|
JavaScript 前端开发 安全
【技术革新】Vue.js + TypeScript:如何让前端开发既高效又安心?
【8月更文挑战第30天】在使用Vue.js构建前端应用时,结合TypeScript能显著提升代码质量和开发效率。TypeScript作为JavaScript的超集,通过添加静态类型检查帮助早期发现错误,减少运行时问题。本文通过具体案例展示如何在Vue.js项目中集成TypeScript,并利用其类型系统提升代码质量。首先,使用Vue CLI创建支持TypeScript的新项目,然后构建一个简单的待办事项应用,通过定义接口描述数据结构并在组件中使用类型注解,确保代码符合预期并提供更好的编辑器支持。
85 0
|
3月前
|
JavaScript 前端开发 安全
立等可取的 Vue + Typescript 函数式组件实战
立等可取的 Vue + Typescript 函数式组件实战
|
4月前
|
JavaScript 前端开发
【Vue3+TypeScript】CRM系统项目搭建之 — 关于如何设计出优质的 Vue 业务组件
【Vue3+TypeScript】CRM系统项目搭建之 — 关于如何设计出优质的 Vue 业务组件
51 0
【Vue3+TypeScript】CRM系统项目搭建之 — 关于如何设计出优质的 Vue 业务组件
|
5月前
|
JavaScript 安全 前端开发
Vue 3 中的 TypeScript
【6月更文挑战第15天】
84 6
|
6月前
|
JavaScript 前端开发 开发者
类型检查:结合TypeScript和Vue进行开发
【4月更文挑战第24天】TypeScript是JavaScript超集,提供类型注解等特性,提升代码质量和可维护性。Vue.js是一款高效前端框架,两者结合优化开发体验。本文指导如何配置和使用TypeScript与Vue:安装TypeScript和Vue CLI,创建Vue项目时选择TypeScript支持,配置`tsconfig.json`,编写`.tsx`组件,最后运行和构建项目。这种结合有助于错误检查和提升开发效率。
56 2
|
6月前
|
JavaScript 前端开发 开发者
Vue工具和生态系统: Vue.js和TypeScript可以一起使用吗?
【4月更文挑战第18天】Vue.js与TypeScript兼容,官方文档支持在Vue项目中集成TypeScript。TypeScript作为JavaScript超集,提供静态类型检查和面向对象编程,增强代码准确性和健壮性。使用TypeScript能提前发现潜在错误,提升代码可读性,支持接口和泛型,使数据结构和函数更灵活。然而,不是所有Vue插件都兼容TypeScript,可能需额外配置。推荐尝试在Vue项目中使用TypeScript以提升项目质量。
115 0
|
6天前
|
缓存 JavaScript 前端开发
vue学习第四章
欢迎来到我的博客!我是瑞雨溪,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中计算属性的基本与复杂使用、setter/getter、与methods的对比及与侦听器的总结。如果你觉得有用,请关注我,将持续更新更多优质内容!🎉🎉🎉
vue学习第四章
|
6天前
|
JavaScript 前端开发
vue学习第九章(v-model)
欢迎来到我的博客,我是瑞雨溪,一名热爱JavaScript与Vue的大一学生,自学前端2年半,正向全栈进发。此篇介绍v-model在不同表单元素中的应用及修饰符的使用,希望能对你有所帮助。关注我,持续更新中!🎉🎉🎉
vue学习第九章(v-model)
|
5天前
|
JavaScript 前端开发 开发者
vue学习第十章(组件开发)
欢迎来到瑞雨溪的博客,一名热爱JavaScript与Vue的大一学生。本文深入讲解Vue组件的基本使用、全局与局部组件、父子组件通信及数据传递等内容,适合前端开发者学习参考。持续更新中,期待您的关注!🎉🎉🎉
vue学习第十章(组件开发)
|
11天前
|
JavaScript 前端开发
如何在 Vue 项目中配置 Tree Shaking?
通过以上针对 Webpack 或 Rollup 的配置方法,就可以在 Vue 项目中有效地启用 Tree Shaking,从而优化项目的打包体积,提高项目的性能和加载速度。在实际配置过程中,需要根据项目的具体情况和需求,对配置进行适当的调整和优化。
下一篇
无影云桌面