ホームページ  >  記事  >  ウェブフロントエンド  >  ゼロから立ち上げまで: リミックスを活用した開発の取り組みから得た重要なポイント

ゼロから立ち上げまで: リミックスを活用した開発の取り組みから得た重要なポイント

王林
王林オリジナル
2024-09-07 00:01:08430ブラウズ

約 6 か月前、私は会社の Web アプリケーションの基盤として Remix を選択するという、一部の人が大胆な決断だと言うかもしれませんが、その決断を下しました。今日は、一歩下がって私たちが行った選択を振り返る時期が来たと思います。インフラストラクチャに関する主な決定事項を確認し、途中で実用的な使用例を少し散りばめます。

それでは、早速、この旅のハイライトとハイライト、つまり満足感と学んだ教訓を組み合わせて説明しましょう。

From Zero to Launch: Key Takeaways from Our Remix-Powered Development Journey

リミックス (または React Router と言うべきでしょうか?)

ハイライト: リミックス

Remix は NextJS ほど普及しておらず、私の知る限り大企業で Remix を使用している例はあまりなかったため、これはおそらく私が当時行ったインフラストラクチャの決定の中で「最もリスクが高い」ものでした。
今日に遡って - ChatGPT はつい数日前に Next から Remix に移行しました!

前の記事で詳しく説明したように、私が Remix を選択した理由は数多くありますが、その理由の 1 つは、そのシンプルさ、「フルスタック」の側面 (つまり、リミックス サーバーを「フロントエンドのバックエンド」として利用すること)、およびその優れた抽象化です。ルーティング、データフェッチ、ミューテーション。

幸いなことに、リミックスは配信されましたか?このフレームワークは直感的で、簡単に学習して他の人に教えることができ、ベスト プラクティスが確実に使用されるため、コードの作成とテストの両方が簡単になります。

Remix との連携を開始してから数か月後、彼らは React Router との正式な統合を発表しました。これにより、vite への移行と同様に、さらに多くの人が React Router を使用するように説得されることを期待しています。

私にとって、リミックスが正しい選択であることが多くの場面で明らかになりました。私が最近取り組んだ実際的な例を 1 つ挙げます。リミックス サーバーで単一のロガー インスタンスを使用して、アプリ全体のアクションとエラーをログに記録して追跡し、監視機能を強化します。実装は非常に簡単でした:

ステップ 1 - ロガーを作成します (私の場合は、監視に使用する Datadog とうまく連携する winston を使用しました)

ステップ 2 - ロガーをサーバーのロードコンテキストに追加します (私の場合は高速でした):

app.all(
  '*',
  createRequestHandler({
    getLoadContext: () => ({
      logger,
      // add any other context variables here
    }),
    mode: MODE,
    // ...
  }),
);

ステップ 3 (typescript ユーザー向け) - Remix のデフォルトの型定義を更新して、アプリの読み込みコンテキストにロガーを含めます

import '@remix-run/node';
import { type Logger } from 'winston';

declare module '@remix-run/node' {
  interface AppLoadContext {
    logger: Logger;
  }
}

ステップ 4 - ルートのローダーまたはアクションで必要に応じてロガーを使用します!

export async function action({ request, context }: ActionFunctionArgs) {

  try {
    await someAction();
  } catch (e) {
    context.logger.error(e);
  }
}

このセクションを終える前に、ストリーミング データ/コンポーネント用の RSC の実装や、認証に最適な Route ミドルウェアなど、Remix にあればよかったのにまだ実現していない機能があることをお伝えしたいと思います。 /認可。幸いなことに、これらの機能 (およびその他の優れた機能) はロードマップで優先されているようですので、すぐに入手できることを願っています!

From Zero to Launch: Key Takeaways from Our Remix-Powered Development Journey

タンスタッククエリ。永遠のお気に入り

ハイライト: クエリへの反応

過去の前向きな経験に基づいて、@tanstack/react-query を選択するのは私にとって簡単な決断でした。今回も期待を裏切りませんでした。この API は多用途で拡張可能で、最良の点で偏見にとらわれないため、他のツールとの統合が容易になります。

私はこれがとても気に入ったので、Apollo クライアントというより明白な選択肢ではなく、内部 API が GraphQL ベースであることを知ってこれを選択しました。その理由については多くの理由があります。 Tanstack Query には優れた API があり、Apollo よりも大幅に軽量です。また、必要になった場合に備えて、GraphQL のような特定のテクノロジーに合わせて大幅に調整されたツールに依存したくなかったからです。他のテクノロジーを切り替えるか、組み込んでください。

さらに、Remix を使用しているため、Tanstack Query の SSR 機能を最大限に活用できます。つまり、クライアント側でこれらのクエリを変更、無効化、または再フェッチする機能を維持しながら、サーバー側でクエリをプリフェッチできます。簡略化した例を次に示します:

import { dehydrate, QueryClient, HydrationBoundary, useQuery } from '@tanstack/react-query';
import { json, useLoaderData } from '@remix-run/react';


const someDataQuery = {
  queryKey: ['some-data'],
  queryFn: () => fetchSomeData()
}

export async function loader() {
  const queryClient = new QueryClient();
  try {
    await queryClient.fetchQuery(someDataQuery);

    return json({ dehydrate: dehydrate(queryClient) });
  } catch (e) {
    // decide whether to handle the error or continue to
    // render the page and retry the query in the client
  }
}

export default function MyRouteComponent() {
  const { dehydratedState } = useLoaderData<typeof loader>();
  const { data } = useQuery(someDataQuery);

  return (
           <HydrationBoundary state={dehydratedState}>
             <SomeComponent data={data} />
           </HydrationBoundary />
  );
}

From Zero to Launch: Key Takeaways from Our Remix-Powered Development Journey

Tailwind CSS

Highlight: Tailwind

I was initially skeptical about Tailwind, having never used it before, and because I didn’t quite understand the hype (it seemed to me at first just like syntactic sugar over CSS). However, I decided to give it a try because of its strong recommendations and popularity within the community, and I’m really glad I did. Tailwind’s utility-first approach made it incredibly easy to build a consistent and robust design system right from the start, which, looking back, was a total game changer.
It also pairs perfectly with shadcn, which we used, and together they allowed me to deliver quickly while keeping everything modular and easy to modify later on - a crucial advantage in a startup environment.

I also really like how easy it is to customize tailwind's theme to your needs - for example, overriding tailwind's default scheme:

First, define your colors as variable's under tailwind's main .css file:

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

@layer base {

  :root {
    /* define the primitive design system tokens */
    --colors-blue-100: hsl(188 76% 90%);
    --colors-blue-200: hsl(187 63% 82%);
    --colors-blue-25: hsl(185 100% 98%);
    --colors-blue-300: hsl(190 52% 74%);
    --colors-blue-400: hsl(190 52% 61%);
    --colors-blue-50: hsl(188 92% 95%);
    --colors-blue-500: hsl(190 74% 39%);
    --colors-blue-600: hsl(191 77% 34%);
    --colors-blue-700: hsl(190 51% 35%);
    --colors-blue-800: hsl(191 52% 29%);
    --colors-blue-900: hsl(190 51% 23%);
    --colors-blue-950: hsl(190 52% 17%);
    --colors-gray-100: hsl(0 0 90%);
    --colors-gray-200: hsl(0 0 85%);
    --colors-gray-25: hsl(0 0 98%);
    --colors-gray-300: hsl(0 0 73%);
    --colors-gray-400: hsl(0 1% 62%);
    --colors-gray-50: hsl(0 0 94%);
    --colors-gray-500: hsl(0 0% 53%);
    --colors-gray-600: hsl(0 0 44%);
    --colors-gray-700: hsl(0 0 36%);
    --colors-gray-800: hsl(0 2% 28%);
    --colors-gray-900: hsl(0 0 20%);
    --colors-gray-950: hsl(0 0 5%);
    --colors-red-100: hsl(4 93% 94%);
    --colors-red-200: hsl(3 96% 89%);
    --colors-red-25: hsl(12 100% 99%);
    --colors-red-300: hsl(4 96% 80%);
    --colors-red-400: hsl(4 92% 69%);
    --colors-red-50: hsl(5 86% 97%);
    --colors-red-500: hsl(4 88% 61%);
    --colors-red-600: hsl(4 74% 49%);
    --colors-red-700: hsl(4 76% 40%);
    --colors-red-800: hsl(4 72% 33%);
    --colors-red-900: hsl(8 65% 29%);
    --colors-red-950: hsl(8 75% 19%);

    /*
      ...
    */

    /* define the semantic design system tokens */

    --primary-light: var(--colors-blue-200);
    --primary: var(--colors-blue-600);
    --primary-dark: var(--colors-blue-800);
    --primary-hover: var(--colors-blue-50);

    --text-default-primary: var(--colors-gray-700);
    --text-default-secondary: var(--colors-gray-800);
    --text-default-tertiary: var(--colors-gray-900);
    --text-default-disabled: var(--colors-gray-300);
    --text-default-read-only: var(--colors-gray-400);

    --disabled: var(--colors-gray-300);
    --tertiary: var(--colors-gray-50);

    /*
      ...
    */
  }
}

Then, extend Tailwind's default theme via the tailwind config file:

import { type Config } from 'tailwindcss';

const ColorTokens = {
  BLUE: 'blue',
  GRAY: 'gray',
  RED: 'red',
} as const;

const generateColorScale = (colorName: string) => {
  const scales = [25, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950];
  return scales.reduce(
    (acc, scale) => {
      acc[scale] = `var(--colors-${colorName}-${scale})`;
      return acc;
    },
    {} as Record<string, string>,
  );
};

export const customColors = Object.values(ColorTokens).reduce((acc, color) => {
  return {
    ...acc,
    [color]: generateColorScale(color),
  };
}, {});

const config = {
  // ... additional config
  theme: {
    extend: {
      colors: customColors
    },
  },
} satisfies Config;

export default config;

This is just the tip of the iceberg - you can go on to define custom spacing, text sizing and much more!

From Zero to Launch: Key Takeaways from Our Remix-Powered Development Journey

Playwright - makes writing e2e tests fun

Highlight: Playwright

Previously using Cypress, I was inclined to choose it, but I kept hearing hype around Playwright and figured I'll research it extensively before making a decision. After comparing Playwright with Cypress, it was clear Playwright is the right choice to make - the fact it comes with parallel execution out of the box, the broader browser support, running times and debugging capabilities - all made Playwright the obvious choice.
And, while this is very subjective, I like Playwright's syntax much better. I find it similar to React Testing Library's syntax, which I like, and I tend to think the tests are a lot more readable, with the asynchronous aspect of the tests being very straight forward, unlike the syntax of Cypress that can cause tests to feel bloated by .then() statements and subsequent indentations.

I think my favorite feature of Playwright is their implementation of Test Fixtures. They provide a clean way to initialize and reuse resources like page objects, making tests more modular and maintainable. Make sure to check out the above link to learn more about it!

From Zero to Launch: Key Takeaways from Our Remix-Powered Development Journey

Tanstack Table vs AG Grid

Lowlight: (Starting with) Tanstack Table

First off, let me clarify — @tanstack/react-table is a fantastic tool, which is why I was inclined to choose it in the first place, but it wasn’t the best fit for my particular use case. The very features that make it great, like its small bundle size and customizable API, ended up being less relevant to our needs than I originally thought. Despite having full control of the rendering of the Table, I was having some issues aligning its scrolling behavior to our desired outcome (why is it still not possible in 2024 to have a

element with dynamic sizing and scrolling on its body only, without resorting to clunky solutions? ?).

I soon realized that to deliver my feature fast and provide a good user experience, I needed a table with built-in features like pagination, column resizing and row auto-sizing, and I preferred having those out of the box over full control of the UI rendering. Additionally, since the table only appears after a query is run, I could lazy load it, making the bundle size less of a concern.

I highly recommend using the AG Grid theme builder to customize AG Grid according to your preferences/design system. And, for those using Cypress for their testing purposes - I found this cool plugin that abstracts AG Grid to easily interact with it in tests (sadly I could not find the same for Playwright ?)

Final thoughts

Looking back, I definitely feel a sense of pride in what we’ve accomplished. Not every decision was perfect, but taking the time to research and find the most fitting solution was worth it. And when things didn’t go as planned - it challenged us to think critically and adapt quickly, which is important no less.

Please let me know in the comments if there’s something you’d like to see explored further in future articles.
Here’s to more lessons learned, personal growth and having fun along the way ?

以上がゼロから立ち上げまで: リミックスを活用した開発の取り組みから得た重要なポイントの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。