nuxt3+pinia

3,246 阅读3分钟

nuxt3:基于vue3的服务端渲染框架。

服务端渲染解决的问题:1、搜索引擎优化(seo)2、首屏优化。

1、创建Nuxt项目: 终端中输入npx nuxi init <project-name> 安装依赖:npm i 运行项目:npm run dev

注*:node 版本要 > 16.11*。

目录结构:

image.png

启动成功页面:

image.png

2、写一个商品加入购物车的demo。

编辑器打开项目:app.vue里面的 <NuxtWelcome />就是启动页,这个组件是内置的(目录中找不到),删掉即可。app.vue是Nuxt应用程序的主要组件,添加到里面的东西都是全局的,包含在每个页面中。这里我们不需要这个文件,删掉即可。

根目录下新建pages文件夹。新建两个文件。 根目录下新建layouts文件夹,新建default.vue文件。

layouts:是一个自定义的布局框架,可在整个应用使用。也可以指定页面使用。

注:nuxt是约定式路由,按照官方规定的目录结构去创建文件,会自动生成路由,不需要我们去写路由文件。

image.png

layouts/default.vue:

<template>
  <div>
    <header>
      <nuxt-link to="/">
        <h1>商品列表</h1>
      </nuxt-link>
      <nuxt-link to="cart">
        <h1>购物车</h1>
      </nuxt-link>
    </header>
    <div>
      <slot />
    </div>
  </div>
</template>

看下效果:

image.png image.png

改下样式,用下taiwindcss:

1、下载npm install --save-dev @nuxtjs/tailwindcss。 2、nuxt.config.ts配置

  modules: [
    '@nuxtjs/tailwindcss'
  ]
})

3、npx tailwindcss init 创建一个tailwind.config.js文件,可以配置全局的一些样式,具体去看官网。(修改配置文件要重启哦~) tailwind.confit.js:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [],
  theme: {
    extend: {
      colors: {
        primary: '#270A4B',
        secondary:'#f08200',
        dark: '#333843',
      },
      fontFamily: {
        sans: [ '"Noto Sans"'],
      }
    },
  },
  plugins: [],
}

4、根目录新建assets/css/tailwind.css assets/css/tailwind.css:

@tailwind base;
@tailwind components;
@tailwind utilities;

body {
    @apply bg-dark;
}
.wrapper {
    @apply my-8 max-w-5xl mx-auto;
}

5、使用:layouts/default.vue:

<template>
  <div>
    <header class="wrapper flex justify-between">
      <nuxt-link class="text-secondary text-5xl" to="/">
        <h1>商品列表</h1>
      </nuxt-link>
      <nuxt-link to="cart">
        <span class=" text-secondary text-5xl">购物车</span>
      </nuxt-link>
    </header>
    <div>
      <slot />
    </div>
  </div>
</template>

写页面: 1、根目录新建一个data/db.json随便写几条数据,模拟数据,实际开发中是接口请求来的数据,新建public/img 存放造的数据要用到的图片。 注:可以使用json-server来模拟数据库接口,本文直接用的假数据,json-server使用可参考:juejin.cn/post/717949…

注*:public/目录直接在服务器根部提供服务,包含必须保持其名称(如 robots.txt)或可能不会改变的公共文件(如 favicon.ico)。* image.png 2、pages/index.vue

<template>
  <div>
    <h2 class="text-white text-2xl text-center my-8">Products</h2>
    <div class="grid grid-cols-2 gap-7">
      <div v-for="product in products" :key="product.id">
        <ProductCard :product="product" />
      </div>
    </div>
  </div>
</template>
<script setup>
import { products } from '../data/db.json';
</script>
<style scoped></style>

注: <ProductCard />是个组件,渲染每个商品。在 components/ProductCard.vue文件中。通过defineProps(['product']);接收父组件传过来的值。 3、pages/cart.vue:

<template>
  <div>
    <h2 class="text-white text-2xl text-center my-8">购物车</h2>
    <div v-for="p in cart" :key="p.id" class="flex items-center gap-8">
        <img :src="p.img" :alt="p.title">
        <p class="text-white">{{ p.title }}</p>
        <p class="text-white">{{ p.price * p.quantity}} coins</p>
      </div>
  </div>
</template>

<script setup>
import { cart } from '../data/db.json';
</script>

<style scoped>

</style>

以上是布局样式.

下一步是利用pinia管理购物车数据,实现购物车数据的增删改查。

1、安装: npm i @pinia/nuxt --legacy-peer-deps。npm版本较高时要加 --legacy-peer-deps。 2、配置:nuxt.config.ts:

modules: [
    '@nuxtjs/tailwindcss',
    '@pinia/nuxt',
],

3、新建:store/cartStore.vue

import { defineStore } from "pinia";
import { cart } from "@/data/db.json";
export const useCartStore = defineStore("cart", {
  state: () => ({
    cart: [],
  }),
  actions: {
    async getCart() {
      const data = cart;
      this.cart = data;
    },
    // 从购物车删除
    async deleteFromCart(product) {
      this.cart = this.cart.filter((p) => {
        return p.id !== product.id;
      });
      // 请求删除接口更新数据库
      // await $fetch('http://xxxxx' + product.id, {
      //     method: 'delete'
      // })
    },
    // 增加商品数量
    async incQuantity(product) {
      let updataedProduct;
      this.cart = this.cart.map((p) => {
        if (p.id === product.id) {
          p.quantity++;
          updataedProduct = p;
        }
        return p;
      });
      // 请求接口增加商品数量
      // await $fetch('http://xxxxx' + product.id, {
      //     method: 'put',
      //     body: JSON.stringify(updatedProduct)
      // })
    },
    // 减少商品数量
    async decQuantity(product) {
      let updatedProduct;
      this.cart = this.cart.map((p) => {
        if (p.id === product.id && p.quantity > 1) {
          p.quantity--;
          updatedProduct = p;
        }
        return p;
      });
      // 减少商品数量接口
      // if (updatedProduct) {
      //     await $fetch('http://xxxx' + product.id, {
      //         method: 'put',
      //         body: JSON.stringify(updatedProduct)
      //     })
      // }
    },
    // 加入购物车
    async addToCart(product) {
      // 购物车中是否存在此商品
      const exists = this.cart.find((p) => p.id === product.id);
      // 存在则数量 +1
      if (exists) {
        this.incQuantity(product);
      }
      // 不存在增加
      if (!exists) {
        this.cart.push({ ...product, quantity: 1 });
        // 加入购物车接口请求更新数据库
        // await $fetch('http://xxxxx', {
        //     method: 'post',
        //     body: JSON.stringify({...product, quantity: 1})
        // })
      }
    },
  },
});

4、页面中使用pinia: (1)引入:import { useCartStore } from '@/stores/cartStore'; (2)使用:useCartStore().cart