如何用Vue和Dinero.js建立购物车

如何用Vue和Dinero.js建立购物车

入门

对于这个项目,我们将使用vue-cli和简单的webapp-Vue.js模板。如果您的计算机上没有全局安装vue-cli,请启动您的终端并输入以下内容:

npm install -g vue-cli

然后:

vue init webpack-simple path/to/my-project

您可以保留所有问题的默认选项。完成后,导航到新目录,安装依赖项,然后运行项目:

cd path/to/my-project npm install npm run dev

Webpack将开始在端口上为您的项目提供服务8080(如果可用)并在您的浏览器中打开它。

设置HTML/CSS

在本教程中,我不会涉及页面结构和样式,所以我邀请您复制/粘贴代码。打开App.vue文件并粘贴以下代码片段。

这在<template>标签之间:

<div id =“app”> 
  <div class =“cart”> 
    <h1 class =“title”>订单</ h1> 
    <ul class =“items”> 
      <li class =“item”> 
        <div class =“ item-preview“> 
          <img src =”“alt =”“class =”item-thumbnail“> 
          <div> 
            <h2 class =”item-title“> </ h2> 
            <p class =”item-description“> </ p> 
          </ div> 
        </ div> 
        <div> 
          <input type =“text”class =“item-quantity”> 
          <span class =“item-price”> </ span>
        </ DIV> 
      </ LI> 
    </ UL> 
    <H3类= “购物车线”> 
      小计<跨度类= “购物价格”> </跨度> 
    </ H3> 
    <H3类= “购物车线” >
      运费<span class =“cart-price”> </ span> 
    </ h3> 
    <h3 class =“cart-line”> 
      总计<span class =“cart-price cart-total”> </ span> 
    </ h3 > 
  </ div> 
</ div>

CSS样式:

body {
  margin: 0;
  background: #fdca40;
  padding: 30px;
}
.title {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin: 0;
  text-transform: uppercase;
  font-size: 110%;
  font-weight: normal;
}
.items {
  margin: 0;
  padding: 0;
  list-style: none;
}
.cart {
  background: #fff;
  font-family: 'Helvetica Neue', Arial, sans-serif;
  font-size: 16px;
  color: #333a45;
  border-radius: 3px;
  padding: 30px;
}
.cart-line {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin: 20px 0 0 0;
  font-size: inherit;
  font-weight: normal;
  color: rgba(51, 58, 69, 0.8);
}
.cart-price {
  color: #333a45;
}
.cart-total {
  font-size: 130%;
}
.item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 15px 0;
  border-bottom: 2px solid rgba(51, 58, 69, 0.1);
}
.item-preview {
  display: flex;
  align-items: center;
}
.item-thumbnail {
  margin-right: 20px;
  border-radius: 3px;
}
.item-title {
  margin: 0 0 10px 0;
  font-size: inherit;
}
.item-description {
  margin: 0;
  color: rgba(51, 58, 69, 0.6);
}
.item-quantity {
  max-width: 30px;
  padding: 8px 12px;
  font-size: inherit;
  color: rgba(51, 58, 69, 0.8);
  border: 2px solid rgba(51, 58, 69, 0.1);
  border-radius: 3px;
  text-align: center;
}
.item-price {
  margin-left: 20px;
}

添加数据

在处理产品时,您通常会从数据库或API中检索原始数据。我们可以通过在单独的JSON文件中表示它,然后异步地导入它,就像查询API一样。

我们创建一个products.json文件assets/并添加以下内容:

{
  "items": [
    {
      "title": "Item 1",
      "description": "A wonderful product",
      "thumbnail": "https://fakeimg.pl/80x80",
      "quantity": 1,
      "price": 20
    },
    {
      "title": "Item 2",
      "description": "A wonderful product",
      "thumbnail": "https://fakeimg.pl/80x80",
      "quantity": 1,
      "price": 15
    },
    {
      "title": "Item 3",
      "description": "A wonderful product",
      "thumbnail": "https://fakeimg.pl/80x80",
      "quantity": 2,
      "price": 10
    }
  ],
  "shippingPrice": 20
}

这与我们从真实API获得的数据非常相似:数据作为集合,标题和文本作为字符串,数量和价格作为数字。

我们可以返回App.vue并设置空值data。这将允许模板在获取实际数据时进行初始化。

data(){ 
  return { 
    data:{ 
      items:[],
      shippingPrice:0 
    } 
  } 
}

最后,我们可以从products.json异步请求中获取数据,并data在其准备就绪时更新属性:

export default {
  ...
  created() {
    fetch('./src/assets/products.json')
      .then(response => response.json())
      .then(json => (this.data = json))
  }
}

现在让我们用这些数据填充我们的模板:

<ul class="items">
  <li :key="item.id" v-for="item in data.items" class="item">
    <div class="item-preview">
      <img :src="item.thumbnail" :alt="item.title" class="item-thumbnail">
      <div>
        <h2 class="item-title">{{ item.title }}</h2>
        <p class="item-description">{{ item.description }}</p>
      </div>
    </div>
    <div>
      <input type="text" class="item-quantity" v-model="item.quantity">
      <span class="item-price">{{ item.price }}</span>
    </div>
  </li>
</ul>
...
<h3 class="cart-line">
  Shipping
  <span class="cart-price">{{ data.shippingPrice }}</span>
</h3>
...

你应该看到你的购物车中的所有物品。现在让我们添加一些计算属性来计算小计和总计:

export default {
  ...
  computed: {
    getSubtotal() {
      return this.data.items.reduce(
        (a, b) => a + b.price * b.quantity,
        0
      )
    },
    getTotal() {
      return (
        this.getSubtotal + this.data.shippingPrice
      )
    }
  }
}

并将它们添加到我们的模板中:

<h3 class="cart-line">
  小计
  <span class="cart-price">{{ getSubtotal }}</span>
</h3>
...
<h3 class="cart-line">
  总计
  <span class="cart-price cart-total">{{ getTotal }}</span>
</h3>

尝试改变数量-你应该看到小计和总金额相应变化。

现在我们在这里有几个问题。首先,我们只显示金额,而不是货币。当然,我们可以在反应量旁边的模板中对它们进行硬编码。但是如果我们想制作一个多语言网站呢?并非所有的语言都以相同的方式进行金钱化

如果我们想要显示所有金额的小数点后两位,以便更好地对齐?您可以尝试使用该toFixed方法将所有初始金额保留为浮点数,但那时您将使用的String类型在进行数学运算时难度更大,性能更低。而且,这意味着为了纯粹的表达目的而改变数据,这从来不是一个好主意。如果您需要将相同的数据用于其他目的并且需要不同的格式?

最后,当前的解决方案依赖于浮点数学,这对于处理金钱来说是个坏主意。尝试并更改一些金额:

{
  "items": [
    {
      ...
      "price": 20.01
    },
    {
      ...
      "price": 15.03
    },
    ...
  ]
}

现在,看看您的购物车是如何损坏的?这不是一些错误的JavaScript行为,而是我们如何用二进制机器表示我们的小数编号系统的限制。如果你用花车做数学,你迟早会遇到那些不准确的。

好消息是,我们不必使用花车来存储钱。这正是Dinero.js发挥作用的地方。

Dinero.js,金钱包装

Dinero.js之于金钱,正如Moment.js之于日期。它是一个库,允许您创建货币价值对象、操作它们、向它们提问并格式化它们。它依赖于Martin Fowler的货币模式,并帮助您解决由浮点数引起的所有常见问题,主要方法是将金额以整数的形式存储在较小的货币单位中。

打开终端,安装Dinero.js:

npm install dinero.js --save

然后将其导入到App.vue:

import Dinero from 'dinero.js'
export default {
  ...
}

现在可以创建Dinero对象:

// 返回Dinero对象,金额为$ 50
Dinero({ amount: 500, currency: 'USD' })
// 返回$ 4,000.00
Dinero({ amount: 500 })
  .add(Dinero({ amount: 500 }))
  .multiply(4)
  .toFormat()

让我们创建一个工厂方法,将我们的price属性按需转换为Dinero对象。我们有最多两位小数的浮点数。这意味着如果我们想要以次要货币单位(在我们的情况下为美元)将它们转换为它们的等价物,我们需要将它们乘以10来乘以2的幂。

我们将该factor参数作为参数传递给默认值,以便我们可以使用具有不同指数的货币的方法。

export default {
  ...
  methods: {
    toPrice(amount, factor = Math.pow(10, 2)) {
      return Dinero({ amount: amount * factor })
    }
  }
}

美元是默认货币,所以我们不需要指定它。

因为我们在转换过程中正在进行浮点数学运算,所以有些计算结果可能会稍微不准确。通过将结果四舍五入到最接近的整数很容易解决。

toPrice(amount, factor = Math.pow(10, 2)) {
  return Dinero({ amount: Math.round(amount * factor) })
}

现在我们可以toPrice在我们的计算属性中使用:

export default {
  ...
  computed: {
    getShippingPrice() {
      return this.toPrice(this.data.shippingPrice)
    },
    getSubtotal() {
      return this.data.items.reduce(
        (a, b) =>
          a.add(
            this.toPrice(b.price).multiply(b.quantity)
          ),
        Dinero()
      )
    },
    getTotal() {
      return this.getSubtotal.add(this.getShippingPrice)
    }
  }
}

在我们的模板中:

<ul class="items">
  <li :key="item.id" v-for="item in data.items" class="item">
    <div class="item-preview">
      <img :src="item.thumbnail" :alt="item.title" class="item-thumbnail">
      <div>
        <h2 class="item-title">{{ item.title }}</h2>
        <p class="item-description">{{ item.description }}</p>
      </div>
    </div>
    <div>
      <input type="text" class="item-quantity" v-model="item.quantity">
      <span class="item-price">{{ toPrice(item.price) }}</span>
    </div>
  </li>
</ul>
<h3 class="cart-line">
  Subtotal
  <span class="cart-price">{{ getSubtotal }}</span>
</h3>
<h3 class="cart-line">
  Shipping
  <span class="cart-price">{{ getShippingPrice }}</span>
</h3>
<h3 class="cart-line">
  Total
  <span class="cart-price cart-total">{{ getTotal }}</span>
</h3>

如果你看看你的购物车,你会看到{}价格的地方。这是因为我们试图显示一个对象。相反,我们需要对它们进行格式化,以便它们可以使用正确的语法显示价格以及货币符号。

我们可以用Dinero的toFormat方法来实现这一点。

<ul class="items">
  <li :key="item.id" v-for="item in data.items" class="item">
    ...
    <div>
      ...
      <span class="item-price">
        {{ toPrice(item.price).toFormat() }}
      </span>
    </div>
  </li>
</ul>
<h3 class="cart-line">
  Subtotal
  <span class="cart-price">
    {{ getSubtotal.toFormat() }}
  </span>
</h3>
<h3 class="cart-line">
  Shipping
  <span class="cart-price">
    {{ getShippingPrice.toFormat() }}
  </span>
</h3>
<h3 class="cart-line">
  Total
  <span class="cart-price cart-total">
    {{ getTotal.toFormat() }}
  </span>
</h3>

现在你已经掌握了Dinero.js的基本知识,现在有时间来提升吧。

介绍

让我们shippingPrice转到0JSON文件。您的购物车现在应该显示“运费:$0.00”,这是准确的,但不便于用户使用。它会说“免费”更好吗?

幸运的是,Dinero.js有很多方便的方法来向您的实例提问。在我们的例子中,这个isZero方法正是我们需要的。

在模板中,只要表示零,就可以显示文本而不是格式化的Dinero对象:

<h3 class="cart-line">
  Shipping
  <span class="cart-price">
    {{
      getShippingPrice.isZero() ?
      'Free' :
      getShippingPrice.setLocale(getLocale).toFormat()
    }}
  </span>
</h3>

当然,你可以通过将它包装在一个方法中来概括这种行为。这将需要一个Dinero对象作为参数并返回一个String。这样,只要您尝试显示零金额,就可以显示“免费”。

区域设置切换

想象一下你正在制作一个电子商务网站。你想适应你的国际观众,所以你翻译内容并添加一个语言切换器。但是,有一个细节可能会引起您的注意:货币格式也会根据语言而变化。例如,在美国英语中10.00欧元翻译为法语中的10,00欧元。

Dinero.js通过I18n API支持国际格式。这可让您以本地化格式显示金额。

Dinero.js是不可变的,所以我们不能依靠改变Dinero.globalLocale来重新格式化所有现有的实例。相反,我们需要使用该setLocale方法。

首先,我们添加了一个新的属性language中data,并将其设置为默认值。对于语言环境,您需要使用BCP 47语言标签,如en-US。

data() {
  return {
    data: {
      ...
    },
    language: 'en-US'
  }
}

现在我们可以直接在Dinero对象上使用setLocale。当语言发生变化时,格式也会发生变化。

export default {
  ...
  methods: {
    toPrice(amount, factor = Math.pow(10, 2)) {
      return Dinero({ amount: Math.round(amount * factor) })
        .setLocale(this.language)
    }
  },
  computed: {
    ...
    getSubtotal() {
      return this.data.items.reduce(
        (a, b) =>
          a.add(
            this.toPrice(b.price).multiply(b.quantity)
          ),
        Dinero().setLocale(this.language)
      )
    },
    ...
  }
}

我们只需要在toPrice和getSubtotal中添加setLocale,这是我们创建Dinero对象的唯一位置。

现在我们可以添加语言切换器:

// HTML
<h1 class="title">
  Order
  <span>
    <span class="language" @click="language = 'en-US'">English</span>
    <span class="language" @click="language = 'fr-FR'">French</span>
  </span>
</h1>
// CSS
.language {
  margin: 0 2px;
  font-size: 60%;
  color: rgba(#333a45, 0.6);
  text-decoration: underline;
  cursor: pointer;
}

当您单击切换器时,它将重新分配语言,这将更改对象的格式化方式。因为库是不可变的,这将返回新的对象,而不是更改现有的对象。这意味着,如果您创建一个Dinero对象并决定在某个地方显示它,然后在其他地方引用它并在其上应用setLocale,那么您的初始实例将不会受到影响。没有讨厌的副作用!

所有含税

在购物车上看到一条税收线是很常见的。你可以用百分比法添加Dinero.js

首先,让我们在JSON文件中添加一个vatRate属性:

{
  ...
  "vatRate": 20
}

数据的初值:

data() {
  return {
    data: {
      ...
      vatRate: 0
    }
  }
}

现在我们可以用这个值来计算我们的带税购物车的总数。首先,我们需要创建一个getTaxAmount computed属性。然后我们也可以将它添加到getTotal中。

export default {
  ...
  computed: {
    getTaxAmount() {
      return this.getSubtotal.percentage(this.data.vatRate)
    },
    getTotal() {
      return this.getSubtotal
        .add(this.getTaxAmount)
        .add(this.getShippingPrice)
    }
  }
}

购物车现在显示了含税总额。我们也可以加一行来显示税额是多少:

<h3 class="cart-line">
  VAT ({{ data.vatRate }}%)
  <span class="cart-price">{{ getTaxAmount.toFormat() }}</span>
</h3>

结束语

到这儿文章就将如何用Vue和Dinero.js建立购物车就完成了!小编带大家已经探讨了Dinero的几个概念。但这只是它所能提供的服务的皮毛。如果你感兴趣的话可以阅读文档并在GitHub上查看项目。

未经允许不得转载:码云笔记 » 如何用Vue和Dinero.js建立购物车
喜欢(0) 打赏

评论抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏

在线客服

在线客服

  • 扫描二维码,微信联系 扫描二维码,微信联系