现代电子商务店面,带有XATA和Cloudinary Gallery小部件
#网络开发人员 #nextjs #database #jamstack
众所周知,

电子商务商店在当前需求很高。从我对自由职业平台的观察中,超过50%的开发人员至少有一次构建电子商务店面的演出。因此,开发人员需要一种快速,直接的方法来创建电子商务网站,而不会损害安全性和性能。

在本文中,我们将研究一种使用以下工具来构建表演者电子商务店面的快速方法:

  • Xata无服务器数据库,可以节省自己处理服务器的时间和精力。
  • Cloudinary Product gallery cloudinary窗口小部件处理具有已经风格的组件的产品图像。
  • FingerprintJS - 一个指纹图书馆,根据其浏览器属性为站点访问者分配唯一的ID。
  • Next.js - 一个提供服务渲染和快速建立时间的反应框架。

先决条件

本文需要了解Next.js及其API路由以进行平滑流程。另外,我们将需要创建一个Xata accountCloudinary accountFingerprintJS键。他们都是免费的帐户。
我们还假设有一个下一个。

什么是XATA

XATA是构建在Next.js上的无服务器数据库。它提供了各种数据类型,其中包括:

  • 字符串:简短的文字
  • 链接:数据表之间的连接
  • 电子邮件:电子邮件地址
  • Date
  • 整数:所有整数
  • 长文字:冗长的文字;如描述
  • 所示
  • 小数:数字的所有分数

它还支持各种语言,例如Typescript和Python。

什么是云

Cloudinary是用于管理Web和应用程序媒体资产的SaaS解决方案
。它可以帮助开发人员提供优化的Web资产,同时可以随时修改它们。
Cloudinary提供了来自媒体托管和管理的各种其他服务。其中一些包括:产品库小部件,将在本文中使用。

设置开发环境

在您的下一个。

yarn add fingerprintjs/fingerprint-pro-react axios @xata.io/client

应用演示

可以找到应用程序演示here和代码here

XATA入门

在这里,我们将讨论如何将XATA集成到我们的应用程序中。

设置数据库

要使用XATA,我们将在我们的免费层帐户中创建一个工作区。下图显示了在XATA中创建数据库的步骤;

create xata database
create xata database

created database

接下来,导航到 e-commercedB ,然后单击菜单栏上的 schema 导航。现在,下面的屏幕进入视图;

schema page

在这一点上,让我们为演示存储创建数据库架构。每个商店都有其出售的产品;因此,让我们为我们的商店创建一个产品表产品表将包含以下列:

  • 名称:一种字符串的类型。存储每个产品的名称
  • desc:一种 longtext。保存每种产品的描述
  • imageUrl:一种字符串的类型。存储每个产品中云的图像URL
  • 价格: a 十进制类型。存储每种产品的价格
  • 标签:一种 String 的类型。存储每种产品的标签
  • discount_id: a 链接是对 product_discount表的引用。 products table products table linked to product_discount table

product_discount表

组成
  • 名称: a 字符串键入存储折扣名称的类型,例如“ ecommerce20 ”。。
  • iSActive: a 布尔具有折扣有效性的类型
  • discount_percent: a 十进制类型存储产品价格的百分比。

discount_table

接下来,让我们创建一个用户表,该表将存储FingerPrintJS生成的 ID 。之后,让我们创建一个购物车表存储每个用户所选产品。该表将包含以下列:

  • product_id:参考所需产品的属性
  • user_id:参考选择产品的用户
  • 数量:用户选择的每产品总量
  • 金额:用户选择的每种产品总价

一旦我们成功创建了这些表,就该用一些虚拟数据填充它们了。
首先,让我们下载并在Cloudinary上托管我们的产品图像。为此,我们将将所需的产品图像上传到Cloudinary,同时向每个添加唯一的标签。然后,Cloudinary为每个图像生成一个URL,我们将在每个产品的 imageurl 列中使用。下图显示了创建的示例记录。

xata record sample

从XATA数据库获取产品

打开Next.js样板并导航到pages/index.js目录。在文件中添加以下片段

// pages/index.js
import { BaseClient } from '@xata.io/client';
    import Products from '../components/Products';

    export default function Home({ products }) {
      return (
        <div className="">
          <main>
            <div id='products' className="">
              <h1 className="text-3xl font-semibold text-center py-10">Products</h1>
              <Products products={products} />
            </div>
          </main>
        </div>
      )
    }
    export async function getServerSideProps() {
      const xata = new BaseClient({
        branch: 'main',
        apiKey: process.env.API_KEY,
        databaseUrl: process.env.XATA_DATABASE_URL
        //fetch: fetchImplementation // Required if your runtime doesn't provide a global `fetch` function.
      });
      const page = await xata.db.products
        .select(["*", "discount_id.*"])
        .getAll()
      return {
        props: {
          products: page,
        }
      }
    }

上面的摘要负责从数据库获取我们的产品。为此,

  • 首先,从@xata.io/client导入BaseClient,而axios from axios
  • 接下来,用products导出Home页面函数7
  • 然后,创建一个异步的getServerSideProps()函数,该函数初始化XATA客户端。要初始化我们的XATA客户端,我们需要从XATA中获得API键,并将它们添加到.env文件

我们的API键和数据库URL可以找到如下图所示
Xata Settings

  • 接下来,我们的产品将使用XATA方法获取; xata.db.products并使用"*"选择所有产品。将每个链接(引用)表附加到结果时。如下所示,这将返回响应; log data

由于现在可以在我们的Homepage上提供产品,因此请显示它们。为了实现这一目标,让S创建一个components目录和一个Products.jsx文件。在此文件中添加以下片段:

    //components/Products.jsx
    import Link from "next/link"
    const Products = ({ products }) => {
        return (
            <div className="grid grid-cols-2 md:grid-cols-2 lg:grid-cols-3 justify-items-center">
                {
                    products && products.map((prod, index) => (
                        <div className="relative w-36 md:w-52 bg-white rounded-md border border-gray-200 shadow-md m-3" key={index}>
                            <small>
                                <b className={`${!prod.discount_id ? 'none' : 'absolute top-2 right-2 text-xs text-red-300 bg-red-100 p-1'}`}>{prod.discount_id && prod.discount_id.isActive ? `${prod.discount_id.discount_percent * 100}%` : ''}</b>
                            </small>
                            <div className="">
                                <Link href={`/product/${prod.id}`} className="">
                                    <img className='object-cover w-36 md:w-52 h-48' src={prod.imageUrl} alt="" />
                                </Link>
                            </div>
                            <div className="card-info py-2 px-2">
                                <Link href={`/product/${prod.id}`}>
                                    <h3>{prod.name}</h3>
                                    <p>
                                        <b>{prod.discount_id && prod.discount_id.isActive ? (prod.price - (prod.price * prod.discount_id.discount_percent)) : prod.price}</b> <br />
                                        <small><s className={`${!prod.discount_id ? 'hidden' : 'text-gray-500 decoration-gray-500'}`}>{prod.discount_id && prod.discount_id.isActive ? prod.price : ''}</s></small>
                                    </p>
                                </Link>
                            </div>
                        </div>
                    ))
                }
            </div>
        )
    }
    export default Products

然后将上述组件导入到我们的pages/index.js文件中。以props的方式传递了所取的products。然后,它通过这些产品映射并显示它们如下所示:

displalyed products

使用Fingerprintjs创建用户识别

一家电子商务商店必须意识到其访客以跟踪其活动。遵循的重要活动之一是用户正在添加到购物车中的“什么”产品。
但是,我们不想用登录表单不断地打扰您的用户。为了消除这一点,该应用将使用指纹库默默创建每个用户唯一的ID。现在,让我们浏览代码。导航到/pages/_app.jsx并添加以下片段。

// pages/_app.jsx
import { FpjsProvider } from '@fingerprintjs/fingerprintjs-pro-react'
    import NavBar from '../components/NavBar'
    import '../styles/globals.css'
    function MyApp({ Component, pageProps }) {
      return (
        <FpjsProvider
          loadOptions={{
            apiKey: process.env.FPJS_KEY
          }}
        >
          <NavBar />
          <Component {...pageProps} />
        </FpjsProvider>
      )
    }
    export default MyApp

在上面的摘要中,Fingerprintjs使用@fingerprinjs/fingerprintjs-pro-react的提供商绑定到我们的应用程序,并使用API​​键进行初始化。

接下来,让我们在API路由中创建服务器逻辑。此逻辑将接收我们的用户 id 并在数据库中搜索实例。如果没有用户实例,请创建新的用户记录。为了实现这一目标,我们将为/pages/api添加一个名为assignuser.js的文件。然后我们添加以下片段:

// pages/api/assignuser.js
import { BaseClient } from '@xata.io/client';
    export default async function handler(req, res) {
      const { visitorID } = req.body
      const xata = new BaseClient({
        branch: 'main',
        apiKey: process.env.API_KEY,
        databaseURL: process.env.XATA_DATABASE_URL
        //fetch: fetchImplementation // Required if your runtime doesn't provide a global `fetch` function.
      });
      try {
        //Fetch all users and find a match for the visitorID
        const records = await xata.db.anon_user.select(["*"]).filter("id", visitorID).getAll()
        //if a match is not found, create a user with the visitorID
        if (records.length === 0) {
          await xata.db.user.create(visitorID, {
            _id: visitorID
          })
        }
        res.status(200)
      } catch (err) {
        console.error(err)
      }
    }

在上述片段中,我们将:

  • 从我们的@xata.io/client导入BaseClient,并用我们有效的凭据初始化客户
  • 创建一个处理程序功能,我们从XATA获取用户,并通过从我们的客户端收到的visitorId过滤
  • 检查是否存在具有visitorID的用户。如果它不存在,我们会创建一个新记录,但是如果这样做,我们什么也不会做

接下来,让我们创建一个客户端逻辑,以将用户的S id 发布到我们的API路由/api/assignuser。导航到/pages/index.jsx并添加以下片段:

    // pages/index.jsx
    import { useVisitorData } from '@fingerprintjs/fingerprintjs-pro-react'
    import { useEffect } from 'react'
    import axios from 'axios'
    export default function Home({ products }) {
      const { isLoading, data } = useVisitorData({ immediate: true })
      useEffect(() => {
        const registerAnon = async () => {
          if (!isLoading) {
            const visitorID = data?.visitorId
            await axios.post('/api/assignuser', { visitorID })
          }
        }
        registerAnon()
      }, [isLoading, data])
      return (
        <div className="">
          ...
        </div>
      )
    }
    export async function getServerSideProps() {
      ...
    }

在上述片段中,我们将:

  • @fingerprintjs/fingerprintjs-pro-react导入useVisitorData
  • 破坏isLoadingisLoadingdata的值
  • 启动useEffect钩并创建一个registerAnon函数。在这里,我们将从data中获取visitorId的价值。下一

这样,我们已成功地将ID分配给访问者并将其存储在我们的数据库中。

创建一个产品页面

在这里,我们将利用Next.js动态路由功能为单个产品库显示。

创建动态路由
首先,在pages文件夹中创建一个product目录。然后,我们将创建一个名为[id].jsx的动态文件。在此文件中添加以下片段:

   // pages/product/[id].jsx
    import { BaseClient } from '@xata.io/client';

    const Product = ({ product }) => {
        return (
        ...
        )
    export default Product;
    export async function getStaticPaths() {
        const xata = new BaseClient({
            branch: 'main',
            apiKey: process.env.API_KEY,
            databaseURL: process.env.XATA_DATABASE_URL
            //fetch: fetchImplementation // Required if your runtime doesn't provide a global `fetch` function.
        });

        const page = await xata.db.products
            .select(["*", "category_id.*", "inventory_id.*", "discount_id.*", "tag_id.*"])
            .getAll()
        const products = page
        const paths =
            products &&
            products.map((prod) => ({
                params: { id: prod.id }
            }));
        return {
            paths: paths,
            fallback: false
        };
    }

在上述片段中,我们将:

  • 创建一个Product页面组件,然后从@xata.io/client导入BaseClient
  • 创建一个getStaticPaths异步函数。在这里,我们将从XATA获取所有产品id,并将其分配为paths

接下来,我们将过滤这些paths并仅返回用户要求的产品。为此,添加以下片段:

    // pages/product/[id].jsx

    // this line is below getStaticPaths()
    export async function getStaticProps({ params }) {
        const xata = new BaseClient({
            branch: 'main',
            apiKey: process.env.API_KEY,
            databaseURL: process.env.XATA_DATABASE_URL
            //fetch: fetchImplementation // Required if your runtime doesn't provide a global `fetch` function.
        });

        const page = await xata.db.products
            .select(["*", "category_id.*", "inventory_id.*", "discount_id.*", "tag_id.*"])
            .getAll()
        const products = page
        const product =
            products && products.filter((prod) => params.id === prod.id);
        return {
            props: {
                product
            }
        };
    }

在上面的片段中,我们还将过滤的产品返回为页面道具。

现在,我们需要通过将以下片段添加到pages/product/[id].jsx
来向用户显示此请求的产品

    // pages/product/[id].jsx

    import { useState } from "react";
    const Product = ({ product }) => {
          return (
            <div className='relative mx-auto'>
                <div className="flex flex-col md:w-6/6 p-8 md:flex-row">
                    <div className="price md:w-2/6">
                        <h3 className='text-3xl font-semibold'>{product[0].name}</h3>
                        <p className='text-lg'>
                            {product[0].desc} <br />
                            Lorem ipsum dolor sit amet consectetur adipisicing elit. Laudantium eaque totam aperiam temporibus asperiores, soluta eligendi architecto magni tempora. Perspiciatis a sequi id ut qui eligendi illo non unde vero!
                            Numquam quia maxime laudantium, provident libero fuga, voluptatem animi eveniet aliquid beatae mollitia odit laborum amet dolores. Id adipisci quidem nostrum alias dolorem ea, commodi rem repellendus repellat nihil molestias.
                        </p>
                        <p>
                            <b>{product[0].discount_id && product[0].discount_id.isActive ? (product[0].price - (product[0].price * product[0].discount_id.discount_percent)) : product[0].price}</b> <br />
                            <small><s className={`${!product[0].discount_id ? 'hidden' : 'text-gray-500 decoration-gray-500'}`}>{product[0].discount_id && product[0].discount_id.isActive ? product[0].price : ''}</s></small>
                        </p>
                    </div>
                </div>
            </div>
        );
    }

上面的摘要使我们的产品titlepricedescription
接下来,我们将显示每种产品的图像。为此,我们将使用Cloudinary的产品库小部件功能。

s cluming产品库小部件
云画廊小部件根据Client-side asset lists功能检索带有指定标签的图像(或视频)。要了解有关云产品库的更多信息,请参见here

确保在云帐户上启用资源列表选项以使用此功能。默认情况下,列表交付类型受到限制。在您的控制台中,导航到安全设置并清除资源列表下的受限制媒体类型项目。
Cloudinary widget settings
Cloudinary widget settings

接下来,将以下片段添加到/pages/_document.js

    // pages/_document.js
    import Document, { Html, Head, Main, NextScript } from "next/document";
    class MyDocument extends Document {
        render() {
            return (
                <Html>
                    <Head />
                    <body>
                        <Main />
                        <NextScript />
    //script for cloudinary product gallery
                        <script
                            src="https://product-gallery.cloudinary.com/all.js"
                            type="text/javascript"
                            strategy="beforeInteractive"
                        ></script>
                    </body>
                </Html>
            );
        }
    }
    export default MyDocument;

上面标记的script在页面变得互动之前获取产品小部件。

接下来,要在我们的产品页面中渲染此内容,我们将添加以下片段:

    // pages/product/[id].jsx'

    import { useState, useEffect } from "react";
    import axios from 'axios';
    const Product = ({ product }) => {
        useEffect(() => {
            const productGallery = cloudinary.galleryWidget(
                {
                    container: "#gallery",
                    cloudName: "jilis",
                    mediaAssets: [{ tag: `${product[0].tag_id.tagname}`, mediaType: "image" }]
                },
                []
            );
            productGallery.render();
        });
          return (
            <div className='relative mx-auto'>
                <div className="flex flex-col md:w-6/6 p-8 md:flex-row">
                    //the line below renders the product gallery
                    <div className="md:w-4/6 md:h-96 px-5">
                        <div className='' id="gallery"></div>
                    </div>
                  ...
                </div>
            </div>
        );
    }

在上面的片段中,我们使用useEffect钩将画廊的小部件渲染为带有galleryid的元素。要渲染的图像托管在Cloudinary上,并具有与用户要求的产品相同的tagname。以下是UI出现的方式:

product page

结论

使用XATA数据库构建是一种快速的体验。进入编辑之前,我喜欢在操场上的界面上锻炼我的代码。另外,集成云画廊小部件是无缝的,更不用说我的图像在云托管上托管以来的传递速度。这些工具很棒,我相信每个开发人员也会很高兴能跳上它们以创造出令人敬畏的。

资源

Cloudinary Gallery Widget
Xata Documentation
FingerprintJs Docs