Bootstrap

从零到部署:用 Vue 和 Express 实现迷你全栈电商应用(五)

组件化和逻辑复用能帮助写出简洁易懂的代码,随着应用越写越复杂,我们有必要把视图层中重复的逻辑抽成组件,以求在多个页面中复用;同时对于 Vuex 端,Store 中的逻辑也会越来越臃肿,我们有必要使用 Vuex 提供的 Getters 来复用本地数据获取逻辑。在这篇教程中,我们将带领你抽出 Vue 组件简化页面逻辑,使用 Vuex Getters 复用本地数据获取逻辑。

使用 Vue 组件简化页面逻辑

在前面的教程中,我们已经学习了如何使用 Vuex 进行状态管理,如何使用 Action 获取远程数据以及如何使用 Mutation 修改本地状态,实现了用户修改客户端数据的同时,同步更新后端数据,然后更新本地数据,最后进行重新渲染。

这一节我们将进一步通过 Vue 组件化的思想简化复杂的页面逻辑。

实现 ProductButton 组件

我们打开 文件,它是用于操作商品在购物车中状态的按钮组件,代码如下:




该组件通过 判断 是否为 来决定创建加入购物车按钮还是从购物车移除按钮。 数组是通过 从本地获取的。在 中我们先令其为 ,然后通过 数组的 方法遍历数组,判断当前商品是否在购物车中,如果不在则 为 ,创建加入购物车按钮;如果在则 为 ,创建从购物车移除按钮。

对应的两个按钮添加了两个点击事件: 和

  • 当点击加入购物车按钮时触发 ,我们通过 的方式将包含当前商品的对象作为载荷直接提交到类型为 的 中,将该商品添加到本地购物车中。

  • 当点击从购物车移除按钮时触发,我们也是通过的方式将包含当前商品id的对象作为载荷直接提交到类型为的中,将该商品从本地购物车中移除。

实现 ProductItem 组件

文件为商品信息组件,用来展示商品详细信息,并且注册了上面讲的按钮组件,改变商品在购物车中的状态,除此之外我们还使用了之前创建好的组件,实现对商品在购物车中的状态进行修改。

  • 首先通过导入创建好的组件。

  • 然后在中注册组件。

  • 最后在模板中使用该组件。

代码如下:




可以看到,我们将父组件传入的对象展示到模板中,并将该对象传到子组件中。

重构 ProductList 组件

有了 ProductButton 和 ProductItem,我们便可以来重构之前略显臃肿的 ProductList 组件了,修改 ,代码如下:



        This is ProductList
      

这部分代码是将之前展示商品信息的逻辑代码封装到了子组件中,然后导入并注册子组件,再将子组件挂载到模板中。

可以看到,我们通过从本地获取数组,并返回给计算属性。然后在模板中利用遍历数组,并将每个对象传给每个子组件,在每个子组件中展示对应的商品信息。

重构 Cart 组件

最后,我们重构一波购物车组件 ,也使用了子组件简化了页面逻辑,修改代码如下:



      

{{msg}}

这里也是首先导入并注册子组件,然后在模板中挂载子组件。通过的方式从本地获取购物车数组,并返回给计算属性。在模板中通过遍历购物车数组,并将购物车中每个商品对象传给对应的子组件,通过子组件来展示对应的商品信息。

把项目开起来,查看商品列表,可以看到每个商品下面都增加了“添加到购物车”按钮:

购物车中,也有了“移出购物车”按钮:

尽情地买买买吧!

小结

这一节我们学习了如何使用 Vue 组件来简化页面逻辑:

使用 Vuex Getters 复用本地数据获取逻辑

在这一节中,我们将实现这个电商应用的商品详情页面。商品详情和之前商品列表在数据获取上的逻辑是非常一致的,能不能不写重复的代码呢?答案是肯定的。之前我们使用 Vuex 进行状态管理是通过 的方式获取本地数据,而在这一节我们使用 来复用本地数据的获取逻辑。

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

也是定义在 Vuex Store 的 属性中的一系列方法,用于获取本地状态中的数据。我们可以通过两种方式访问 ,一个是通过属性访问,另一个是通过方法访问:

allProducts(state) {
    // 返回本地中的数据
    return state.products;
}

productById: (state, getters) => id => {
      //通过传入的id参数进行一系列操作并返回本地数据
      return state.product;
  }

我们可以看到可以接受两个参数:和,就表示本地数据源;我们可以通过第二个参数获取到不同的属性。

定义 Vuex Getters

光说不练假把式,我们来手撸几个 getters。打开 文件,我们添加了一些需要用到的 属性、 属性以及这一节的主角—— 。代码如下:

// src/store/index.js
// ...

      state.showLoader = false;
      state.products = products;
    },
    PRODUCT_BY_ID(state) {
      state.showLoader = true;
    },
    PRODUCT_BY_ID_SUCCESS(state, payload) {
      state.showLoader = false;

      const { product } = payload;
      state.product = product;
    }
  },
  getters: {
    allProducts(state) {
      return state.products;
    },
    productById: (state, getters) => id => {
      if (getters.allProducts.length > 0) {
        return getters.allProducts.filter(p => p._id == id)[0];
      } else {
        return state.product;
      }
    }
  },
  actions: {
    // ...
      commit('ALL_PRODUCTS')

      axios.get(`${API_BASE}/products`).then(response => {
        commit('ALL_PRODUCTS_SUCCESS', {
          products: response.data,
        });
      })
    },
    productById({ commit }, payload) {
      commit('PRODUCT_BY_ID');

      const { productId } = payload;
      axios.get(`${API_BASE}/products/${productId}`).then(response => {
        commit('PRODUCT_BY_ID_SUCCESS', {
          product: response.data,
        });
      })
    }
  }
});

这里主要添加了三部分内容:

在后台 Products 组件中使用 Getters

我们先通过一个简单的例子演示如果使用 Vuex Getters。打开后台商品组件,,我们通过属性访问的方式调用对应的 属性,从而获取本地商品,代码如下:



export default {
  computed: {
    product() {
      return this.$store.getters.allProducts[0];
    }
  }
}

我们通过属性访问的方式调用对应中的属性,并返回本地商品数组中的第一个商品。

创建 ProductDetail 组件

接着开始实现商品详情组件 ,代码如下:






该组件将父组件传入的对象展示在了模板中,并复用了组件。

在 ProductItem 组件中添加链接

有了商品详情,我们还需要进入详情的链接。再次进入 文件中,我们对其进行了修改,将模板中的商品信息用 Vue 原生组件 包裹起来,实现商品信息可点击查看详情。代码如下:






该组件中定义了一个计算属性,用于返回本地状态中指定的商品。这里我们使用了方法访问的方式获取本地中指定的商品,这里的id参数通过从当前处于激活状态的路由对象中获取,并传入对应的中,进而从本地中获取指定商品。

在该组件刚被创建时判断当前本地中是否有该商品,如果没有则通过的方式将包含当前商品id的对象作为载荷分发到类型为的中,在中进行异步操作从后端获取指定商品,然后提交到对应的中进行本地状态修改,这已经使我们习惯的思路了。

配置 Detail 页面的路由

最后我们打开路由配置 文件,导入了 组件,并添加了对应的路由参数,代码如下:

// src/router/index.js
// ...

import Home from '@/pages/Home';
import Cart from '@/pages/Cart';
import Detail from '@/pages/Detail';

// Admin Components
import Index from '@/pages/admin/Index';
// ...
      name: 'Cart',
      component: Cart,
    },
    {
      path: '/detail/:id',
      name: 'Detail',
      component: Detail,
    }
  ],
});

又到了验收的环节,运行项目,点击单个商品,可以进入到商品详情页面,并且数据是完全一致的:

小结

这一节中我们学会了如何使用来复用本地数据的获取逻辑:

关于我们

Customize this section to tell your visitors a little bit about your publication, writers, content, or something else entirely. Totally up to you.