在 Nuxt 应用中创建动态路由

Avatar of Sarah Drasner
Sarah Drasner

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费积分!

在这篇文章中,我们将使用一个我创建并部署到 Netlify 的电子商务商店演示,来展示如何为传入数据创建动态路由。这是一个相当常见的用例:您从 API 获取数据,而且您要么不确定这些数据可能是什么,要么数据很多,要么数据可能发生变化。幸运的是,Nuxt 使创建动态路由的过程非常无缝。

让我们开始吧!

创建页面

在这种情况下,我们有一些我在 mockaroo 中创建并存储在静态文件夹中的商店虚拟数据。通常,您将使用 fetch 或 axios 以及 Vuex 存储中的一个操作来收集这些数据。无论哪种方式,我们都将数据与 Vuex 一起存储在 store/index.js 中,以及 UI 状态和一个空数组作为购物车。

import data from '~/static/storedata.json'

export const state = () => ({
 cartUIStatus: 'idle',
 storedata: data,
 cart: []
})

需要说明的是,在 Nuxt 中,我们只需在 pages 目录中创建一个 .vue 文件即可在应用程序中设置路由。因此,我们有一个用于主页的 index.vue 页面,一个用于购物车的 cart.vue 页面,等等。Nuxt 会自动为我们生成所有这些页面的路由。

为了创建动态路由,我们将创建一个目录来容纳这些页面。在这种情况下,我创建了一个名为 /products 的目录,因为这些将是路由,每个产品的详细信息视图。

在该目录中,我将创建一个带有下划线的页面,以及我想要在每个页面上使用的唯一标识符来创建路由。如果我们查看我在购物车中拥有的数据,它看起来像这样

[
 {
   "id": "9d436e98-1dc9-4f21-9587-76d4c0255e33",
   "color": "Goldenrod",
   "description": "Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.",
   "gender": "Male",
   "name": "Desi Ada",
   "review": "productize virtual markets",
   "starrating": 3,
   "price": 50.40,
   "img": "1.jpg"
 },
  …
]

您可以看到每个条目的 ID 都是唯一的,所以这是一个很好的使用候选,我们将页面命名为

_id.vue

现在,我们可以使用路由参数将特定页面的 ID 存储在数据中

data() {
 return {
   id: this.$route.params.id,
  }
},

对于上面的条目,如果我们在 devtools 中查看数据,它将是

id: "9d436e98-1dc9-4f21-9587-76d4c0255e33"

现在,我们可以使用它从商店中检索此条目的所有其他信息。我将使用 mapState

import { mapState } from "vuex";

computed: {
 ...mapState(["storedata"]),
 product() {
   return this.storedata.find(el => el.id === this.id);
 }
},

我们正在过滤 storedata 以找到具有我们唯一 ID 的条目!

让 Nuxt 配置知道

如果我们使用 yarn build 构建应用程序,我们就完成了,但我们使用 Nuxt 创建一个静态站点来部署,在本例中是 Netlify。当我们使用 Nuxt 创建静态站点时,我们将使用 yarn generate 命令。我们必须使用 nuxt.config.js 中的 generate 命令让 Nuxt 知道动态文件。

此命令将期望一个函数,该函数将返回一个解析为如下所示数组的 promise

export default {
  generate: {
    routes: [
      '/product/1',
      '/product/2',
      '/product/3'
    ]
  }
}

为了创建它,我们将在文件顶部引入静态目录中的数据,并创建该函数

import data from './static/storedata.json'
let dynamicRoutes = () => {
 return new Promise(resolve => {
   resolve(data.map(el => `product/${el.id}`))
 })
}

然后,我们将在配置中调用该函数

generate: {
  routes: dynamicRoutes
},

如果你是使用 axios 从 API 收集你的数据(这更常见),它看起来更像这样

import axios from 'axios'
let dynamicRoutes = () => {
 return axios.get('https://your-api-here/products').then(res => {
   return res.data.map(product => `/product/${product.id}`)
 })
}

就这样,我们完成了动态路由!如果您关闭并重新启动服务器,您将看到每个产品的动态路由!

对于这篇文章的最后部分,我们将继续,展示页面的其余部分是如何制作的以及我们如何将商品添加到购物车,因为这可能是您也想要学习的东西。

填充页面

现在,我们可以使用我们想要的任何格式,用我们想要显示的任何信息来填充页面,因为我们通过产品计算属性可以访问所有这些信息

<main>
 <section class="img">
   <img :src="`/products/${product.img}`" />
 </section>
 <section class="product-info">
   <h1>{{ product.name }}</h1>
   <h4 class="price">{{ product.price | dollar }}</h4>
   <p>{{ product.description }}</p>
 </section>
 ...
</main>

在我们的案例中,我们还需要将商品添加到商店中的购物车。我们将添加添加和删除商品的能力(同时不使数量减少到零以下)

<p class="quantity">
 <button class="update-num" @click="quantity > 0 ? quantity-- : quantity = 0">-</button>
 <input type="number" v-model="quantity" />
 <button class="update-num" @click="quantity++">+</button>
</p>
...
<button class="button purchase" @click="cartAdd">Add to Cart</button>

在该组件的 methods 中,我们将添加商品加上一个新字段(数量)到一个数组中,我们将这个数组作为有效负载传递给存储中的 mutation

methods: {
 cartAdd() {
   let item = this.product;
   item.quantity = this.quantity;
   this.tempcart.push(item);
   this.$store.commit("addToCart", item);
 }
}

在 Vuex 存储中,我们将检查商品是否已存在。如果存在,我们只需增加数量。如果不存在,我们将使用数量将整个商品添加到购物车数组中。

addToCart: (state, payload) => {
 let itemfound = false
 state.cart.forEach(el => {
   if (el.id === payload.id) {
     el.quantity += payload.quantity
     itemfound = true
   }
 })
 if (!itemfound) state.cart.push(payload)
}

现在,我们可以使用存储中的 getter 来计算总数,这将是我们最终传递给 Stripe 无服务器函数的内容(关于此的另一篇文章即将发布!)。我们将为此使用 reduce,因为 reduce 在从多个值中检索一个值方面非常出色。(我在这里写了一些关于 reduce 工作原理的更多详细信息)。

cartTotal: state => {
 if (!state.cart.length) return 0
 return state.cart.reduce((ac, next) => ac + next.quantity * next.price, 0)
}

就这样!我们已经设置了单独的产品页面,Nuxt 在构建时会为我们生成所有单独的路由。你一定会忍不住自己试试的。😬