zoukankan      html  css  js  c++  java
  • Vuex实现状态管理

    Vuex使用总结

    1 Vuex简介

      Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式,Vuex抽取了各个组件的共享部分,以全局单例模式进行状态的管理。在原生vue中各个组件之间传值使用的是props和event,如果组件嵌套层数过多使用props进行传参会十分繁琐,vuex使用“唯一数据源”进行管理,所有的组件直接从vuex获取数据即可;在使用原生vue时,如果多个视图共享同一个状态的话,当一个视图修改这一状态,我们需要同步其他视图的状态,而vuex中的状态值都是响应式的,状态值一旦被修改,所有引用该值的地方就会自动更新。

      通过一个商品列表的栗子来演示一下Vuex的用法,首先看一下使用props进行传参的用法,添加两个组件:ProductListOne和ProductListTwe,两个组件都展示商品的名字和价格,组件代码如下:

    <!-----------------------------App.vue--------------------------------->
    <template>
      <div id="app">
        <production-list-one :products="products"></production-list-one>
        <production-list-twe :products="products"></production-list-twe>
      </div>
    </template>
    
    <script>
    import ProductListOne from "./components/ProductListOne.vue";
    import ProductListTwe from "./components/ProductListTwe.vue";
    export default {
      name: "app",
      components: {
        "production-list-one": ProductListOne,
        "production-list-twe": ProductListTwe
      },
      data() {
        return {
          products: [
            { id: 1, name: "电视", price: 2000 },
            { id: 2, name: "电脑", price: 5000 },
            { id: 3, name: "空调", price: 1500 },
            { id: 4, name: "冰箱", price: 3000 }
          ]
        };
      }
    };
    </script>
    
    <!-----------------------------ProductListOne.vue--------------------------------->
    <template>
      <div id="production-list-one">
      <h4>Product list one </h4>
      <ul>
        <li  v-for="product in products" v-bind:key="product.id">
          <span class=name>{{product.name}}</span>
          <span class=price>¥{{product.price}}</span>
        </li>
      </ul>
      </div>
    </template>
    
    <script>
    export default {
      props:["products"],
      data() {
        return {
        
        };
      }
    };
    </script>
    
    
    <!-----------------------------ProductListTwe.vue--------------------------------->
    <template>
      <div id="product-list-twe">
        <h4>Product list twe</h4>
        <ul>
          <li v-for="product in products" v-bind:key="product.id">
            <span class="name">{{product.name}}</span>
            <span class="price">¥{{product.price}}</span>
          </li>
        </ul>
      </div>
    </template>
    <script>
    export default {
      props: ["products"],
      data() {
        return {};
      }
    }
    </script>

      运行程序后,显示如下:

    state

       前边父组件App.vue中的数据products通过props传到这两个组件中,现在我们使用vuex来保存products,首先使用 cnpm install vuex --save 添加vuex包,然后添加一个store.js文件来保持数据,该文件代码如下,其中store对象作为“唯一数据源”而存在,所以每个应用都仅仅包含一个store实例,在store对象中state用于保存原始数据:

    import Vue from 'vue'
    import Vuex from 'vuex'
    Vue.use(Vuex)
    export const store = new Vuex.Store({
      state: {
        products: [
            {id: 1,name: "洗衣机",price: 2000},
            {id: 2,name: "电脑",price: 5000},
            {id: 3,name: "空调",price: 1500},
            {id: 4,name: "冰箱",price: 3000}
        ]
      }
    }) 

      接着在main.js中引入vuex,如下:

    import Vue from 'vue'
    import App from './App.vue'
    import {store} from './store/store.js'
    new Vue({
      store:store,
      el: '#app',
      render: h => h(App)
    })

      然后修改组件如下:

    <!-----------------------------App.vue--------------------------------->
    <template>
      <div id="app">
        <production-list-one></production-list-one>
        <production-list-twe></production-list-twe>
      </div>
    </template>
    
    <script>
    import ProductListOne from "./components/ProductListOne.vue";
    import ProductListTwe from "./components/ProductListTwe.vue";
    export default {
      name: "app",
      components: {
        "production-list-one": ProductListOne,
        "production-list-twe": ProductListTwe
      },
      data() {return {};}
    };
    </script>
    
    
    
    <!-----------------------------ProductListOne.vue--------------------------------->
    <template>
      <div id="production-list-one">
      <h4>Product list one </h4>
      <ul>
        <li  v-for="product in saleProducts1" v-bind:key="product.id">
          <span class=name>{{product.name}}</span>
          <span class=price>¥{{product.price}}</span>
        </li>
      </ul>
      </div>
    </template>
    
    <script>
    export default {
        computed:{
          saleProducts1(){
            return this.$store.state.products
          }
      }
    };
    </script>
    
    <!-----------------------------ProductListTwe.vue--------------------------------->
    <template>
      <div id="product-list-twe">
        <h4>Product list twe</h4>
        <ul>
          <li v-for="product in salesProducts2" v-bind:key="product.id">
            <span class="name">{{product.name}}</span>
            <span class="price">¥{{product.price}}</span>
          </li>
        </ul>
      </div>
    </template>
    
    <script>
    export default {
      computed:{
          salesProducts2(){
            return this.$store.state.products
          }
      }
    };
    </script>

      运行程序后,显示和使用props传值的效果一样,两个列表组件的数据源都不在通过props获取,而是通过 this.$store.state.products 从vuex中获取,到这里我们已经完成了vuex的简单使用。

    getters

      在vuex中state属性保存的是原始状态,有时候我们需要展示的数据是一些派生状态,就是是对state中的原始数据做一定的逻辑处理后数据,如我们展示的商品列表的价格是打八折后的价格。对应这种需求,我们可以先在组件中使用this.$store.state.xxx获取到原始数据然后在组件中直接写逻辑代码进行处理,但是如果逻辑处理获取的数据在多个组件中都要使用的话,这就需要在每个组件中都重复一遍逻辑代码。为了减少代码冗余,我们可以使用getter属性把"八折"这个共享的逻辑提取出来,实现很简单,直接看代码吧

    修改store.js,代码如下:

    import Vue from 'vue'
    import Vuex from 'vuex'
    Vue.use(Vuex)
    export const store = new Vuex.Store({
      state: {
        products: [
            {id: 1,name: "洗衣机",price: 2000},
            {id: 2,name: "电脑",price: 5000},
            {id: 3,name: "空调",price: 1500},
            {id: 4,name: "冰箱",price: 3000}
        ]
      },
      getters: {
        saleProducts(state) {
            var saleProducts=state.products.map((product)=>{
                return {id:product.id,name:`产品${product.id}:${product.name} `,price:product.price*0.8}
            })
            return saleProducts;
        }
      }
    }) 

    修改组件代码如下:

    <!-----------------------------App.vue--------------------------------->
    <template>
      <div id="app">
        <production-list-one></production-list-one>
        <production-list-twe></production-list-twe>
      </div>
    </template>
    
    <script>
    import ProductListOne from "./components/ProductListOne.vue";
    import ProductListTwe from "./components/ProductListTwe.vue";
    export default {
      name: "app",
      components: {
        "production-list-one": ProductListOne,
        "production-list-twe": ProductListTwe
      },
      data() {return {};}
    };
    </script>
    
    <!-----------------------------ProductListOne.vue--------------------------------->
    <template>
      <div id="production-list-one">
      <h4>Product list one </h4>
      <ul>
        <li  v-for="product in saleProducts1" v-bind:key="product.id">
          <span class=name>{{product.name}}</span>
          <span class=price>¥{{product.price}}</span>
        </li>
      </ul>
      </div>
    </template>
    
    <script>
    export default {
      computed:{
        saleProducts1(){
          return this.$store.getters.saleProducts
        }
      },
    };
    </script>
    
    <!-----------------------------ProductListTwe.vue--------------------------------->
    <template>
      <div id="product-list-twe">
        <h4>Product list twe</h4>
        <ul>
          <li v-for="product in saleProducts2" v-bind:key="product.id">
            <span class="name">{{product.name}}</span>
            <span class="price">¥{{product.price}}</span>
          </li>
        </ul>
      </div>
    </template>
    <script>
    export default {
      computed:{
          saleProducts2(){
           return this.$store.getters.saleProducts
          }
      }
    };
    </script>

      我们可以通过 this.$state.getters.xxx 去获取getters中的数据,运行程序后结果如下:

     mutations

      上边的state用于存储和获取原始值,getters负责封装公共逻辑,获取计算后的状态,两者都是获取数据时使用的。当我们想修改store中的状态怎么实现呢?提交mutations是更改vuex的stroe中状态的唯一方法。看一个需求:添加一个降价按钮,每次点击都会降价指定的金额。

      首先修改store.js

    import Vue from 'vue'
    import Vuex from 'vuex'
    Vue.use(Vuex)
    export const store = new Vuex.Store({
      state: {
        products: [
            {id: 1,name: "洗衣机",price: 2000},
            {id: 2,name: "电脑",price: 5000},
            {id: 3,name: "空调",price: 1500},
            {id: 4,name: "冰箱",price: 3000}
        ]
      },
      getters: {
        saleProducts(state) {
            var saleProducts=state.products.map((product)=>{
                return {id:product.id,name:`产品${product.id}:${product.name} `,price:product.price*0.8}
            })
            return saleProducts;
        }
      },
      mutations:{
        reducePrice:function(state,num){
            state.products.forEach(proudcts=>proudcts.price-=num)
          }
      }
    }) 

      修改组件如下:

    <!-----------------------------App.vue--------------------------------->
    <template>
      <div id="app">
        <production-list-one></production-list-one>
        <production-list-twe></production-list-twe>
      </div>
    </template>
    
    <script>
    import ProductListOne from "./components/ProductListOne.vue";
    import ProductListTwe from "./components/ProductListTwe.vue";
    export default {
      name: "app",
      components: {
        "production-list-one": ProductListOne,
        "production-list-twe": ProductListTwe
      },
      data() {return {};}
    };
    </script>
    
    
    <!-----------------------------ProductListOne.vue--------------------------------->
    <template>
      <div id="production-list-one">
      <h4>Product list one </h4>
      <ul>
        <li  v-for="product in saleProducts1" v-bind:key="product.id">
          <span class=name>{{product.name}}</span>
          <span class=price>¥{{product.price}}</span>
        </li>
      </ul>
      <button @click="reducePrice(10)">商品降价</button>
      </div>
    </template>
    
    <script>
    export default {
    //使用vuex中的getters
      computed:{
        saleProducts1(){
          return this.$store.getters.saleProducts
        }
      },
    
    //使用mutations降价
      methods:{
         reducePrice:function(num){
          this.$store.commit('reducePrice',num)
        }
      }
    };
    </script>
    
    <!-----------------------------ProductListOne.vue--------------------------------->
    <template>
      <div id="product-list-twe">
        <h4>Product list twe</h4>
        <ul>
          <li v-for="product in saleProducts2" v-bind:key="product.id">
            <span class="name">{{product.name}}</span>
            <span class="price">¥{{product.price}}</span>
          </li>
        </ul>
      </div>
    </template>
    <script>
    export default {
      computed:{
          saleProducts2(){return this.$store.getters.saleProducts}
      }
    };
    </script>

      刷新页面,效果如下:

    actions

      使用mutations我们可以直接修改store中的原始值,但是官方不推荐这种做法,官方推荐需要修改store中的值时,我们首先要提交一个action,在action中提交mutation来修改状态值。这种方式方便我们进行调试,同时容易实现异步操作。还是使用降价的栗子,我们点击按钮2秒后实现降价。action的参数是一个和store具有相同方法和属性的context对象,我们可以通过 context.state 和 context.getters 来获取state和getters,也可以使用 context.commit(mutation,payload) 来提交一个mutation,使用首先修改store.js:

    import Vue from 'vue'
    import Vuex from 'vuex'
    Vue.use(Vuex)
    export const store = new Vuex.Store({
      state: {
        products: [
            {id: 1,name: "洗衣机",price: 2000},
            {id: 2,name: "电脑",price: 5000},
            {id: 3,name: "空调",price: 1500},
            {id: 4,name: "冰箱",price: 3000}
        ]
      },
      getters: {
        saleProducts(state) {
            var saleProducts=state.products.map((product)=>{
                return {id:product.id,name:`产品${product.id}:${product.name} `,price:product.price*0.8}
            })
            return saleProducts;
        }
      },
      mutations:{
        reducePrice:function(state,num){
            state.products.forEach(proudcts=>proudcts.price-=num)
          }
      },
    //两秒后在提交reducePrice,context是上下文相当于组件中的this.$store actions:{ reducePriceAction:(context,num)=>{ setTimeout(function(){ context.commit("reducePrice",num) },2000) } } })

      修改组件如下:

    <!-----------------------------App.vue--------------------------------->
    <template>
      <div id="app">
        <production-list-one></production-list-one>
        <production-list-twe></production-list-twe>
      </div>
    </template>
    
    <script>
    import ProductListOne from "./components/ProductListOne.vue";
    import ProductListTwe from "./components/ProductListTwe.vue";
    export default {
      name: "app",
      components: {
        "production-list-one": ProductListOne,
        "production-list-twe": ProductListTwe
      },
      data() {return {};}
    };
    </script>
    
    <!-----------------------------ProductListOne.vue--------------------------------->
    <template>
      <div id="production-list-one">
        <h4>Product list one</h4>
        <ul>
          <li v-for="product in saleProducts1" v-bind:key="product.id">
            <span class="name">{{product.name}}</span>
            <span class="price">¥{{product.price}}</span>
          </li>
        </ul>
        <button @click="reducePrice(10)">商品降价</button>
      </div>
    </template>
    
    <script>
    export default {
      //使用vuex中的getters
      computed: {
        saleProducts1() {
          return this.$store.getters.saleProducts;
        }
      },
    
      //使用action降价,每次降价20元
      methods: {
        reducePrice: function(num) {
          this.$store.dispatch("reducePriceAction", num);
        }
      }
    };
    </script>
    
    
    <!-----------------------------ProductListTwe.vue--------------------------------->
    <template>
      <div id="product-list-twe">
        <h4>Product list twe</h4>
        <ul>
          <li v-for="product in saleProducts2" v-bind:key="product.id">
            <span class="name">{{product.name}}</span>
            <span class="price">¥{{product.price}}</span>
          </li>
        </ul>
      </div>
    </template>
    <script>
    export default {
      computed:{
          saleProducts2(){
           return this.$store.getters.saleProducts
          }
      }
    };
    </script>

      实现效果如下:

       本文是vuex的简单入门笔记,更高的特性在以后开发中遇到了在做研究,如果文中有错误希望大家可以指出,我会及时改正。

  • 相关阅读:
    架构师之路--视频业务介绍,离线服务架构和各种集群原理
    架构师之路--怎样聊技术天,限流技术和各类编程语言
    一条项目中常用的linux命令引发的经典算法题
    架构师之路--应用架构的选型和dubbo
    乐视开放平台技术架构-servlet和spring mvc篇
    架构师之路--从业务角度谈缓存的选型
    架构师之路--谈业务的合理架构
    IO回忆录之怎样过目不忘(BIO/NIO/AIO/Netty)
    520特篇-爱的境界
    编程十年的十种武学境界
  • 原文地址:https://www.cnblogs.com/wyy1234/p/10278538.html
Copyright © 2011-2022 走看看