zoukankan      html  css  js  c++  java
  • vue中vuex的使用练习

    Vuex 是什么?

    Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。访问官网,点击这里

    每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同:

    1. Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。

    2. 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。

    vuex中,有默认的五种基本的对象:

    • state:存储状态(变量)

    • getters:对数据获取之前的再次编译,可以理解为state的计算属性。我们在组件中使用 $sotre.getters.fun()

    • mutations:修改状态,并且是同步的。在组件中使用$store.commit('',params)。这个和我们组件中的自定义事件类似。

    • actions:异步操作。在组件中使用是$store.dispath('')

    • modules:store的子模块,为了开发大型项目,方便状态管理而使用的。这里我们就不解释了,用起来和上面的一样。

    0、在 main.js里面,引入vuex相关的js文件

    import store from './store'
    // store.js
    
    import Vue from 'vue'
    import Vuex from 'vuex'
    
    import user from './module/user'
    import app from './module/app'
    
    Vue.use(Vuex)
    
    export default new Vuex.Store({
      state: {
        //
        number: 0
      },
      mutations: {
      
      },
      getters: {
       
      },
      actions: {
        
      },
      modules: {
    
      }
    })

    1、在 Vue 组件中获得 Vuex 状态(state)

    (1)直接在组件father.vue中调用:

    <div style="border: 2px solid #007606;padding: 5px;">
          VUEX的数据【number】:
          <span style="font-size: 30px;font-weight: bolder;">
            {{this.$store.state.number}}
          </span>
    </div>

     (2)或者在计算属性中处理后使用

    <div style="border: 2px solid #007606;padding: 5px;">
      VUEX的数据【number】:
      <span style="font-size: 30px;font-weight: bolder;">
        {{count}}
      </span>
    </div>
    computed: {
      count () {
        return '==这是VUEX中定义的Number==:'+this.$store.state.number
      }
    },

    2、Getter

    有时候我们需要从 store 中的 state 中派生出一些状态,

    如果有多个组件需要用到此属性,我们要么复制这个函数,或者抽取到一个共享函数然后在多处导入它——无论哪种方式都不是很理想。

    Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

    (1)实例:孙子级组件F中,处理【number】的值,*2

    <div style="border: 2px solid #2700ff;padding: 5px;">
      Vuex的数值【number*2】:
      <span style="font-size: 30px;font-weight: bolder;">
      {{$store.getters.doneTodosCount}}
      </span>
    </div>

    (2)store.js代码:

    getters: {
      doneTodosCount: (state, getters) => {
        return state.number * 2
      }
    },

    3、Mutation(Mutation 必须是同步函数)

    更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数

    (1)实例:祖父级组件parent,点击按钮,对number进行++或者--的操作

    <div style="border: 2px solid #007606;padding: 5px;">
      VUEX的数据【number】:
      <span style="font-size: 30px;font-weight: bolder;">
        {{count}}
      </span>
    
      <br>
      <el-button @click="vuexClick1">点击++</el-button>
      <el-button @click="vuexClick2">点击--</el-button>
    </div>
    methods: {
      vuexClick1 () {
        this.$store.commit('vuexClick1')
      },
      vuexClick2 () {
        this.$store.commit('vuexClick2')
      }
    },

    (2)store.js代码:

    mutations: {
      //
      vuexClick1 (state) {
        return state.number++
      },
      vuexClick2 (state) {
        return state.number--
      }
    },

    4、Ation

    Action 类似于 mutation,不同在于:

    • Action 提交的是 mutation,而不是直接变更状态。

    • Action 可以包含任意异步操作。

    (1)实例:祖父级组件parent,点击按钮"number延时3s加10",对number进行延时3s加10的操作

    <el-button @click="vuexClick3">number延时3s加10</el-button>
    vuexClick3 () {
      this.$store.dispatch('vuexClick3')
    }

    (2)store.js代码:

    actions: {
      //
      vuexClick3 (state) {
        setTimeout(() => {
          return state.commit('NumberChange3')
        }, 3000)
      }
    },
    NumberChange3 (state) {
      return state.number += 10
    }

    5、Module

    官方文档

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

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

    image.png

    modules: {
      user,
      app
    }

    命名空间

    默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。

    如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。

    const store = new Vuex.Store({
      modules: {
        account: {
          namespaced: true,
    
          // 模块内容(module assets)
          state: { ... }, // 模块内的状态已经是嵌套的了,使用 `namespaced` 属性不会对其产生影响
          getters: {
            isAdmin () { ... } // -> getters['account/isAdmin']
          },
          actions: {
            login () { ... } // -> dispatch('account/login')
          },
          mutations: {
            login () { ... } // -> commit('account/login')
          },
    
          // 嵌套模块
          modules: {
            // 继承父模块的命名空间
            myPage: {
              state: { ... },
              getters: {
                profile () { ... } // -> getters['account/profile']
              }
            },
    
            // 进一步嵌套命名空间
            posts: {
              namespaced: true,
    
              state: { ... },
              getters: {
                popular () { ... } // -> getters['account/posts/popular']
              }
            }
          }
        }
      }})

    【官网实例】

    完整代码:

    1.store.js

    import Vue from 'vue'
    import Vuex from 'vuex'
    
    import user from './module/user'
    import app from './module/app'
    
    Vue.use(Vuex)
    
    export default new Vuex.Store({
      state: {
        //
        number: 0
      },
      mutations: {
        //
        vuexClick1 (state) {
          return state.number++
        },
        vuexClick2 (state) {
          return state.number--
        },
        NumberChange3 (state) {
          return state.number += 10
        }
      },
      getters: {
        doneTodosCount: (state, getters) => {
          return state.number * 2
        }
      },
      actions: {
        //
        vuexClick3 (state) {
          setTimeout(() => {
            return state.commit('NumberChange3')
          }, 3000)
        }
      },
      modules: {
        user,
        app
      }
    })

    2.father.vue

    <!--
      文件描述:祖父级组件
      创建时间:2019/12/28 15:30
    -->
    <template>
      <div class="" style=" 900px;height: 700px;background-color: #d6e7ff;padding: 20px;">
        <span style="font-size: 20px;font-weight: bold;">祖父级组件:father</span>
        <div style="border: 2px solid #007606;padding: 5px;">
          VUEX的数据【number】:
          <span style="font-size: 30px;font-weight: bolder;">
            {{count}}
          </span>
    
          <br>
          <el-button @click="vuexClick1">点击++</el-button>
          <el-button @click="vuexClick2">点击--</el-button>
          <el-button @click="vuexClick3">number延时3s加10</el-button>
        </div>
        <br>
    
        <div>
          <childrenA></childrenA>
          <childrenB></childrenB>
          <childrenC></childrenC>
        </div>
      </div>
    </template>
    
    <script>
    // 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
    // 例如:import 《组件名称》 from '《组件路径》';
    import childrenA from './childrenA'
    import childrenB from './childrenB'
    import childrenC from './childrenC'
    
    export default {
      name: 'father',
      // import引入的组件需要注入到对象中才能使用
      components: {
        childrenA,
        childrenB,
        childrenC
      },
      computed: {
        count () {
          return this.$store.state.number
        }
      },
      methods: {
        vuexClick1 () {
          this.$store.commit('vuexClick1')
        },
        vuexClick2 () {
          this.$store.commit('vuexClick2')
        },
        vuexClick3 () {
          this.$store.dispatch('vuexClick3')
        }
      },
      data () {
        // 这里存放数据
        return {
        }
      }
    }
    </script>

    3.childrenG.vue

    <!--文件描述:孙子级组件G
      创建时间:2019/12/28 15:37-->
    <template>
      <div class="" style="float: left;margin: 10px; 200px;height: 200px;background-color: #ff00de;padding: 20px;color:#fff;">
        <span style="font-size: 14px;font-weight: bold;color:#fff;">孙子级组件G:childrenG</span>
        <div style="border: 2px solid #00ff09;padding: 5px;">
          VUEX的数据【number】:
          <span style="font-size: 30px;font-weight: bolder;">
            {{this.$store.state.number}}
          </span>
        </div>
      </div>
    </template>
    
    <script>
    export default {
      name: 'childrenA',
      // import引入的组件需要注入到对象中才能使用
      components: {},
      data () {
        // 这里存放数据
        return {
        }
      }
    }
    </script>

    4.childrenF.vue

    <!--
      文件描述:孙子级组件F
      创建时间:2019/12/28 15:37
      创建人:
    -->
    <template>
      <div class="" style="float: left;margin: 10px; 200px;height: 200px;background-color: #aaff1a;padding: 20px;">
        <span style="font-size: 14px;font-weight: bold;color:#000000;">孙子级组件F:childrenF</span>
        <div style="border: 2px solid #2700ff;padding: 5px;">
          Vuex的数值【number*2】:
          <span style="font-size: 30px;font-weight: bolder;">
          {{$store.getters.doneTodosCount}}
          </span>
        </div>
      </div>
    </template>
    
    <script>
    // 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
    // 例如:import 《组件名称》 from '《组件路径》';
    // 例如:import uploadFile from '@/components/uploadFile/uploadFile'
    
    export default {
      name: 'childrenA',
      // import引入的组件需要注入到对象中才能使用
      components: {},
      data () {
        // 这里存放数据
        return {
    
        }
      },
      // 监听属性 类似于data概念
      computed: {},
      // 方法集合
      methods: {},
      // 监控data中的数据变化
      watch: {},
      // 生命周期 - 创建完成(可以访问当前this实例)
      created () {
    
      },
      // 生命周期 - 挂载完成(可以访问DOM元素)
      mounted () {
    
      },
      beforeCreate () {
      }, // 生命周期 - 创建之前
      beforeMount () {
      }, // 生命周期 - 挂载之前
      beforeUpdate () {
      }, // 生命周期 - 更新之前
      updated () {
      }, // 生命周期 - 更新之后
      beforeDestroy () {
      }, // 生命周期 - 销毁之前
      destroyed () {
      }, // 生命周期 - 销毁完成
      activated () {
      } // 如果页面有keep-alive缓存功能,这个函数会触发
    }
    </script>
    
    <style scoped  lang="scss">
      //@import url(); 引入公共css类
    </style>

     5.user.js

    import {
      login,
      logout,
      getUserInfo,
      getMessage,
      getContentByMsgId,
      hasRead,
      removeReaded,
      restoreTrash,
      getUnreadCount
    } from '@/api/user'
    import { setToken, getToken } from '@/libs/util'
    
    export default {
      state: {
        userName: '',
        userId: '',
        avatorImgPath: '',
        token: getToken(),
        access: '',
        hasGetInfo: false,
        unreadCount: 0,
        messageUnreadList: [],
        messageReadedList: [],
        messageTrashList: [],
        messageContentStore: {}
      },
      mutations: {
        setAvator (state, avatorPath) {
          state.avatorImgPath = avatorPath
        },
        setUserId (state, id) {
          state.userId = id
        },
        setUserName (state, name) {
          state.userName = name
        },
        setAccess (state, access) {
          state.access = access
        },
        setToken (state, token) {
          state.token = token
          setToken(token)
        },
        setHasGetInfo (state, status) {
          state.hasGetInfo = status
        },
        setMessageCount (state, count) {
          state.unreadCount = count
        },
        setMessageUnreadList (state, list) {
          state.messageUnreadList = list
        },
        setMessageReadedList (state, list) {
          state.messageReadedList = list
        },
        setMessageTrashList (state, list) {
          state.messageTrashList = list
        },
        updateMessageContentStore (state, { msg_id, content }) {
          state.messageContentStore[msg_id] = content
        },
        moveMsg (state, { from, to, msg_id }) {
          const index = state[from].findIndex(_ => _.msg_id === msg_id)
          const msgItem = state[from].splice(index, 1)[0]
          msgItem.loading = false
          state[to].unshift(msgItem)
        }
      },
      getters: {
        messageUnreadCount: state => state.messageUnreadList.length,
        messageReadedCount: state => state.messageReadedList.length,
        messageTrashCount: state => state.messageTrashList.length
      },
      actions: {
        // 登录
        handleLogin ({ commit }, { userName, password }) {
          userName = userName.trim()
          return new Promise((resolve, reject) => {
            login({
              userName,
              password
            }).then(res => {
              const data = res.data
              commit('setToken', data.token)
              resolve()
            }).catch(err => {
              reject(err)
            })
          })
        },
        // 退出登录
        handleLogOut ({ state, commit }) {
          return new Promise((resolve, reject) => {
            logout(state.token).then(() => {
              commit('setToken', '')
              commit('setAccess', [])
              resolve()
            }).catch(err => {
              reject(err)
            })
            // 如果你的退出登录无需请求接口,则可以直接使用下面三行代码而无需使用logout调用接口
            // commit('setToken', '')
            // commit('setAccess', [])
            // resolve()
          })
        },
        // 获取用户相关信息
        getUserInfo ({ state, commit }) {
          return new Promise((resolve, reject) => {
            try {
              getUserInfo(state.token).then(res => {
                const data = res.data
                commit('setAvator', data.avator)
                commit('setUserName', data.name)
                commit('setUserId', data.user_id)
                commit('setAccess', data.access)
                commit('setHasGetInfo', true)
                resolve(data)
              }).catch(err => {
                reject(err)
              })
            } catch (error) {
              reject(error)
            }
          })
        },
        // 此方法用来获取未读消息条数,接口只返回数值,不返回消息列表
        getUnreadMessageCount ({ state, commit }) {
          getUnreadCount().then(res => {
            const { data } = res
            commit('setMessageCount', data)
          })
        },
        // 获取消息列表,其中包含未读、已读、回收站三个列表
        getMessageList ({ state, commit }) {
          return new Promise((resolve, reject) => {
            getMessage().then(res => {
              const { unread, readed, trash } = res.data
              commit('setMessageUnreadList', unread.sort((a, b) => new Date(b.create_time) - new Date(a.create_time)))
              commit('setMessageReadedList', readed.map(_ => {
                _.loading = false
                return _
              }).sort((a, b) => new Date(b.create_time) - new Date(a.create_time)))
              commit('setMessageTrashList', trash.map(_ => {
                _.loading = false
                return _
              }).sort((a, b) => new Date(b.create_time) - new Date(a.create_time)))
              resolve()
            }).catch(error => {
              reject(error)
            })
          })
        },
        // 根据当前点击的消息的id获取内容
        getContentByMsgId ({ state, commit }, { msg_id }) {
          return new Promise((resolve, reject) => {
            let contentItem = state.messageContentStore[msg_id]
            if (contentItem) {
              resolve(contentItem)
            } else {
              getContentByMsgId(msg_id).then(res => {
                const content = res.data
                commit('updateMessageContentStore', { msg_id, content })
                resolve(content)
              })
            }
          })
        },
        // 把一个未读消息标记为已读
        hasRead ({ state, commit }, { msg_id }) {
          return new Promise((resolve, reject) => {
            hasRead(msg_id).then(() => {
              commit('moveMsg', {
                from: 'messageUnreadList',
                to: 'messageReadedList',
                msg_id
              })
              commit('setMessageCount', state.unreadCount - 1)
              resolve()
            }).catch(error => {
              reject(error)
            })
          })
        },
        // 删除一个已读消息到回收站
        removeReaded ({ commit }, { msg_id }) {
          return new Promise((resolve, reject) => {
            removeReaded(msg_id).then(() => {
              commit('moveMsg', {
                from: 'messageReadedList',
                to: 'messageTrashList',
                msg_id
              })
              resolve()
            }).catch(error => {
              reject(error)
            })
          })
        },
        // 还原一个已删除消息到已读消息
        restoreTrash ({ commit }, { msg_id }) {
          return new Promise((resolve, reject) => {
            restoreTrash(msg_id).then(() => {
              commit('moveMsg', {
                from: 'messageTrashList',
                to: 'messageReadedList',
                msg_id
              })
              resolve()
            }).catch(error => {
              reject(error)
            })
          })
        }
      }
    }
  • 相关阅读:
    休息一会。。。。。。
    iPhone开发之webview 拖动和显示本地图片的几组代码
    Android 防止手机休眠
    Android数据存储SQLite 事务操作
    解决png图片在IE6下的透明问题
    教你如何在博客园放“可运行"代码
    弹出层原理
    右键集成JS文件压缩 YUI Compressor
    原来window.event快达到全浏览器支持了
    QWrap入门指南
  • 原文地址:https://www.cnblogs.com/pangchunlei/p/12118391.html
Copyright © 2011-2022 走看看