• Vuex学习笔记


    Vuex 是什么?

    Vuex 是一个专为 Vue.js应用程序开发的状态管理模式。由于SPA应用的模块化,每个组件都有它各自的数据(state)、视图(view)和方法(actions),当项目内容越来越多时,每个组件中的状态就变得很难管理。Vuex 就是采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

    1、单个组件中的状态

    看一下官网提供的计数示例:

    复制代码
    <template>
      <div>
          <button class="btn btn-success" @click="increment">increment</button>
          view: {{count}}
      </div>
    </template>
    
    <script>
        export default {
            // state
            data () {
                return {
                    count: 0
                }
            },
            // actions
            methods: {
                increment () {
                    this.count++
                }
            }
        }
    </script>
    复制代码

    运行结果:

    从效果图中可以直观的看到,每点击一次按钮触发添加事件(actions),数据count(state)就会发生改变,然后映射到视图界面(view)中。

    下图可以表示 “ 单项数据流 ” 理念的极简示意:

    这个状态管理应用包含以下几个部分:

    • state:驱动应用的数据源

    • view:以声明方式将 state 映射到视图

    • actions:响应在 view 上的用户输入导致的状态变化

    2、多个组件中的状态

    当我们的应用遇到 多个组件共享状态 时,单向数据流的简洁性很容易被破坏:

    • 多个视图依赖于同一状态

    • 来自不同视图的行为需要变更同一状态

    同样是计数器,我们现在更换一种场景,两个相同的组件A和B,共享一个数据count,并且都有一个方法可以操作这个count(是不是跟上面提到的多组件共享状态描述的一样呢)

    复制代码
    // 组件A
    <template>
      <div>
        {{ $store.state.count }}
        <button @click="increment">组件A</button>
      </div>
    </template>
    
    <script>
      export default {
        methods: {
          increment () {
            this.$store.commit('increment')
          }
        }
      }
    </script>
    
    //组件B
    <template>
      <div>
        {{ $store.state.count }}
        <button @click="increment">组件B</button>
      </div>
    </template>
    
    <script>
      export default {
        methods: {
          increment () {
            this.$store.commit('increment')
          }
        }
      }
    </script>
    复制代码

    运行效果:

    从图中可以看到,“组件A” 和 “组件B” 两个按钮 会同时改变两个 count 的数据,因为数据源 count 和 方法increment 都是全局的。如下图所示,我们把 全局数据源 state改变数据源的方法 mutations 和 异步操作方法 actions 提取出来放到 store 中,实现全局数据状态单独管理的功能

    安装 

    1、使用 npm 安装并保存到 package.json 中

    npm install vuex --save

    package.json

    复制代码
    "dependencies": {
        ...,
        ...,
        ...,
        "vuex": "^2.4.1"
      },
    复制代码

    2、配置

    复制代码
    // 如果在模块化构建系统中,请确保在开头调用了 Vue.use(Vuex)
    import Vue from 'vue'
    import Vuex from 'vuex'
    
    Vue.use(Vuex)
    
    //创建Store实例
    const store = new Vuex.Store({
      // 存储状态值
      state: {
        ...
      },
      // 状态值的改变方法,操作状态值
      // 提交mutations是更改Vuex状态的唯一方法
      mutations: {
        ...
      },
      // 在store中定义getters(可以认为是store的计算属性)。Getters接收state作为其第一个函数
      getters: {
        ...
      },
      actions: { 
        ...
      }
    })
    // 要改变状态值只能通过提交mutations来完成
    
    /* eslint-disable no-new */
    const app = new Vue({
        router,
        i18n,
        // 将 store 实例注入到根组件下的所有子组件中,子组件通过 this.$store 来访问store
        store,
        ...App
    })
    
    app.$mount('#app')
    复制代码

    看一下官网提供的例子:

    复制代码
    <template>
      <div>
            <p>{{ count }}</p>
            <p>
                <button @click="increment">+</button>
                <button @click="decrement">-</button>
            </p>
      </div>
    </template>
    
    <script>
        export default {
            computed: {
                count () {
                    // 通过 store.state 来获取状态对象
                    return this.$store.state.count
                }
            },
            methods: {
                increment () {
                    // 通过 store.commit 方法触发状态变更
                    this.$store.commit('increment')
                },
                decrement () {
                    this.$store.commit('decrement')
                }
            }
        }
    </script>
    复制代码
    复制代码
    // 创建 Store 实例
    const store = new Vuex.Store({
        // 存储状态值
        state: {
            count: 0
        },
        // 状态值的改变方法,操作状态值
        // 提交 mutations 是更改Vuex状态的唯一方法
        mutations: {
            increment: state => state.count++,
            decrement: state => state.count--
        }
    })
    复制代码

    运行效果:

    核心概念

    1、State

    state 就是全局的状态(数据源),从前面的例子中看到我们可以按如下方式获取 Vuex 的state 状态

    // html 中
    {{ $store.state.count }}
    
    // js 中
    this.$store.state.count

    2、Getter

    getter 可以认为是 store 的计算属性,跟计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会重新计算

    如下官网提供的案例:

    computed: {
      doneTodosCount () {
        return this.$store.state.todos.filter(todo => todo.done).length
      }
    }

    如果有多个组件需要用到此属性,我们要么复制这个函数,或者抽取到一个共享函数然后在多处导入它,然而这两种方法都不是很理想,最佳方式当然是使用 getter 了

    我们尝试使用下getter

    (1)、定义 getter

    复制代码
    const store = new Vuex.Store({
        state: {
            count: 0
        },
        getters: {
            formatMoney: state => {
                return '¥'+state.count.toFixed(2)+'元'
            }
        },
        mutations: {
            increment: state => state.count++
        }
    })
    复制代码

    (2)、在组件中引用 getter

    复制代码
    export default {
        methods: {
          increment () {
            this.$store.commit('increment')
            // 这里为了更清楚的看到计算后的值
            let aaa = document.getElementById('aaa')
            let p = document.createElement('p')
            p.innerHTML = this.$store.getters.formatMoney
            aaa.appendChild(p)
          }
        },
        computed: {
            formatMoney() {
                return this.$store.getters.formatMoney
            }
        }
      }
    复制代码

    效果:

    3、Mutation

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

    复制代码
    const store = new Vuex.Store({
      state: {
        count: 1
      },
      mutations: {
        increment (state) {
          // 变更状态
          state.count++
        }
      }
    })
    复制代码

    要唤醒一个 mutation handler,你需要以相应的 type 调用 store.commit 方法

    store.commit('increment')

    (1)、提交载荷(Payload)

    载荷(payload)就是说 可以向 store.commit 传入额外的参数

    复制代码
    // ...
    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
    })
    复制代码

    4、Action

    Vuex 中一条重要的原则就是 mutation 必须是同步函数, action 类似于 mutation,不同之处在于:

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

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

    复制代码
    const store = new Vuex.Store({
      state: {
        count: 0
      },
      mutations: {
        increment (state) {
          state.count++
        }
      },
      actions: {
        increment (context) {
          context.commit('increment')
        },
        // 异步
        incrementAsync (context) {
          // 延时1秒
          setTimeout(() => {
            context.commit('increment')
          }, 1000)
        }
      } 
    })
    复制代码

    Action 函数接受一个与 store 实例具有相同方法和属性的context对象,因此,可以有以下调用方法

    • context.commit  提交一个 mutation

    • context.state  获取 state

    • context.getters   获取 getters

    不同于 mutation 使用 commit 方法,action 使用 dispatch 方法

    store.dispatch('increment')

    Actions 同样支持 载荷方式 和 对象方式 进行分发:

    复制代码
    // 以载荷形式分发
    store.dispatch('incrementAsync', {
      amount: 10
    })
    
    // 以对象形式分发
    store.dispatch({
      type: 'incrementAsync',
      amount: 10
    })
    复制代码

    5、Module

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

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

    复制代码
    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 的状态
    复制代码

    关于项目结构,我们可以看看官网提供的示例:

    复制代码
    ├── index.html
    ├── main.js
    ├── api
    │   └── ... # 抽取出API请求
    ├── components
    │   ├── App.vue
    │   └── ...
    └── store
        ├── index.js          # 我们组装模块并导出 store 的地方
        ├── actions.js        # 根级别的 action
        ├── mutations.js      # 根级别的 mutation
        └── modules
            ├── cart.js       # 购物车模块
            └── products.js   # 产品模块
    复制代码

    官网同时也提供了一个 购物车 示例:

    app.js 文件如下:

    复制代码
    import 'babel-polyfill'
    import Vue from 'vue'
    import App from './components/App.vue'
    import store from './store'
    import { currency } from './currency'
    
    Vue.filter('currency', currency)
    
    new Vue({
      el: '#app',
      store,
      render: h => h(App)
    })
    复制代码

    index.js 文件如下:

    复制代码
    import Vue from 'vue'
    import Vuex from 'vuex'
    import * as actions from './actions'
    import * as getters from './getters'
    import cart from './modules/cart'
    import products from './modules/products'
    import createLogger from '../../../src/plugins/logger'
    
    Vue.use(Vuex)
    
    const debug = process.env.NODE_ENV !== 'production'
    
    export default new Vuex.Store({
      actions,
      getters,
      modules: {
        cart,
        products
      },
      strict: debug,
      plugins: debug ? [createLogger()] : []
    })
    复制代码

    getters.js 文件如下:

    复制代码
    export const cartProducts = state => {
      return state.cart.added.map(({ id, quantity }) => {
        const product = state.products.all.find(p => p.id === id)
        return {
          title: product.title,
          price: product.price,
          quantity
        }
      })
    }
    复制代码

    actions.js 文件如下:

    复制代码
    import * as types from './mutation-types'
    
    export const addToCart = ({ commit }, product) => {
      if (product.inventory > 0) {
        commit(types.ADD_TO_CART, {
          id: product.id
        })
      }
    }
    复制代码

    mutation-type.js 文件如下:

    export const ADD_TO_CART = 'ADD_TO_CART'
    export const CHECKOUT_REQUEST = 'CHECKOUT_REQUEST'
    export const CHECKOUT_SUCCESS = 'CHECKOUT_SUCCESS'
    export const CHECKOUT_FAILURE = 'CHECKOUT_FAILURE'
    export const RECEIVE_PRODUCTS = 'RECEIVE_PRODUCTS'

    cart.js 文件如下:

    复制代码
    import shop from '../../api/shop'
    import * as types from '../mutation-types'
    
    // initial state
    // shape: [{ id, quantity }]
    const state = {
      added: [],
      checkoutStatus: null
    }
    
    // getters
    const getters = {
      checkoutStatus: state => state.checkoutStatus
    }
    
    // actions
    const actions = {
      checkout ({ commit, state }, products) {
        const savedCartItems = [...state.added]
        commit(types.CHECKOUT_REQUEST)
        shop.buyProducts(
          products,
          () => commit(types.CHECKOUT_SUCCESS),
          () => commit(types.CHECKOUT_FAILURE, { savedCartItems })
        )
      }
    }
    
    // mutations
    const mutations = {
      [types.ADD_TO_CART] (state, { id }) {
        state.lastCheckout = null
        const record = state.added.find(p => p.id === id)
        if (!record) {
          state.added.push({
            id,
            quantity: 1
          })
        } else {
          record.quantity++
        }
      },
    
      [types.CHECKOUT_REQUEST] (state) {
        // clear cart
        state.added = []
        state.checkoutStatus = null
      },
    
      [types.CHECKOUT_SUCCESS] (state) {
        state.checkoutStatus = 'successful'
      },
    
      [types.CHECKOUT_FAILURE] (state, { savedCartItems }) {
        // rollback to the cart saved before sending the request
        state.added = savedCartItems
        state.checkoutStatus = 'failed'
      }
    }
    
    export default {
      state,
      getters,
      actions,
      mutations
    }
    复制代码

    products.js 文件如下:

    复制代码
    import shop from '../../api/shop'
    import * as types from '../mutation-types'
    
    // initial state
    const state = {
      all: []
    }
    
    // getters
    const getters = {
      allProducts: state => state.all
    }
    
    // actions
    const actions = {
      getAllProducts ({ commit }) {
        shop.getProducts(products => {
          commit(types.RECEIVE_PRODUCTS, { products })
        })
      }
    }
    
    // mutations
    const mutations = {
      [types.RECEIVE_PRODUCTS] (state, { products }) {
        state.all = products
      },
    
      [types.ADD_TO_CART] (state, { id }) {
        state.all.find(p => p.id === id).inventory--
      }
    }
    
    export default {
      state,
      getters,
      actions,
      mutations
    }
    复制代码

    购物车运行效果:

  • 相关阅读:
    linux学习之centos(四):git的安装
    MongoDB学习
    linux学习之centos(三):mysql数据库的安装和配置
    面经中高频知识点归纳(三)
    各编程语言的内存分配方式
    carson常用linux命令整理
    在 Linux 虚拟机中手动安装或升级 VMware Tools
    Fidder 网络抓包调试工具
    面经中高频知识点归纳(二)
    java集合类
  • 原文地址:https://www.cnblogs.com/Bkxk/p/10649501.html
Copyright © 2020-2023  润新知