در این مقاله ، ما می آموزیم که monorepo چیست و چگونه monorepos در توسعه سریع برنامه ها با تجربه توسعه بهتر کمک می کند. ما در مورد مزایای استفاده از Nx ابزارهای توسعه برای مدیریت monorepo ، و یاد بگیرید که چگونه از آن ابزارها برای ایجاد یک Next.js کاربرد.

کد این مقاله در دسترس است GitHubبه می توانید نسخه نمایشی برنامه ای را که در حال ساخت آن هستیم پیدا کنید اینجابه

Monorepo چیست و چرا باید از آن استفاده کنیم

آ monorepo یک مخزن واحد است که شامل برنامه ها ، ابزارها و تنظیمات چندین پروژه یا اجزای پروژه است. این یک جایگزین برای ایجاد مخازن جداگانه برای هر پروژه یا بخشی از پروژه است.

سناریویی را در نظر بگیرید که در آن ما با استفاده از کتابخانه یا چارچوب اصلی یک برنامه داشبورد ایجاد می کنیم. ممکن است کد این برنامه جلویی در برنامه ذخیره شود dashboard مخزن اجزای UI که این مخزن از آنها استفاده می کند ممکن است در مخزن دیگری به نام ذخیره شود componentsبه در حال حاضر ، هر بار که ما به روز رسانی components مخزن ، ما باید وارد آن شویم dashboard مخزن و به روز رسانی components وابستگی

دو مخزن - داشبورد و اجزای سازنده

برای تسکین این مشکل ، می توانیم ادغام کنیم components repo با dashboard مخزن.

repo اجزاء به repo داشبورد ادغام شد

با این حال ، ممکن است یک برنامه پیش فرض دیگر برای سایت بازاریابی وجود داشته باشد که در آن ذخیره شده است marketing مخزن و بستگی به آن دارد components مخزن بنابراین ، ما باید کپی کنیم components و با آن ادغام کنید marketing همچنین. با این حال ، به همین دلیل ، هر گونه تغییر مربوط به components باید در دو مکان ساخته شود ، که ایده آل نیست.

داشبورد و مخازن بازاریابی ، هر کدام دارای یک کپی از اجزاء هستند

مشکل فوق را می توان با استفاده از monorepo ، جایی که dashboard، components و marketing اجزاء در یک مخزن واحد قرار دارند.

Monorepo حاوی داشبورد ، بازاریابی و اجزای سازنده است

مزایای مختلفی برای استفاده از monorepo وجود دارد:

  • به روز رسانی بسته ها بسیار ساده تر است ، زیرا همه برنامه ها و کتابخانه ها در یک مخزن واحد قرار دارند. از آنجا که همه برنامه ها و بسته ها تحت یک مخزن یکسان هستند ، افزودن کد جدید یا اصلاح کد موجود را می توان به راحتی آزمایش و ارسال کرد.
  • تغییر شکل کد بسیار ساده تر است ، زیرا ما مجبوریم این کار را در یک مکان واحد انجام دهیم به جای این که مطالب مشابه را در چندین مخزن تکرار کنیم.
  • monorepo امکان پیکربندی مداوم برای خطوط لوله CI/CD را فراهم می آورد ، که می تواند توسط همه برنامه ها و کتابخانه های موجود در یک مخزن مجدد مورد استفاده قرار گیرد.
  • انتشار بسته ها نیز به دلیل ابزارهایی مانند Nx بسیار آسان تر می شود.

این Nx CLI به ما در ایجاد برنامه های جدید Next.js و کتابخانه های کامپوننت React کمک می کند. همچنین به ما در اجرای یک وب سرور توسعه با بارگذاری مجدد ماژول کمک می کند. همچنین می تواند مجموعه ای از کارهای مهم دیگر مانند خط کشی ، قالب بندی و تولید کدبه مزیت استفاده از CLI مانند این این است که حس استانداردسازی را در پایگاه کد ما ایجاد می کند. همانطور که پایگاه کد ما رشد می کند ، مدیریت و درک پیچیدگی های اساسی بسیار دشوار است. Nx CLI با ارائه ابزارهایی برای ایجاد خودکار تولید کد ، اکثر این پیچیدگی ها را از بین می برد.

نرم افزار مورد نیاز

برای اجرای برنامه ما به موارد زیر نیاز داریم:

از این فناوری ها در برنامه استفاده می شود:

توجه: اگر می خواهید با این کار سرعت بیشتری کسب کنید ، می توانید نحوه نصب چندین نسخه Node.js را با استفاده از nvm بخوانید.

ما همچنین به یک مورد نیاز خواهیم داشت شکار محصول حساب.

نصب و راه اندازی بوت Nx Workspace

ما می توانیم آن را نصب کنیم Nx CLI با استفاده از دستور زیر:

npm install nx -g

دستور بالا Nx CLI را به صورت جهانی نصب می کند. این مفید است زیرا در حال حاضر ما می توانیم یک برنامه Next.js جدید با استفاده از این CLI از هر فهرست ایجاد کنیم.

بعد ، ما باید دستور زیر را در داخل پوشه ای که می خواهیم monorepo خود را ایجاد کنیم اجرا کنیم:

npx create-nx-workspace@latest nx-nextjs-monorepo

دستور بالا یک فضای کاری Nx ایجاد می کند. همه برنامه های Nx می توانند در داخل یک فضای کاری Nx قرار بگیرند.

ممکن است نیاز به تعویض داشته باشید nx-nextjs-monorepo با نام فضای کار شما می توان نام آن را هر چیزی که دوست دارید بگذارید. نام فضای کار به طور کلی نام یک سازمان ، شرکت و غیره است.

وقتی دستور بالا را اجرا می کنیم ، مجموعه ای از مراحل به ما داده می شود که نوع برنامه هایی را که می خواهیم با Nx ایجاد کنیم ایجاد می کند.

  • مرحله 1: ابتدا از ما می پرسد که چه نوع برنامه ای را می خواهیم ایجاد کنیم. Next.js را از لیست گزینه ها انتخاب می کنیم.

    یک فضای کاری ایجاد کنید

  • گام 2: نام برنامه ای را که می خواهیم ایجاد کنیم از ما می خواهد. ما می توانیم آن را هر چیزی بنامیم. در این مورد ، ما از آن به عنوان “شکار محصول” نام می بریم.

    نام برنامه را وارد کنید

  • مرحله 3: از ما می پرسد که می خواهیم از چه نوع شیوه نامه ای استفاده کنیم. ما Styled Components را انتخاب می کنیم.

    قالب پیش فرض شیوه نامه را وارد کنید

  • مرحله 4: از ما می پرسد که آیا می خواهیم استفاده کنیم Nx Cloud، که بستری برای افزایش سرعت ساخت برنامه های Nx است. در این مورد ، ما No را انتخاب می کنیم ، اما لطفاً آن را بررسی کنید.

    از Nx Cloud استفاده کنید؟

Nx اکنون همه فایلها و فهرستها را داربست کرده و ساختار زیر را برای ما ایجاد می کند.

ساختار دایرکتوری

این apps فهرست شامل همه برنامه های کاربردی ما است. در مورد ما ، این فهرست شامل برنامه Next.js است که ما در حال ساخت آن هستیم (نام product-hunt) این دایرکتوری همچنین شامل برنامه های آزمایشی سرتاسر (به نام product-hunt-e2e) داربست با استفاده از سروبه

این libs فهرست شامل همه کتابخانه ها مانند اجزا ، توابع مفید و غیره است. این کتابخانه ها می توانند توسط هر یک از برنامه های موجود در apps فهرست راهنما.

این tools فهرست شامل همه اسکریپت های سفارشی ، مدل های کد و غیره است که برای ایجاد تغییرات خاصی در پایگاه کد ما استفاده می شود.

توجه: اطلاعات بیشتر در مورد ساختار دایرکتوری در دسترس است اینجابه

ایجاد صفحه اول محصول شکار با استفاده از Next.js

در این مرحله ، ما می سازیم صفحه اول Producthuntبه ما داده ها را از API رسمی محصول شکاربه API محصول شکار رابط GraphQL را ارائه می دهد که در حال حاضر وجود دارد https://api.producthunt.com/v2/api/graphqlبه از طریق an قابل دسترسی است نشانه دسترسی، که می تواند از داشبورد API محصول Huntبه

برای ایجاد یک برنامه جدید ، باید بر روی آن کلیک کنیم افزودن یک برنامه کاربردی دکمه.

یک برنامه اضافه کنید

در مرحله بعد ، می توانیم یک نام برای برنامه خود و https: // localhost: 4200/ به عنوان Redirect URI برای برنامه جدید ما و روی آن کلیک کنید ایجاد برنامه دکمه.

یک برنامه ایجاد کنید

اکنون می توانیم اعتبار برنامه جدید خود را مشاهده کنیم.

اعتبارنامه برنامه جدید

در مرحله بعد ، ما باید a را تولید کنیم توسعه دهنده توکن با کلیک بر روی TOKEN ایجاد کنید دکمه در همان صفحه

ایجاد توکن توسعه دهنده

این یک توکن جدید ایجاد می کند و آن را در صفحه نشان می دهد.

نشان توسعه دهنده را نمایش دهید

در مرحله بعد ، ما باید این اطلاعات را در برنامه خود ذخیره کنیم. ما می توانیم یک جدید ایجاد کنیم .env.local فایل داخل apps/product-hunt فهرست با محتوای زیر:

// apps/product-hunt/.env.local

NEXT_PUBLIC_PH_API_ENDPOINT=https://api.producthunt.com/v2/api/graphql
NEXT_PUBLIC_PH_TOKEN=<your-developer-token>

از آنجا که API Product Hunt در GraphQL است ، باید چند بسته را نصب کنیم تا برنامه ما با GraphQL کار کند. برای نصب بسته های لازم ، از فهرست اصلی باید دستور زیر را اجرا کنیم:

yarn add graphql-hooks graphql-hooks-memcache

graphql-hooks حداقل مشتری اولین قلاب GraphQL است. این به ما در درخواست داده از سرور GraphQL کمک می کند.

graphql-hooks-memcache پیاده سازی حافظه پنهان برای حافظه است graphql-hooksبه

بعد ، ما باید کلاینت GraphQL را از graphql-hooks بسته ما می توانیم با ایجاد یک مورد جدید این کار را انجام دهیم graphql-client.ts فایل داخل apps/product-hunt/lib فهرست با محتوای زیر:



import { GraphQLClient } from "graphql-hooks";
import memCache from "graphql-hooks-memcache";
import { useMemo } from "react";

let graphQLClient;

const createClient = (initialState) => {
  return new GraphQLClient({
    ssrMode: typeof window === "undefined",
    url: process.env.NEXT_PUBLIC_PH_API_ENDPOINT, 
    cache: memCache({ initialState }),
    headers: {
      Authorization: `Bearer ${process.env.NEXT_PUBLIC_PH_TOKEN}`,
    },
  });
};

export const initializeGraphQL = (initialState = null) => {
  const _graphQLClient = graphQLClient ?? createClient(initialState);

  
  
  
  
  if (initialState && graphQLClient) {
    graphQLClient.cache = memCache({
      initialState: Object.assign(
        graphQLClient.cache.getInitialState(),
        initialState
      ),
    });
  }

  
  if (typeof window === "undefined") {
    return _graphQLClient;
  }

  
  if (!graphQLClient) {
    graphQLClient = _graphQLClient;
  }

  return _graphQLClient;
};

export const useGraphQLClient = (initialState) => {
  const store = useMemo(() => initializeGraphQL(initialState), [initialState]);

  return store;
};

کد بالا شبیه کد زیر است مثال رسمی Next.js GraphQLبه ایده اصلی فایل فوق ایجاد یک سرویس گیرنده GraphQL است که به ما در درخواست داده از سرور GraphQL کمک می کند.

این createClient تابع وظیفه ایجاد کلاینت GraphQL با استفاده از graphql-hooks بسته

این initializeGraphQL تابع مسئول راه اندازی اولیه سرویس گیرنده GraphQL ما با استفاده از است createClient و همچنین هیدراته کردن مشتری GraphQL ما در سمت مشتری. این امر ضروری است زیرا ما از Next.js استفاده می کنیم ، که به ما امکان می دهد داده ها را در سمت سرویس گیرنده و سرور واکشی کنیم. بنابراین ، اگر داده ها در سمت سرور واکشی شوند ، طرف سرویس گیرنده نیز باید با همان داده ها بدون نیاز به سرور GraphQL هیدراته شود.

این useGraphQLClient یک قلاب است که می تواند برای تولید سرویس گیرنده GraphQL استفاده شود.

در مرحله بعد ، ما همچنین باید یک فایل دیگر ایجاد کنیم ، graphql-request.ts، درون apps/product-hunt/lib فهرست با محتوای زیر:



const defaultOpts = {
  useCache: true,
};






const graphQLRequest = async (client, query, options = defaultOpts) => {
  const operation = {
    query,
  };
  const cacheKey = client.getCacheKey(operation, options);
  const cacheValue = await client.request(operation, options);

  client.saveCache(cacheKey, cacheValue);

  return cacheValue;
};

export default graphQLRequest;

این graphQLRequest تابع مسئول برگرداندن نتیجه پرس و جو GraphQL و همچنین افزودن نتیجه به حافظه پنهان سرویس گیرنده GraphQL است.

کد بالا شبیه کد زیر است مثال رسمی Next.js GraphQLبه

بعد ، ما باید برنامه را به روز کنیم apps/product-hunt/pages/_app.tsx فایل با محتوای زیر:



import { ClientContext } from "graphql-hooks";
import { AppProps } from "next/app";
import Head from "next/head";
import React from "react";
import { useGraphQLClient } from "../lib/graphql-client";

const NextApp = ({ Component, pageProps }: AppProps) => {
  const graphQLClient = useGraphQLClient(pageProps.initialGraphQLState);

  return (
    <ClientContext.Provider value={graphQLClient}>
      <Head>
        <title>Welcome to product-hunt!</title>
      </Head>
      <Component {...pageProps} />
    </ClientContext.Provider>
  );
};

export default NextApp;

کد بالا اطمینان حاصل می کند که کل برنامه ما به آن دسترسی دارد ارائه دهنده زمینه GraphQL با پیچاندن برنامه ما با ClientContext.Providerبه

در مرحله بعد ، ما باید یک فایل دیگر ایجاد کنیم ، all-posts.ts، درون apps/product-hunt/queries فهرست با محتوای زیر:



const ALL_POSTS_QUERY = `
  query allPosts {
    posts {
      edges {
        node {
          id
          name
          description
          votesCount
          website
          thumbnail {
            url
          }
        }
      }
    }
  }
`;

export default ALL_POSTS_QUERY;

پرس و جو GraphQL فوق به ما اجازه می دهد تا همه پست ها را از نقطه پایانی ProductHunt GraphQL API واکشی کنیم.

بیایید یک مورد جدید نیز ایجاد کنیم product.ts فایل داخل apps/product-hunt/types دایرکتوری با محتوای زیر برای تعریف Product نوع:



export default interface Product {
  id: number;
  name: string;
  tagline: string;
  slug: string;
  thumbnail: {
    image_url: string;
  };
  user: {
    avatar_url: string;
    name: string;
  };
}

کد بالا کد را اضافه می کند انواع TypeScript برای Productبه یک محصول می تواند دارای شناسه ، نام ، نشان ، اسلاگ ، تصویر کوچک و کاربر باشد. اینگونه است که Product Hunt GraphQL داده ها را برمی گرداند.

بعد ، ما باید برنامه را به روز کنیم apps/product-hunt/pages/index.tsx فایل با محتوای زیر:



import { useQuery } from "graphql-hooks";
import { GetStaticProps, NextPage } from "next";
import Image from "next/image";
import React from "react";
import { initializeGraphQL } from "../lib/graphql-client";
import graphQLRequest from "../lib/graphql-request";
import {
  StyledCard,
  StyledCardColumn,
  StyledCardLink,
  StyledCardRow,
  StyledCardTagline,
  StyledCardThumbnailContainer,
  StyledCardTitle,
  StyledContainer,
  StyledGrid,
} from "../public/styles";
import ALL_POSTS_QUERY from "../queries/all-posts";
import Product from "../types/product";

interface IProps {
  hits: Product[];
}

const ProductsIndexPage: NextPage<IProps> = () => {
  const { data } = useQuery(ALL_POSTS_QUERY);

  return (
    <StyledContainer>
      <StyledGrid>
        {data.posts.edges.map(({ node }) => {
          return (
            <StyledCardLink key={node.id} href={node.website} target="_blank">
              <StyledCard>
                <StyledCardColumn>
                  <StyledCardThumbnailContainer>
                    <Image src={node.thumbnail.url} layout="fill" />
                  </StyledCardThumbnailContainer>
                </StyledCardColumn>
                <StyledCardColumn>
                  <StyledCardRow>
                    <StyledCardTitle>{node.name}</StyledCardTitle>
                    <StyledCardTagline>{node.description}</StyledCardTagline>
                  </StyledCardRow>
                </StyledCardColumn>
              </StyledCard>
            </StyledCardLink>
          );
        })}
      </StyledGrid>
    </StyledContainer>
  );
};

export const getStaticProps: GetStaticProps = async () => {
  const client = initializeGraphQL();

  await graphQLRequest(client, ALL_POSTS_QUERY);

  return {
    props: {
      initialGraphQLState: client.cache.getInitialState(),
    },
    revalidate: 60,
  };
};

export default ProductsIndexPage;

در قطعه کد بالا ، ما دو کار را انجام می دهیم:

  1. ما داده ها را از طریق واکشی می کنیم ALL_POSTS_QUERY پرس و جو GraphQL و سپس ما در حال نقشه برداری بر روی data آرایه توسط API ProductHunt باز می گردد.

  2. ما داده ها را واکشی می کنیم در زمان ساخت از طریق getStaticProps، که یک تابع Next.js است. با این حال ، اگر داده ها را در زمان ساخت خود واکشی کنیم ، ممکن است داده ها منسوخ شوند. بنابراین ، ما از revalidate گزینه. اعتبار مجدد مقدار اختیاری (در ثانیه) و پس از آن ایجاد مجدد صفحه می تواند رخ دهد. این نیز به عنوان شناخته می شود بازسازی استاتیک افزایشیبه

بیایید همچنین سبک ها را با افزودن محتوای زیر در داخل apps/product-hunt/public/styles.ts فایل:



import styled from "styled-components";

export const StyledContainer = styled.div`
  padding: 24px;
  max-width: 600px;
  margin: 0 auto;
  font-family: sans-serif;
`;

export const StyledGrid = styled.div`
  display: grid;
  grid-template-columns: repeat(1, minmax(0, 1fr));
  grid-gap: 24px;
`;

export const StyledCardLink = styled.a`
  text-decoration: none;
  color: #000;
`;

export const StyledCard = styled.div`
  display: flex;
  gap: 12px;
  padding: 12px;
  background-color: #f7f7f7;
`;

export const StyledCardColumn = styled.div`
  display: flex;
  flex-direction: column;
  gap: 4px;
  justify-content: space-between;
`;

export const StyledCardRow = styled.div`
  display: flex;
  flex-direction: column;
  gap: 4px;
`;

export const StyledCardThumbnailContainer = styled.div`
  object-fit: cover;

  width: 150px;
  height: 150px;
  position: relative;
`;

export const StyledCardTitle = styled.div`
  font-size: 18px;
  font-weight: bold;
`;

export const StyledCardTagline = styled.div`
  font-size: 14px;
  line-height: 1.5;
`;

حالا اگر بدویم yarn start دستور داخل پنجره ترمینال جدید ، صفحه زیر را مشاهده می کنیم http: // localhost: 4200/به

خطای سرور

برای برطرف کردن مشکل فوق ، باید برنامه خود را به روز کنیم apps/product-hunt/next.config.js فایل با محتوای زیر:




const withNx = require("@nrwl/next/plugins/with-nx");

module.exports = withNx({
  nx: {
    
    
    svgr: true,
  },
  images: {
    domains: ["ph-files.imgix.net", "ph-avatars.imgix.net"],
  },
});

اضافه کردیم دامنه ها که از آن Product Hunt API تصاویر را دریافت می کند. این ضروری است زیرا ما از آن استفاده می کنیم جزء تصویر بعدیبه

حال ، اگر سرور خود را مجدداً راه اندازی کنیم ، باید بتوانیم صفحه زیر را در آن مشاهده کنیم http: // localhost: 4200/به

صفحه آزمایشی Product Hunt

ایجاد کتابخانه اجزای قابل استفاده مجدد

ما با موفقیت صفحه اول Product Hunt را ایجاد کردیم. با این حال ، ما می توانیم ببینیم که همه سبک های ما تحت یک برنامه واحد هستند. بنابراین ، اگر بخواهیم از همان سبک ها هنگام ساختن برنامه دیگری استفاده کنیم ، باید این سبک ها را در برنامه جدید کپی کنیم.

یکی از راه های حل این مسئله ایجاد کتابخانه اجزای جداگانه و ذخیره این سبک ها در آنجا است. آن کتابخانه کامپوننت را می توان با برنامه های متعدد مجدداً استفاده کرد.

برای ایجاد یک کتابخانه React جدید در Nx ، می توانیم موارد زیر را اجرا کنیم فرمان از ریشه پروژه ما:

nx generate @nrwl/react:library components

دستور بالا فوری را در تصویر زیر به ما می دهد.

قالب شیوه نامه را انتخاب کنید

از آنجا که از Styled Components استفاده می کنیم ، آن گزینه را در اعلان بالا انتخاب می کنیم. پس از انتخاب آن گزینه ، تغییرات زیر را در ترمینال خود مشاهده می کنیم.

لیست فایلهای تولید شده

در مرحله بعد ، همه استایل ها را از کپی می کنیم apps/product-hunt/public/styles.ts به درون libs/components/src/lib/components.tsx فایل.

ما همچنین باید همه سبک ها را از این کتابخانه وارد کنیم. برای انجام این کار ، ما باید برنامه خود را تغییر دهیم apps/product-hunt/pages/index.tsx فایل:



import {
  StyledCard,
  StyledCardColumn,
  StyledCardLink,
  StyledCardRow,
  StyledCardTagline,
  StyledCardThumbnailContainer,
  StyledCardTitle,
  StyledContainer,
  StyledGrid,
} from "@nx-nextjs-monorepo/components";

اگر به وضعیت خودمان نگاه کنیم tsconfig.base.json فایل ، خط زیر را مشاهده می کنیم:



"paths": {
  "@nx-nextjs-monorepo/components": ["libs/components/src/index.ts"]
}

@nx-nextjs-monorepo/components نام کتابخانه اجزای ما است. بنابراین ، ما همه سبک ها را از آن کتابخانه در وارد کرده ایم apps/product-hunt/pages/index.tsx فایل.

ما می توانیم حذف کنیم apps/product-hunt/public/styles.ts فایل ، زیرا دیگر به آن نیاز نداریم

اکنون ، اگر سرور Nx خود را مجدداً راه اندازی کنیم ، صفحه زیر را مشاهده می کنیم http: // localhost: 4200/به

نسخه ی نمایشی Produzct Hunt هنوز در حال اجرا است

نتیجه

در این مقاله ، ما آموخته ایم که چگونه می توانیم از Nx برای ایجاد monorepo با Next.js و Styled Components استفاده کنیم. ما همچنین آموخته ایم که چگونه استفاده از monorepos می تواند تجربه توسعه و سرعت ساخت برنامه های کاربردی را بهبود بخشد. ما یک برنامه Next.js و یک کتابخانه Styled Components ایجاد کرده ایم ، اما با استفاده از Nx ، امکان ایجاد زاویه دار، سرو، لانه، گتسبی، بیان و کتاب داستان برنامه های کاربردی با استفاده از ژنراتورهای خود

و فراموش نکنید: کد این مقاله در دسترس است GitHub، و می توانید نسخه نمایشی برنامه را پیدا کنید اینجابه