本文深入研究Vuex,一个Vue.js状态管理库。我们将介绍创建它是为了解决的问题、其背后的核心概念、如何设置它,当然,还将在每一步中使用代码示例。

Vuex是一个由Vue团队构建的状态管理库,用于管理Vue.js应用程序中的数据。它提供了一种集中管理跨应用程序使用的数据的方式,并允许轻松的读写操作。

为什么它?-Vuex

Vue提倡将视图分解为组件。这些组件是可重用的Vue实例,可以接受数据、方法等。数据是保存视图状态的地方,而方法是允许我们根据用户在视图上的交互操作该状态的地方。

当用户单击组件中的按钮时,将调用一个方法,该方法依次对所述状态执行操作,而所述状态则更新有关该更改的视图。

然而,有时多个组件需要共享一个状态,或者在一个组件中的状态被修改后,您需要父/子或兄弟组件执行后续操作。

根据第二个组件的位置,您可以决定使用或关键字直接访问第二个组件的数据或方法并执行上述操作。但是如果你必须为尽可能多的组件执行此操作呢?propsthis.$parent

随着项目越来越大,你会发现自己到处传递属性,并直接操作DOM来访问各种组件。

这种方法变得非常乏味,并且当你遇到错误时,也会使代码库难以维护或调试。这就是Vuex的光芒。它提供了一个全局作用域,您可以在其中放置将在各种组件之间共享的所有状态。

它还为我们的代码提供了更多的结构,使调试变得更容易,因为我们可以使用开发者工具来跟踪发生的错误,当然还提供了Vue带来的响应性。可以把它想象成JavaScript中的windows——每个组件都可以访问它

安装Vuex

npm install vuex --save

这将在您的项目中安装最新版本的Vuex。完成后,我们需要使用下面的代码创建我们的store.js文件,在Vue应用中初始化Vuex;

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

现在我们可以继续创建一个store。store本质上是一个响应式对象,它保存了应用程序的state、getters、mutations和actions。

理解Store

store本质上是中心化的状态,它有一些核心概念允许我们实现这种中心化。这些概念包括:

  1. State
  2. Getters
  3. Mutations
  4. Actions

State

这是一个包含整个数据的对象。这类似于单组件结构中的关键字,除了此状态可以从多个组件访问,并且当此状态更新时,所有访问它的组件也会收到此更改。要创建这个对象,我们做以下操作

// 导入vue
import Vue from 'vue';
// 导入 Vuex
import Vuex from 'vuex';

// 安装 Vuex 插件到vue中
Vue.use(Vuex);

// create a Vuex store instance
export const store = new Vuex.Store({
    state: {
        cart: ''
    }
})

要在我们的Vue组件中访问Vuex状态,我们必须首先通过创建一个返回该状态的计算属性将store导入到组件中,然后将该状态渲染到视图中。

现在,让我们导入store。主要有两种方法:

  1. 在你将要使用Vuex状态的每个组件中手动导入store,如下所示:
<template>
    <main>
        <h1>Cart Content</h1>
        <p>{{cartValue}}</p>
    </main>
</template>

<script>
// Import Vuex Store into Component
import store from 'store.js';
export default {
    computed: {
        cartValue() {
            // Return Vuex state from store
            return store.state.cart;
        }
    }
}
</script>

  2.将Vuex store全局注入到Vue实例中,这自动使我们可以使用

import Vue from 'vue';
import store from './store.js';

new Vue({
    // Adding the Vuex store to the Vue instance
    store,
}).$mount('#app');
<template>
    <main>
        <h1>Cart Content</h1>
        <p>{{cartValue}}</p>
    </main>
</template>

<script>
export default {
    computed: {
        cartValue() {
            // Accessing the Vuex state
            return this.$store.state.cart;
        }
    }
}
</script>

Getter

getter几乎是Vuex存储的计算属性。它们允许我们基于当前状态生成一个新的状态——例如,计算购物车中有多少商品。

它还有助于减少重复代码,理想情况下,多个组件需要这些数据,而我们通常必须在每个组件中进行操作。有了getter,我们可以做一次,并在任何地方引用。

要创建getter,我们做如下操作:

// import Vue
import Vue from 'vue';
// import Vuex
import Vuex from 'vuex';

// Install the Vuex plugin on vue
Vue.use(Vuex);

// create a Vuex store instance
export const store = new Vuex.Store({
    state: {
        cart: ["bread", "rice", "beans", "turkey"]
    },
  
    getters: {
        // Fetch the total number of items in the cart
        totalNumberOfCartItems: state => {
            return state.cart.length;
        },
    },
})

接下来,我们通过以下方式从我们的Vue组件中访问getter:

<template>
    <main>
        <h1>Cart Content</h1>
        <p>Total Number of Items: {{totalNumberOfCartItems}}</p>
    </main>
</template>

<script>
export default {
    computed: {
        totalNumberOfCartItems() {
            // Accessing the Vuex state
            return this.$store.getters.totalNumberOfCartItems;
        }
    }
}
</script>

现在,只要有商品被添加到购物车中,购物车中的总商品数就会自动更新。

Mutations

mutation是我们更新Vuex状态的唯一方式。它们只执行一个任务:设置一个状态。它是一个有两个参数的函数,state和payload,其中payload不是必需的。

有效载荷只是用于更新状态的数据。mutation是同步的,因此我们不能在它们中执行异步任务。

现在,让我们给代码添加一个mutation:

// import Vue
import Vue from 'vue';
// import Vuex
import Vuex from 'vuex';

// Install the Vuex plugin on vue
Vue.use(Vuex);

// create a Vuex store instance
export const store = new Vuex.Store({
    state: {
        cart: ["bread", "rice", "beans", "turkey"]
    },
  
    getters: {
        // Fetch the total number of items in the cart
        totalNumberOfCartItems: state => {
            return state.cart.length;
        },
    },
    
    mutations: {
        // Add item to cart
        addItemToCart (state, payload) {
            state.cart.push(payload);
        },
    },
})

接下来,我们需要从我们的Vue组件中更新状态,为此,我们需要提交mutation。

<template>
    <main>
        <h1>Cart Content</h1>
        <p>Total Number of Items: {{totalNumberOfCartItems}}</p>
        <form @submit.prevent="addItemToCart">
            <input type="text" v-model="item" required>
            <button type="submit">Add to cart</button>
        </form>
    </main>
</template>

<script>
export default {
    data() {
        return {
            item: ''
        }
    },
    computed: {
        totalNumberOfCartItems() {
            // Accessing the Vuex state
            return this.$store.getters.totalNumberOfCartItems;
        }
    },
    methods: {
        addItemToCart() {
            // Check that the input field isn't empty
            if(this.item !== '') {
                // commiting the additemtocart mutation with the payload
                this.$store.commit('addItemToCart', this.item)
            }
        }
    }
}
</script>

现在,只要用户在输入框中输入一个值,并单击提交按钮,商品就会添加到购物车,购物车中的商品总数也会在视图中更新。

Actions

Actions类似于mutation,但是它们提交了mutation,而不是改变状态。它们是异步的,因此允许我们执行异步任务;当这些任务完成时,我们继续提交一个mutation,它反过来更新状态。

为了展示操作,我们将继续向API提交购物车商品。

// import Vue
import Vue from 'vue';
// import Vuex
import Vuex from 'vuex';

// Install the Vuex plugin on vue
Vue.use(Vuex);

// create a Vuex store instance
export const store = new Vuex.Store({
    state: {
        cart: ["bread", "rice", "beans", "turkey"]
    },
  
    getters: {
        // Fetch the total number of items in the cart
        totalNumberOfCartItems: state => {
            return state.cart.length;
        },
    },
    
    mutations: {
        // Add item to cart
        addItemToCart (state, payload) {
            state.cart.push(payload);
        },
        // Clear items in the cart
        emtpyCart (state) {
            state.cart = [];
        }
    },
    
    actions: {
        checkout({commit}, requestObject) {
            // API Call to submit the items in the cart
            Vue.http.post('submit', requestObject).then((response) => {
                // log success
                console.log(response);
                // Clear Cart by mutating the state
                commit('emptyCart');
            }).catch((error) => {
                // log error
                console.log(error);
            }
        }
    }
})

看看上面的代码,我们创建了一个名为action的操作,它接受两个东西 checkout

Commit:允许我们在actions中调用Commit方法

requestObject:允许我们将数据传递给动作

进入action,我们对API进行异步调用,然后将传递给API。成功后,我们记录响应,然后继续清除cart状态,但首先我们必须创建一个mutation,其唯一的任务是清空cart状态。requestObjectemptyCart

现在我们已经看到了如何创建操作,我们继续触发该操作。为了触发一个动作,Vuex为我们提供了一个命令dispatch

this.$store.dispatch('actionName', payload);

让我们在代码中添加一个action,并从视图中dispatch它:

<template>
    <main>
        <h1>Cart Content</h1>
        <p>Total Number of Items: {{totalNumberOfCartItems}}</p>
        <form @submit.prevent="addItemToCart">
            <input type="text" v-model="item" required>
            <button type="submit">Add to cart</button>
        </form>
        
        <button type="button" @click="checkout">Checkout</button>
    </main>
</template>

<script>
export default {
    data() {
        return {
            item: ''
        }
    },
    computed: {
        totalNumberOfCartItems() {
            // Accessing the Vuex state
            return this.$store.getters.totalNumberOfCartItems;
        }
    },
    methods: {
        addItemToCart() {
            // Check that the input field isn't empty
            if(this.item !== '') {
                // commiting the additemtocart mutation with the payload
                this.$store.commit('addItemToCart', this.item)
            }
        },
        
        checkout() {
            // Make sure cart is not empty
            if(this.totalNumberOfCartItems > 0 ) {
                // create request
                let requestPayload = { cart: this.$store.state.cart };
                // Dispatch the action
                this.$store.dispatch('checkout', requestPayload);
            }
            else {
                alert('Cart is empty');
            }
        }
    }
}
</script>

根据上面的代码,我们在视图中创建了一个checkout按钮,并创建了一个checkout方法,在尝试调度提交商品的操作之前检查购物车是否为空。

这是可行的,但缺少了一些东西。你可能想知道这是什么?我们已经能够dispatch一个action,但我们不知道这个action是否成功。

API调用失败了吗?通过了吗?我如何获取这些信息以便通知用户?Actions可以处理Promise,也可以返回一个Promise。

修改示例代码以返回一个Promise:

// import Vue
import Vue from 'vue';
// import Vuex
import Vuex from 'vuex';

// Install the Vuex plugin on vue
Vue.use(Vuex);

// create a Vuex store instance
export const store = new Vuex.Store({
    state: {
        cart: ["bread", "rice", "beans", "turkey"]
    },
  
    getters: {
        // Fetch the total number of items in the cart
        totalNumberOfCartItems: state => {
            return state.cart.length;
        },
    },
    
    mutations: {
        // Add item to cart
        addItemToCart (state, payload) {
            state.cart.push(payload);
        },
        // Clear items in the cart
        emtpyCart (state) {
            state.cart = [];
        }
    },
    
    actions: {
        checkout({commit}, requestObject) {
            return new Promise((resolve, reject) => {
                
                // API Call to submit the items in the cart
                Vue.http.post('submit', requestObject).then((response) => {
                    // log success
                    console.log(response);
                    // Clear Cart by mutating the state
                    commit('emptyCart');
                    // return success
                    resolve(response);
                }).catch((error) => {
                    // log error
                    console.log(error);
                    // return error
                    reject(error);
                }
            })
        }
    }
})

现在,我们可以使用返回的值更新视图的状态,如下所示:
 

<template>
    <main>
        <h1>Cart Content</h1>
        <p>Total Number of Items: {{totalNumberOfCartItems}}</p>
        <form @submit.prevent="addItemToCart">
            <input type="text" v-model="item" required>
            <button type="submit">Add to cart</button>
        </form>
        
        <button type="button" @click="checkout">Checkout</button>
    </main>
</template>

<script>
export default {
    data() {
        return {
            item: ''
        }
    },
    computed: {
        totalNumberOfCartItems() {
            // Accessing the Vuex state
            return this.$store.getters.totalNumberOfCartItems;
        }
    },
    methods: {
        addItemToCart() {
            // Check that the input field isn't empty
            if(this.item !== '') {
                // commiting the additemtocart mutation with the payload
                this.$store.commit('addItemToCart', this.item)
            }
        },
        
        checkout() {
            // Make sure cart is not empty
            if(this.totalNumberOfCartItems > 0 ) {
                // create request
                let requestPayload = { cart: this.$store.state.cart };
                // Dispatch the action
                this.$store.dispatch('checkout', requestPayload).then((response) => {
                    // Alert Response from API
                    alert(response);
                }).catch((error) => {
                    // Alert Error from API
                    alert(error);
                });
            }
            else {
                alert('Cart is empty');
            }
        }
    }
}
</script>

Actions还允许你dispatch多个action(即一个action可以dispatch一个或多个其他action)。你所要做的就是传递一个参数,然后你就可以在你的action中dispatch其他的action。

checkout({ dispatch, commit }, requestObject) {
    // dispatch an action
    dispatch('actionName');

    // dispatch another action
    dispatch('actionName2', request);
};

向Store添加结构

现在,我们所有的state、getter、mutations和actions都在一个文件中,即store.js文件。根据我们的代码库的大小,这个文件可能会变得非常大,因此我们将其拆分为单独的文件是有意义的。

store/
--| store.js
--| state.js
--| getters.js
--| mutations.js
--| actions.js

现在,我们的store看起来像这样:

import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import getters from './getters'
import mutations from './mutations'
import actions from './actions'

Vue.use(Vuex)

export default new Vuex.Store({
  state,
  getters,
  mutations,
  actions
})

Modules

Vuex还为我们提供了模块,我们可以进一步构建或将我们的store分解为更小的store模块。每个模块都有自己的state、getter、mutations和action。

它的工作原理是将相关的状态、获取器、突变和操作分组到一个模块中。当我们有一个大型应用程序,并且store中包含大量代码时,它非常有用。

将我们的store重构为一个模块,我们将创建一个名为cart.js的文件,并继续分解与购物车相关的所有状态、突变和操作,如下所示:

// import Vue
import Vue from 'vue';

export default {
    state: {
        cart: ["bread", "rice", "beans", "turkey"]
    },
  
    getters: {
        // Fetch the total number of items in the cart
        totalNumberOfCartItems: state => {
            return state.cart.length;
        },
    },
    
    mutations: {
        // Add item to cart
        addItemToCart (state, payload) {
            state.cart.push(payload);
        },
        // Clear items in the cart
        emtpyCart (state) {
            state.cart = [];
        }
    },
    
    actions: {
        checkout({commit}, requestObject) {
            return new Promise((resolve, reject) => {
                
                // API Call to submit the items in the cart
                Vue.http.post('submit', requestObject).then((response) => {
                    // log success
                    console.log(response);
                    // Clear Cart by mutating the state
                    commit('emptyCart');
                    // return success
                    resolve(response);
                }).catch((error) => {
                    // log error
                    console.log(error);
                    // return error
                    reject(error);
                }
            })
        }
    }
}

接下来,我们导入它并将其注册到我们的主store。

import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import getters from './getters'
import mutations from './mutations'
import actions from './actions'
import cart from './modules/cart'

Vue.use(Vuex)

export default new Vuex.Store({
  state,
  getters,
  mutations,
  actions,
  modules: {
      cart
  }
})

最后,我们的代码结构看起来像这样:

store/
--| store.js
--| state.js
--| getters.js
--| mutations.js
--| actions.js
--| modules/
    --| cart.js

总结

uex创建了一个store,它由state、getters、mutations和action组成。要更新或改变一个状态,你必须提交一个mutation。

要执行异步任务,你需要一个操作。action被分派,一旦成功就提交一个mutation,该mutation会改变一个状态,从而更新视图。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐