Thủ thuật Website

Cách xây dựng một trang web WordPress tĩnh với Frontity

Trong những năm gần đây, chúng tôi đã khám phá rất nhiều khuôn khổ và ngăn xếp hợp thời trang. Tất cả đều nâng cấp hiệu suất và dễ sử dụng, nhưng chúng ta đã không nói về OG của web hiện đại trong một thời gian dài. Tất nhiên, tôi đang nói về WordPress.

Mặc dù WordPress cung cấp 42% tất cả các trang web trên internet, nhưng đôi khi có cảm giác như WordPress không phát triển đủ để cạnh tranh với các khung công tác mới như Next.js, Vue.js, Gatsby và những người khác.

Điều gì sẽ xảy ra nếu tôi nói với bạn rằng bạn có thể tiếp tục sử dụng WordPress trong khi khai thác sức mạnh và chức năng của React để xây dựng giao diện người dùng của bạn? Vâng, nhờ Frontity, bạn có thể nhanh chóng xây dựng một trang web WordPress hiện đại nhất.

Hãy cùng khám phá khuôn khổ mới này và xem chúng ta có thể tận dụng nó như thế nào để xây dựng một trang web thương mại điện tử.

Frontity là gì?

Frontity là một khung công tác mã nguồn mở dựa trên React. Nó sử dụng trang web WordPress của bạn như một CMS không đầu và hiển thị nó trong một khuôn khổ React. Nó cho phép bạn xây dựng một trang web tĩnh WordPress không đầu nhanh chóng một cách nhanh chóng.

Nó hoạt động giống như một trình tạo trang web tĩnh bằng cách biên dịch và xây dựng các trang HTML và phục vụ chúng khi có yêu cầu của khách hàng. Kết xuất trước không máy chủ của React ‘xử lý nội dung động của trang như bất kỳ trang tĩnh nào khác.

Frontity quản lý việc đóng gói, chuyển đổi, định tuyến, kết xuất máy chủ, quản lý trạng thái, quản lý CSS, truy xuất dữ liệu từ WordPress, v.v. Đó là một khuôn khổ không thiết lập hỗ trợ TypeScript cũng như Emotion cho kiểu CSS trong JavaScript. Nó cũng hỗ trợ Google AMP với cùng một cơ sở mã.

Khung này cũng cung cấp quản lý trạng thái ứng dụng thông qua trình quản lý trạng thái của nó được gọi là Frontity Connect. Đối tượng JavaScript chứa tất cả các trạng thái được gói của bạn hiển thị được hợp nhất với cài đặt. Bằng cách này, các gói có thể truy cập trạng thái được các gói khác hiển thị.

Việc phát triển một trang web WordPress hiện đại giờ đây thật dễ dàng. Và đối với một người như tôi, người không phải là nhà phát triển, nó giúp cuộc sống của tôi dễ dàng hơn.

Các tính năng mà Frontity mang lại tương tự như những tính năng bạn sẽ nhận được từ một trình tạo trang web tĩnh khác.

Nhưng điều khiến nó trở thành một lựa chọn ghép nối không có trí tuệ với WordPress là tốc độ của nó. Nghiêm túc. Vì nó được hiển thị phía máy chủ, mọi thứ đều nhanh và tải gần như ngay lập tức.

Làm thế nào nó hoạt động?

Frontity sử dụng WordPress REST API để tìm nạp dữ liệu từ trang web WordPress của bạn (máy chủ PHP) và hiển thị nó trong giao diện người dùng React của bạn (máy chủ Node.js). Sau đó, Frontity hiển thị các trang bằng HTML hoặc AMP . Frontity có thể được lưu trữ trong một máy chủ Node.js thông thường hoặc một dịch vụ không máy chủ như AWS, Netlify hoặc Vercel.

Điều đó thật tuyệt, nhưng những gì về tín dụng đường phố của họ? Vâng, Frontity được TikTok tin tưởng để xây dựng cổng thông tin dành cho người sáng tạo của họ , CNBC Châu Phi và Forbes Châu Phi , để nêu tên một số người.

Nó so với Gatsby như thế nào?

Gatsby cũng cung cấp một giao diện người dùng để xây dựng một trang web tĩnh bằng cách sử dụng phiên bản WordPress làm CMS không đầu. Vì vậy, hãy khám phá xem Frontity so sánh với nó như thế nào.

Để bắt đầu, hai framework đều dựa trên React.

Cả Gatsby và Frontity đều mang lại thời gian tải trang ban đầu nhanh hơn nhờ các trang tĩnh. Các khung công tác này cũng xử lý định tuyến ứng dụng ngay lập tức, vì vậy chúng tôi không phải thiết lập điều đó.

Cả hai khung công tác cũng được hưởng lợi từ việc tách mã , điều này sẽ tối ưu hóa chúng để có điểm hiệu suất tốt hơn trong Lighthouse.

Tuy nhiên, như chúng ta đã thấy trước đó, Frontity là một khuôn khổ sẵn sàng để chơi, nơi mọi thứ được thiết lập cho chúng ta. Nó thân thiện với nhà phát triển (hoặc trong trường hợp của tôi là thân thiện với người mới bắt đầu), không cần cấu hình phức tạp, các công cụ như truy vấn API được định cấu hình trước.

Frontity cũng loại bỏ nhu cầu xử lý GraphQL bằng cách cung cấp dữ liệu thông qua trình quản lý nhà nước của nó.

Bây giờ nói đủ; đã đến lúc đi sâu vào nó. Hãy tự mình xem framework dựa trên React này có thể làm được những gì!

Hướng dẫn: Xây dựng một trang WordPress thương mại điện tử không đầu với Frontity

Đối với hướng dẫn này, chúng ta hãy xây dựng một trang thương mại điện tử đơn giản bằng cách sử dụng Snipcart để bán nước sốt nóng.

Điều kiện tiên quyết

  • Một phiên bản WordPress được lưu trữ
  • Kiến thức JavaScript
  • Tài khoản Snipcart (miễn phí thử nghiệm vĩnh viễn)

Bước 1: Tạo các trang của chúng tôi trong WordPress

Bước đầu tiên chúng tôi cần làm là tạo các trang của chúng tôi trong WordPress. Chúng tôi sẽ tạo một trang web thương mại điện tử đơn giản với hai trang (không bao gồm các trang sản phẩm của chúng tôi). Trang đầu tiên sẽ là trang chủ của chúng tôi và trang còn lại là trang về chúng tôi.

Để tạo chúng, chỉ cần vào Pagesmenu quản trị của WordPress và nhấp vào ‘Thêm mới’ để tạo các trang của chúng tôi. Trang chủ của chúng tôi sẽ được đặt tên là ‘Nước sốt nóng nhất’ và trang giới thiệu về chúng tôi có tên là ‘Giới thiệu về chúng tôi’. Sau khi hoàn tất, hãy truy cập Settings/Readingvà chọn ‘trang tĩnh’ và chọn trang chủ từ menu thả xuống.

Đọc bảng điều khiển bảng điều khiển WordPress cài đặt trang chủ tĩnh

Trong khi cài đặt, hãy đảm bảo kích hoạt liên kết cố định Tên bài đăng trong Settings/Permalinksđể đảm bảo Frontity sẽ hoạt động bình thường.

Bước 2: Tạo Sản phẩm trong WordPress

Bây giờ chúng ta có một phiên bản WordPress được thiết lập và chạy với các trang của chúng tôi đã tạo, hãy tạo các sản phẩm của chúng tôi.

Để tạo sản phẩm của chúng tôi, trước tiên chúng tôi cần cài đặt plugin Trường tùy chỉnh nâng cao . Sau khi cài đặt và kích hoạt. Hãy tạo một nhóm trường mới có chứa những thứ sau.

Tạo nhóm đệ trình tùy chỉnh nâng cao

Sau đó, chọn ‘Bài đăng’ từ menu bảng điều khiển WordPress và nhấp vào ‘Thêm mới’.

Đặt tên cho nó, sau đó chọn nhập dữ liệu vào các trường tùy chỉnh mà chúng tôi vừa tạo.

Tạo sản phẩm trong WordPress với các trường tùy chỉnh

Chúng tôi cũng sẽ cần thêm ACF vào một plugin REST API để có thể tìm nạp các trường tùy chỉnh nâng cao của chúng tôi sau này trong Frontity.

Bước 3: Tạo dự án Frontity

Bây giờ chúng tôi đã thiết lập mọi thứ chúng tôi cần trong WordPress, đã đến lúc đi sâu vào Frontity.

Chúng tôi sẽ cần tạo dự án của mình. Để làm như vậy, hãy chạy lệnh sau:

npx frontity create my-first-frontity-project

Frontity CLI sẽ nhắc bạn chọn một chủ đề. Đối với bản demo này, chúng ta hãy chọn @frontity/twentytwenty-theme.

Sau khi hoàn tất, bạn sẽ có mọi thứ cần thiết để bắt đầu phát triển và chúng tôi sẽ sẵn sàng cho bước tiếp theo.

Bước 4: Kết nối Frontity với WordPress REST API

Để có dữ liệu của chúng tôi trong Frontity, chúng tôi cần kết nối với WordPress REST API. Đối với điều này, hãy mở frontity.settings.jsvà thay thế YOUR-WORDPRESS_SITE.com(trong const settings) bằng URL của trang web WordPress của chúng tôi. Điều này sẽ cho Frontity biết nơi tìm nội dung trang web của chúng tôi.

Trong cùng một hằng số, chúng tôi sẽ thay đổi giá trị của "title"và "description". Chúng sẽ được hiển thị trong tiêu đề của trang web của chúng tôi.

Chúng tôi cũng sẽ cần kết nối API REST. đối với điều này, chỉ cần thay thế YOUR-WORDPRESS_SITE.com/wp-jsonbằng URL WordPress của bạn theo sau /wp-json.

Lưu ý rằng lộ trình này có thể thay đổi tùy thuộc vào cách bạn thiết lập trang chủ của mình và việc bạn đang sử dụng WordPress.org hay WordPress.com .

Chúng tôi cũng sẽ định cấu hình tên menu và các tuyến của chúng, tiêu đề trang web và mô tả. "title"và "description"chúng tôi sẽ được sử dụng cho meta trang web của chúng tôi.

const settings = {
  "name": "wordpress-frontity-snipcart",
  "state": {
    "frontity": {
      "url": "<https://snipcart-hotsauce-shop.azurewebsites.net/>",
      "title": "Snipcart Hot Sauce Shop",
      "description": "The Hottest Hot Sauce Shop!"
    }
  },
  "packages": [
    {
      "name": "@frontity/twentytwenty-theme",
      "state": {
        "theme": {
          "menu": [
            [
              "Shop",
              "/"
            ],
            [
              "About Us",
              "/about-us/"
            ]
          ],
          "featured": {
            "showOnList": false,
            "showOnPost": false
          }
        }
      }
    },
    {
      "name": "@frontity/wp-source",
      "state": {
        "source": {
          "api": "<https://snipcart-hotsauce-shop.azurewebsites.net/wp-json>"
        }
      }
    },
    "@frontity/tiny-router",
    "@frontity/html2react"
  ]
};

export default settings;

Chúng tôi cũng cần thêm kết nối Frontity vào dữ liệu trường tùy chỉnh và tìm nạp thông tin sản phẩm của chúng tôi. Để làm như vậy, hãy thay thế nội dung của packages/twentytwenty-theme/src/index.jsbằng nội dung sau:

import Theme from "./components";
import image from "@frontity/html2react/processors/image";
import link from "@frontity/html2react/processors/link";

// Custom handler for ACF options
const acfOptionsHandler = {
  pattern: "acf-options-page",
  func: async ({ route, state, libraries }) => {
    // 1. Get ACF option page from REST API.
    const response = await libraries.source.api.get({
      endpoint: `/acf/v3/posts`
    });
    const option = await response.json();

    // 2. Add data to `source`.
    const data = state.source.get(route);
    Object.assign(data, { ...option, isAcfOptionsPage: true });
  }
};

const twentyTwentyTheme = {
  name: "@frontity/twentytwenty-theme",
  roots: {
    /**
     *  In Frontity, any package can add React components to the site.
     *  We use roots for that, scoped to the `theme` namespace.
     */
    theme: Theme,
  },
  state: {
    /**
     * State is where the packages store their default settings and other
     * relevant state. It is scoped to the `theme` namespace.
     */
    theme: {
      colors: {
        gray: {
          base: "#6D6D6D",
          light: "#DCD7CA",
          lighter: "#F5EFE0",
        },
        primary: "#0aa7f5",
        headerBg: "#ffffff",
        footerBg: "#ffffff",
        bodyBg: "#f1f2f4",
      },
      // Whether to show the search button in page header
      showCartInHeader: true,
      // Menu links to display in the header
      menu: [],
      // State for the menu on mobile
      isMobileMenuOpen: false,
      // State for the search modal on mobile
      isSearchModalOpen: false,
      // Whether to show all post content or only excerpt (summary) in archive view
      showAllContentOnArchive: false,
      // Settings for the featured media (image or video)
      featuredMedia: {
        // Whether to show it on archive view
        showOnArchive: true,
        // Whether to show it on post
        showOnPost: true,
      },
      // Whether to auto-fetch links on a page. Values can be "no" | "all" | "in-view" | "hover"
      autoPrefetch: "in-view",

      /**
       * At the moment, we only include the ascii characters of Inter font.
       * Values can be "us-ascii" | "latin" | "all".
       */
      fontSets: "all",
    },
  },

  /**
   * Actions are functions that modify the state or deal with other parts of
   * Frontity like libraries.
   */
  actions: {
    theme: {
      beforeSSR: async ({ state, actions }) => {
        // This will make Frontity wait until the ACF options
        // page has been fetched and it is available
        // using state.source.get("acf-options-page").
        await actions.source.fetch("posts");
      },
      openMobileMenu: ({ state }) => {
        state.theme.isMobileMenuOpen = true;
      },
      closeMobileMenu: ({ state }) => {
        state.theme.isMobileMenuOpen = false;
      },
      openSearchModal: ({ state }) => {
        state.theme.isSearchModalOpen = true;
      },
      closeSearchModal: ({ state }) => {
        state.theme.isSearchModalOpen = false;
      },
    },
  },
  libraries: {
    source: {
      handlers: [acfOptionsHandler]
    },
    html2react: {
      /**
       * Add a processor to `html2react` so it processes the `<img>` tags
       * and internal link inside the content HTML.
       * You can add your own processors too.
       */
      processors: [image, link],
    },
  },
};

export default twentyTwentyTheme;

Chúng ta có thể xem nội dung WordPress của mình bằng cách xây dựng dự án của mình. Chạy lệnh xây dựng trong thiết bị đầu cuối:

npx frontify dev

Sau khi được xây dựng, trình duyệt web của bạn sẽ tự động khởi chạy máy chủ cục bộ. Nếu không, chỉ cần truy cập http://localhost:3000 .

Bước 5: Cài đặt Snipcart

Thêm packages/twentytwenty-theme/src/components/index.jsgợi ý kết nối trước Snipcart và biểu định kiểu trong <head>phần tử:

<Head>
  //..
    <link rel="preconnect" href="<https://app.snipcart.com>"/>
    <link rel="preconnect" href="<https://cdn.snipcart.com>"/>
    <link rel="stylesheet" href="<https://cdn.snipcart.com/themes/v3.2.2/default/snipcart.css>" />
</Head>

Trong cùng một tệp, hãy thêm Snipcart vào trang web của chúng tôi bằng cách dán hai dòng này dưới <Footer />phần tử:

<script async src="<https://cdn.snipcart.com/themes/v3.2.2/default/snipcart.js>"></script>
<div hidden id="snipcart" data-api-key="YOUR_PUBLIC_API_KEY"></div>

Nhắc nhở thay thế YOUR_PUBLIC_API_KEYbằng khóa API công khai của bạn. Bạn có thể tìm thấy nó trong Bảng điều khiển Snipcart ở chế độ thử nghiệm.

Bước 6: Tạo nút mua Snipcart & thành phần thẻ sản phẩm

Bây giờ Snipcart đã được cài đặt, đã đến lúc kết nối các trường tùy chỉnh của sản phẩm mà chúng tôi đã tạo trước đó với nút mua Snipcart. Đồng thời, điều này sẽ cho phép Frontity hiển thị thông tin sản phẩm mà chúng tôi đã nhập trong WordPress.

Để làm như vậy, chúng tôi sẽ tạo một thư mục mới packages/twentytwenty-theme/src/componentscó tên ecommercevà tạo hai tệp mới trong đó. Một tên product-card.jsvà một tên khác được đặt tên snipcart-button.js.

Trong product-card.js, hãy tạo một thành phần mới có tên ProductCardsẽ nhận các bài đăng WordPress (thông tin sản phẩm của chúng tôi) làm chỗ dựa. Thành phần này cũng sẽ gọi SnipcartButtonthành phần sẽ tạo sau. Để làm như vậy, hãy thêm phần sau vào tệp:

import SnipcartButton from "./snipcart-button";

const ProductCard = ({post}) => {
    const product = {
        name: post.acf.product_name,
        id: post.id,
        price: post.acf?.price,
        image: post.acf?.image,
        description: post.acf?.description
    }

    return (
        <article>
            <img src={post.acf.image} />
            <div> {post.acf.description} </div>
            <div>
              <strong> ${post.acf.price} </strong>
            </div>
            <SnipcartButton product={product} />
        </article>
    )
}

export default ProductCard;

Bây giờ, hãy tạo SnipcartButtonthành phần của chúng ta bằng cách thêm những thứ sau vào snipcart-button.js:

const SnipcartButton = ({product}) => {
    return (
        <button className="snipcart-add-item"
            data-item-name={product.name}
            data-item-price={product.price}
            data-item-image={product.image}
            data-item-id={product.id}
            data-item-description={product.description}>Add to cart
        </button>
    )
 }

 export default SnipcartButton;

Bước 7: Thêm các thành phần của chúng tôi vào các trang của chúng tôi

Bây giờ chúng tôi đã tạo ở bước cuối cùng cho trang chủ và trang sản phẩm của chúng tôi. Để làm như vậy, hãy thay thế nội dung của packages/twentytwenty-theme/src/components/post/post.jsbằng nội dung sau:

import { styled, connect } from "frontity";
import { useEffect } from "react";
import FeaturedMedia from "./featured-media";
import {
  EntryContent,
  Post as _Post,
  PostHeader,
  PostInner,
  PostTitle,
  PostCaption,
  SectionContainer,
} from "./post-item";
import ProductCard from "./../ecommerce/product-card";

/**
 * The Post component that the TwentyTwenty theme uses for rendering any kind of
 * "post type" (posts, pages, attachments, etc.).
 *
 * It doesn't receive any prop but the Frontity store, which it receives from
 * {@link connect}. The current Frontity state is used to know which post type
 * should be rendered.
 *
 * @param props - The Frontity store (state, actions, and libraries).
 *
 * @example
 * 
 * <Switch>
 *   <Post when={data.isPostType} />
 * </Switch>
 * 
 *
 * @returns The {@link Post} element rendered.
 */
const Post = ({ state, actions, libraries }) => {
  // Get information about the current URL.
  const data = state.source.get(state.router.link);

  // Get the data of the post.
  const post = state.source[data.type][data.id];

  // Get the html2react component.
  const Html2React = libraries.html2react.Component;

  const isProduct = (post) => {
    return !!post.acf.price;
  }

  /**
   * Once the post has loaded in the DOM, prefetch both the
   * home posts and the list component so if the user visits
   * the home page, everything is ready and it loads instantly.
   */
  useEffect(() => {
    actions.source.fetch("/");
  }, [actions.source]);

  // Load the post, but only if the data is ready.
  return data.isReady ? (
    <PostArticle>
      <Header>
        <SectionContainer>
          {/* If the post has categories, render the categories */}
          <PostTitle
            as="h1"
            className="heading-size-1"
            dangerouslySetInnerHTML={{ __html: post.title.rendered }}
          />
          {/* If the post has a caption (like attachments), render it */}
          {post.caption && (
            <PostCaption
              dangerouslySetInnerHTML={{ __html: post.caption.rendered }}
            />
          )}
        </SectionContainer>
      </Header>

      {/*
       * If the want to show featured media in the
       * list of featured posts, we render the media.
       */}
      {state.theme.featuredMedia.showOnPost && (
        <FeaturedImage id={post.featured_media} isSinglePost={true} />
      )}

      {/* If the post has a description (like attachments), we render it */}
      {post.description && (
        <PostInner size="thin">
          <EntryContent
            dangerouslySetInnerHTML={{ __html: post.description.rendered }}
          />
        </PostInner>
      )}

      {/* If the post has content, we render it */}
      {post.content && isProduct(post) && (
        <PostInner size="thin">
          <EntryContent>
            <ProductCard post={post} />
          </EntryContent>
        </PostInner>
      )}

      {post.content && !isProduct(post) && (
        <PostInner size="thin">
          <EntryContent>
            <Html2React html={post.content.rendered} />
          </EntryContent>
          {/* If the post has tags, render it */}
          {post.tags && <PostTags tags={tags} />}
        </PostInner>
      )}
    </PostArticle>
  ) : null;
};

export default connect(Post);

const Header = styled(PostHeader)`
  background-color: #fff;
  margin: 0;
  padding: 4rem 0;
  @media (min-width: 700px) {
    padding: 8rem 0;
  }
`;

const PostArticle = styled(_Post)`
  padding-top: 0 !important;
`;

const FeaturedImage = styled(FeaturedMedia)`
  margin-top: 0 !important;
  position: relative;

  > div {
    position: relative;
  }

  &:before {
    background: #fff;
    content: "";
    display: block;
    position: absolute;
    bottom: 50%;
    left: 0;
    right: 0;
    top: 0;
  }
`;

Như bạn có thể thấy, chúng tôi đã nhập ProductCardthành phần của mình và thêm một chức năng trợ giúp nhỏ để giúp chúng tôi xác định xem bài đăng có thuộc tính sản phẩm hay không. Chúng tôi đang sử dụng chức năng này để hiển thị thẻ sản phẩm hoặc bài đăng WordPress thông thường.

Chúng tôi cũng sẽ cần thay đổi nội dung của packages/twentytwenty-theme/src/components/post/post-item.jsđể hiển thị thẻ sản phẩm của chúng tôi trên trang chủ.

import { connect, styled } from "frontity";
import Link from "../link";
import FeaturedMedia from "./featured-media";
import ProductCard from "./../ecommerce/product-card";

/**
 * Article Component.
 *
 * It renders the preview of a blog post. Each blog post contains:
 * - Title: clickable title of the post.
 * - FeaturedMedia: the featured image/video of the post.
 *
 * @param props.state - The Frontity state.
 * @param props.libraries - The Frontity libraries.
 * @param props.item - The post entity.
 * @param props.showExcerpt - If the post excerpt should be rendered.
 * @param props.showMedia - If the featured media should be rendered.
 *
 * @returns React element.
 */
const PostItem = ({
  state,
  libraries,
  item,
  showMedia = true,
}) => {

  const post = state.source[item.type][item.id];
  const { Component: Html2React } = libraries.html2react;
  return (
    <Post>
      <PostHeader>
        <SectionContainer>
          {/* The clickable heading for the post */}
          <PostLink link={item.link}>
            <PostItemTitle
              className="heading-size-1"
              dangerouslySetInnerHTML={{ __html: item.title.rendered }}
            />
          </PostLink>
        </SectionContainer>
      </PostHeader>

      {/*
       * If the want to show featured media in the
       * list of featured posts, we render the media.
       */}
      {state.theme.featuredMedia.showOnArchive && showMedia && (
        <FeaturedMedia id={item.featured_media} />
      )}

      {post && post.content && (
        <PostInner size="thin">
          <EntryContent>
            <ProductCard post={post} />
          </EntryContent>
        </PostInner>
      )}
    </Post>
  );
};

// Connect the Item to gain access to `state` as a prop
export default connect(PostItem);

// All styles :)

export const Post = styled.article`
  &:first-of-type {
    padding: 4rem 0 0;
  }

  @media (min-width: 700px) {
    &:first-of-type {
      padding: 8rem 0 0;
    }
  }
`;

export const PostHeader = styled.header`
  text-align: center;
`;

// Header sizes bases on style.css
const maxWidths = {
  thin: "58rem",
  small: "80rem",
  medium: "100rem",
};

/**
 * Return a CSS size depending on the value of the `size` prop received (see
 * {@link maxWidths}).
 *
 * @param props - Component props, including a `size` one.
 * @returns Size in CSS units.
 */
const getMaxWidth = (props) => maxWidths[props.size] || maxWidths["medium"];

export const SectionContainer = styled.div`
  margin-left: auto;
  margin-right: auto;
  width: calc(100% - 4rem);
  max-width: ${getMaxWidth};

  @media (min-width: 700px) {
    width: calc(100% - 8rem);
  }
`;

export const PostItemTitle = styled.h2`
  margin: 0;
  @media (min-width: 700px) {
    font-size: 6.4rem;
  }
`;

export const PostTitle = styled.h1`
  margin: 0;
`;

export const PostCaption = styled(SectionContainer)`
  /* .section-inner.max-percentage */
  margin-left: auto;
  margin-right: auto;
  max-width: ${getMaxWidth({ size: "small" })};
  width: 100%;

  /* .singular .intro-text */
  margin-top: 2rem;
  font-size: 2rem;
  letter-spacing: -0.0315em;
  line-height: 1.4;

  @media (min-width: 700px) {
    margin-top: 2.5rem;
    font-size: 2.6rem;
  }
  @media (min-width: 1000px) {
    font-size: 2.8rem;
  }
  @media (min-width: 1220px) {
    font-size: 3.2rem;
    letter-spacing: -0.03125em;
    line-height: 1.375;
  }
`;

const PostLink = styled(Link)`
  color: #000000;
  text-decoration: none;
  display: inline-block;
  &:hover {
    text-decoration: underline;
  }
`;

export const PostInner = styled(SectionContainer)`
  padding-top: 5rem;
  @media (min-width: 700px) {
    padding-top: 8rem;
  }
`;

export const EntryContent = styled.div`
  line-height: 1.5;
  max-width: 58rem;
  font-family: "Hoefler Text", Garamond, "Times New Roman", serif;
  letter-spacing: normal;

  @media (min-width: 700px) {
    font-size: 2.1rem;
  }

  > *:first-of-type {
    margin-top: 0;
  }

  figure {
    margin: 2em 0;
    max-width: 100%;
  }

  h1,
  h2,
  h3,
  h4,
  h5,
  h6,
  cite,
  figcaption,
  table,
  address,
  .wp-caption-text,
  .wp-block-file {
    font-family: "Inter", -apple-system, BlinkMacSystemFont, "Helvetica Neue",
      Helvetica, sans-serif;
  }

  h1,
  h2,
  h3,
  h4,
  h5,
  h6 {
    margin: 3.5rem auto 2rem;
  }

  @media (min-width: 700px) {
    h1,
    h2,
    h3 {
      margin: 6rem auto 3rem;
    }

    h4,
    h5,
    h6 {
      margin: 4.5rem auto 2.5rem;
    }
  }
`;

Và bây giờ bạn sẽ có thể xem sản phẩm của mình và nút ‘Thêm vào giỏ hàng’ trực tiếp từ trang chủ.

Bước 8: Thêm nút xem giỏ hàng

Bây giờ chúng ta hãy thêm một nút trong tiêu đề để xem giỏ hàng.

Với chủ đề Frontity được cài đặt, chúng tôi có hai chế độ xem; di động và máy tính để bàn. Chúng tôi sẽ tận dụng kiểu dáng thành phần tìm kiếm được xác định trước để tạo nút của chúng tôi.

Đầu tiên, hãy tạo một cart-button.jstệp packages/twentytwenty-theme/src/components/với nội dung sau:

import { connect, styled } from "frontity";
import {
  BaseToggle,
  ToggleWrapper,
} from "./navigation/nav-toggle";

const CartButton = ({ state, actions }) => {

  return (
    <HeaderToggle>
      <ToggleWrapper>
                <BaseToggle className="snipcart-checkout">
          🛒
        </BaseToggle>
      </ToggleWrapper>
    </HeaderToggle>
  );
};

export default connect(CartButton);

const HeaderToggle = styled.div`
  display: none;

  @media (min-width: 1000px) {
    display: flex;
    flex-shrink: 0;
    margin-right: -3rem;
    margin-left: 3rem;
  }

  @media (min-width: 1220px) {
    margin-right: -4rem;
    margin-left: 4rem;
  }
`;

Sau đó, chúng tôi sẽ thêm nút giỏ hàng trên điện thoại di động của mình packages/twentytwenty-theme/src/components/mobilebằng cách tạo một cart-button.jsmã có chứa mã này:

import { connect, styled } from "frontity";
import {
  CartToggle,
  ToggleWrapper,
} from "../navigation/nav-toggle";

const MobileCartButton = ({ state, actions }) => {

  return (
    <ToggleWrapper>
      <ShowMobile>
                <BaseToggle className="snipcart-checkout">
          🛒
        </BaseToggle>
      </ShowMobile>
    </ToggleWrapper>
  );
};

export const ShowMobile = styled.div`
  display: inline-block;

  @media (min-width: 1000px) {
    display: none;
  }
`;
export default connect(MobileCartButton);

Với các thành phần này được tạo, chúng ta sẽ cần xác định chúng trong các thành phần tiêu đề:

packages/src/components/header.js

import { connect, Global, Head, styled } from "frontity";
//..
import CartButton from "./cart-button";
import MobileCartButton from "./mobile/cart-button";

return (
    <PageHeader bg={headerBg} id="site-header">
      <HeaderInner>
        <TitleWrapper>
          {/* Cart button on mobile */}
          <MobileCartButton />

          {/* Heading and Description of the site */}
          <TitleGroup>
            <SiteTitle>
              <StyledLink link="/">{title}</StyledLink>
            </SiteTitle>
            <SiteDescription>{description}</SiteDescription>
          </TitleGroup>

          {/* Mobile menu button and modal */}
          <MobileMenuButton />
          <MobileMenuModal />
        </TitleWrapper>

        <HeaderNavigationWrapper>
          {/* Desktop navigation links */}
          <Navigation />
          {/* Desktop cart button */}
          <CartButton />
        </HeaderNavigationWrapper>
      </HeaderInner>
    </PageHeader>
  );
};

//..
const HeaderNavigationWrapper = styled.div`
  display: none;

  @media (min-width: 1000px) {
    align-items: center;
    display: flex;
  }
`;

Nếu bạn làm mới, bây giờ bạn sẽ có thể thấy nút hiển thị giỏ hàng trong tiêu đề.

Bước 9: Thêm một số kiểu

Bước cuối cùng sẽ là thêm phong cách vào trang web và các thành phần của chúng tôi.

Khi chúng tôi xây dựng dự án Frontity của mình, chúng tôi đã cài đặt một chủ đề được xác định trước, nhưng tôi muốn tùy chỉnh cửa hàng nhiều hơn một chút và thêm một số kiểu dáng vào thành phần mà chúng tôi đã tạo.

Hãy thêm một số kiểu dáng vào nút “Thêm vào giỏ hàng” và các sản phẩm của chúng tôi.

Để làm như vậy, hãy thêm một hằng số mới có tên snipcartStyledtrong tệp kiểu chung có tại packages/twentytwenty-theme/src/components/styles/global-styles.js:

const snipcartStyle = (colors) => css`
  .snipcart-add-item {
    padding: 10px;
    border-radius: 4px;
    cursor: pointer;
    transition: .2s ease-out;
    transition-property: color,border-color,background-color,box-shadow;
    cursor: pointer;
    color: white;
    background-color: #1a4db3;
  }
  .snipcart-add-item:hover {
    box-shadow: var(--shadow-buttonPrimary-hover,0 10px 4px -8px rgba(0,0,0,.5));
    background-color: #0d59f2;
  }

  .snipcart-checkout {
    padding: 5px;
    cursor: pointer;
    background: none;
  }

  .product-price {
    display: flex;
    align-items: center;
    font-size: 1.5em;
  }

  .SectionContainer {
    display: flex;
    justify-content: center;
  }
`;

const productStyle = (colors) => css`
  img {
    display: block;
    margin-left: auto;
    margin-right: auto;
    width: 50%;
    max-width: 100px;
    padding: 10px;

  }
  article {
    text-align: center;
    padding: 5px;
  }
  `;

//..

const globalStyle = (colors) =>
  css([
    cssReset,
    documentSetup(colors),
    accessibilitySettings,
    elementBase(colors),
    elementBase700,
    elementBase1220,
    listStyle,
    quoteStyle(colors),
    codeStyle(colors),
    mediaStyle(colors),
    tableStyles(colors),
    snipcartStyle(colors),
  ]);

export default globalStyle;

Như bạn thấy, chúng ta cũng cần thêm đối tượng kiểu này vào mảng mà chúng ta truyền cho csshàm được gọi trong globalStylehàm của chúng ta.

Đó là nó. Bây giờ bạn có một trang thương mại điện tử được xây dựng trên WordPress và React!

Bản demo trực tiếp và repo GitHub

Xem demo trực tiếp tại đây

Xem repo GitHub tại đây

Bớt tư tưởng

Là một người không phải là nhà phát triển, tôi rất thích và đánh giá cao sự dễ dàng mà Frontity mang lại khi xây dựng một trang web tĩnh với WordPress. Tôi cũng rất thích phát triển chỉ sử dụng JavaScript (sau khi phiên bản WordPress của chúng tôi được xây dựng và triển khai).

Thiết lập zero của Frontity cũng rất đáng để làm việc. Không phải định cấu hình định tuyến và API tăng tốc quá trình phát triển.

Bạn đã thử Frontity chưa? Hãy cho tôi biết trong phần bình luận suy nghĩ của bạn về khuôn khổ này và trải nghiệm của bạn như thế nào.

Related Articles

Trả lời

Email của bạn sẽ không được hiển thị công khai.

Back to top button