A típusok hatékony megosztása a frontend és a háttéralkalmazások között nagyon szükséges megoldás. Kiküszöböli a hibákat és a töréseket, felgyorsítja az integrációt, és biztosítja, hogy az API-k hatékonyan kommunikáljanak, még akkor is, ha Ön tovább fejlődik.

"Néz":

A típusok azonban jellemzően kódbázishoz kapcsolódnak. Ez azt jelenti, hogy korábban szinte lehetetlen volt hatékonyan megosztani és fenntartani a típusokat a kódbázisok között.

Ebben az oktatóanyagban teljesen megoldja a problémát azáltal, hogy „entitás-összetevőket” hoz létre és dolgozik velük, hogy megosszák a típusokat, és könnyen biztosítsák, hogy a háttér és az előtér mindig törés nélkül kommunikálhasson egymással.

Valójában az entitáskomponensek „sokkal többre képesek” – például segíthetnek felgyorsítani az integrációt. Az egyszerű típusmegosztás csak egy előny.

Ez az oktatóanyag Nitsan Cohen „e nagyszerű példáján” alapul, és bemutatja a Nitsan oktatóanyagában létrehozott kódot és lépéseket. Ezt a bejegyzést további gondozással vagy az eredetivel követheti, ahogyan szeretné.

Megosztunk néhány típust!

Megosztási típusok: Valódi használati eset

Nézzünk meg egy valós példát a megosztási típusokra.

Képzeld el, hogy anya vagy apa a gyerekekkel. Ha olyan vagy, mint a Nitsan, akkor pontosan 5 van belőle.

Minden gyermeknek szüksége van egy bizonyos időre, amelyet a házi feladat elvégzésére fordít. Mindegyikük mást vár. Ennek ellenére naponta csak egy egységnyi időt (pl. egy órát) kell felosztania közöttük.

Gyermekei – a „kliensek” – azt várják a típusdefiníciótól (a példában apát használva, de kérjük, használjon anyát vagy bármit, ami neked működik):

export type Dad = {
  name: string;
  age: number;
  children: number;
  spendWithChild?: (childAge: number) => number;
};

A szolgáltatás (szülő) azonban csak az ilyen típusú adatokat használja kimenetként:

type Dad = {
  name: string;
  age: number;
  children: number;
  spendWithChild: 20; // 20 minutes a day
};

Ó, oh… baj van!

Az Ön gyermekei életkoruk alapján exponenciális számot várnak, míg én gyerekenként 20 perces állandó időt adtam vissza.

Végül a szerződései megegyeznek, és Ön megegyezett, hogy ugyanazt a típust osztja meg:

export type Dad = {
  name: string;
  age: number;
  children: number;
  spendWithChild: (children: number) => 20; // Should always return 20 minutes per child :)
};

Megosztunk néhány típust!

Ideje megnézni, hogyan kezdje meg a típusok megosztását.

A kulcs az „entitáskomponensek” létrehozása – egy olyan komponens, amely az adatokat is képes manipulálni (ES6 osztályok használatával). Ennek eredményeként szabványosított módot kap a kommunikációra és az objektum manipulálására, és még a kód tisztán tartására is.

Ez egy nagyon gyors és hatékony módja ennek. Később pedig, amikor változás következik be, könnyen kezelheti azt, ellenőrizheti a verziókat, és gondoskodhat arról, hogy semmi se törjön meg.

Az ilyen komponensek létrehozásához a „Bit” nyílt forráskódú eszközt használjuk, amely megkönnyíti, hogy bármit moduláris, független és változatos komponensként készítsen.

Munkaterület beállítása

Ez az útmutató feltételezi, hogy rendelkezik "bit.cloud fiókkal", és tudja, hogyan kell megnyitni egy "távirányítót". Ha nem, szánjon rá fél percet (és ingyenes).

Ezután telepítse a Bit-et:

npx @teambit/bvm install

Ezután hozzon létre egy „Bit Workspace”-t. Itt építheti, állíthatja össze és kezelheti az összetevőket. Most nem merülünk bele, de hamarosan érezni fogod az élményt.

Nem mintha a Workspace nem „tárol” összetevőket – semmilyen módon nem kapcsolódnak hozzá.

$ bit init

A munkaterület gyökerében két fájl és egy könyvtár jön létre:

  • workspace.jsonc: Ez a fájl tartalmazza a munkaterület konfigurációját.
  • .bitmap: A Here Bit nyomon követi, hogy mely összetevők vannak a munkaterületen. Ezt a fájlt nem szabad közvetlenül szerkeszteni.
  • .bit: Itt tárolódnak az összetevő objektumok.

Így néz ki a munkaterület:

  • Kérjük, cserélje ki a "defaultScope": "[your-bit-cloud-username].[scope-name]" értéket a Bit Cloud-felhasználónevére és a hatókör nevére.

A frontend és a backend komponensek fejlesztése

Kezdje a háttérösszetevő létrehozásával a create paranccsal, amely az összetevősablonokból állít elő összetevőket. A komponenssablonok alapkódot és mappastruktúrát adnak az új komponensekhez.:

$ bit create express-app backend/server

Ezt az összetevőt hozzá kell adnunk a workspace.json fájlunkhoz, mivel ez "alkalmazástípus". Az összetevők alkalmazástípusok segítségével építhetők, szolgálhatók ki és telepíthetők. Minden alkalmazástípushoz (Node, React stb.) létezik összeépítési stratégia a fejlesztéshez és a termeléshez, valamint minden alkalmazástípushoz egy telepítési megoldás.

Adja hozzá ezt a sort a workspace.json fájl legfelső szintjéhez. Ez az összetevő bővítményként hozzáadódik a munkaterületéhez.

{
...
"nitsan770.shared-types/backend/server": {},
...
}

Ne felejtse el lecserélni a Nitsan felhasználói és hatókörneveit a sajátjára.

A bit status egy hasznos parancs, amely megmondja, hogy mi a munkaterület összetevőinek állapota. Futtassuk le, hátha minden rendben van:

$ bit status

Íme a kimenet:

new components
(use "bit tag --all [version]" to lock a version with all your changes)
     > backend/server ...  issues found
       missing packages or links from node_modules to the source (run "bit install" to fix both issues. if it's an external package, make sure it's added as a package dependency):
          route.ts -> express (missing package)
          server.app-root.ts-> express (missing package)

Ahogy tudod, hiányzik a express csomag . A következő paranccsal telepítheti a munkaterületre. A CORS telepítésével engedélyezheti a szerverén.

$ bit install express cors

Módosítsuk komponensünk mock-route fájlját:

import type { Route } from './route';
type Dad = {
  name: string;
  age: number;
  children: number;
  spendWithChild: 20;
};
export function getDaddyRoute(): Route {
  const dad: Dad = {
    name: 'Nitsan Cohen',
    age: 33,
    children: 5,
    spendWithChild: 20,
  };
  return {
    method: 'get',
    route: '/spend-time-with-daddy',
    middlewares: [async (req, res) => res.send(dad)],
  };
}

És add hozzá a „CORS” fejlécet „a válaszhoz”:

import Application from 'express';
import cors from 'cors';
import { getPort } from './get-port';
import { getDadRoute } from './mock-route';
export const expressApp = async () => {
  const app = Application();
  app.use(cors());
  const port = await getPort();
  registerRoutes(app);
  app.listen(port, () => {
    console.log(`server app listening on port ${port}`);
  });
};
function registerRoutes(app: Application.Application) {
  const mockRoute = getDadRoute();
  const routes = [mockRoute];
  routes.forEach((route) => {
    app[route.method](route.route, route.middlewares);
  });
}
expressApp();

Ideje futtatni szerveroldali komponensünket! Futtassa a következő parancsot:

$ bit run server

A következő kimenetet kell látnia a terminálon:

server app listening on port 3000
server app is running on http://localhost:3000

Ha megnyitja böngészőjét, és felkeresi a http://localhost:3000/spend-time-with-daddy oldalt, a következő kimenetet kell látnia:

{
  "name": "Nitsan Cohen",
  "age": 33,
  "children": 5,
  "spendWithChildren": 20
}

Most tegyük ugyanezt a frontend összetevővel. Először hozza létre a react-app sablonból:

$ bit create react-app frontend/fetch-dad

Ezután adja hozzá a workspace.json fájlunkhoz:

{
...
 "nitsan770.shared-types/backend/server": {},
 "nitsan770.shared-types/frontend/fetch-daddy": {},
...
}

Most módosítsuk a app.tsx fájlt, hogy lekérjük az apa adatait a futó szerverünkről:

import React, { useEffect } from 'react';
type Dad = {
  name: string;
  age: number;
  children: number;
  spendWithChild?: (childAge: number) => number;
};
export function FetchDadApp() {
  const [dad, setDad] = React.useState<Dad | null>(null);
  useEffect(() => {
    fetch('http://localhost:3001/spend-time-with-daddy')
      .then((res) => res.json())
      .then((data) => setDad(data));
  }, []);
  return (
    <>
      <div>
        The time you have with your Dad is:
        {dad?.spendWithChild(7)} minutes.
      </div>
    </>
  );
}

Következő futtassa a frontend összetevő alkalmazást! Futtassa a következő parancsot:

$ bit run fetch-Dad

A következő hibaüzenet jelenik meg, amikor megnyitja az alkalmazást a böngészőben:

Pontosan ezt mondtam a kisgyermekemnek: Dad.spendWithChild nem függvény!

Egy szó szerinti apa vicc nem jön be gyakran, szánjon egy percet, hogy értékelje.

A háttérben a spendWithChild tulajdonság egy számra van állítva. A frontend alkalmazásban a spendWithChild függvénynek számít.

Nyilvánvalóan hibaüzenetet kap, ha egész számot próbál meghívni.

Nos, már tudod, mi a megoldás. Megosztott entitás (típus) komponens!

A megosztott entitás (típus) összetevő létrehozása

Először hozzá kell adnia a teambit.community/envs/community-react-et a munkaterület bővítményeként, mivel olyan komponenssablont fog használni, amely nem része a Bit alapvető ökoszisztémájának (btw, hozzáadhatja "saját összetevősablonjait").

Adja hozzá a következő sort a workspace.json fájlhoz:

{
  "nitsan770.shared-types/backend/server": {},
  "nitsan770.shared-types/frontend/fetch-dad": {},
  "teambit.community/envs/[email protected]": {},
...
}

Most futtassa a következő parancsokat a bővítmény telepítéséhez:

bit install && bit compile

Ha futtatja a bit templates alkalmazást, látni fogja, hogy a my-entity sablon is megjelenik (jobbra lent):

The following template(s) are available with the command bit create:
Example - bit create <template-name> <component-name>
teambit.generator/generator
    component-generator (create your own component generator
Docs: https://bit.dev/docs/dev-services-overview/generator/generate-component)
    workspace-generator (create your own workspace generator -
Docs: https://bit.dev/docs/dev-services-overview/generator/generate-workspace)
teambit.pipelines/builder
    build-task (create a custom build task for your component pipelines)
teambit.react/react
    react (a basic react component)
    react-context (a react context component)
    react-hook (a react hook component)
    react-js (a basic react component in js)
    react-env (customize the base React env with your configs and tools)
teambit.harmony/aspect
    aspect (extend Bit capabilities)
teambit.html/html
    html-env (customize the base Html env with your configs and tools)
    html (a basic html component)
teambit.harmony/node
    node-env (customize the base Node env with your configs and tools)
    node (a Node.js module)
    express-app (a bit express application)
    express-route (an express route)
teambit.react/react-native
    react-native-env (customize the base React Native env with your configs and tools)
    react-native (a basic react native component)
teambit.mdx/mdx
    mdx (MDX-file compiled by Bit to a reuseable component)
teambit.community/envs/[email protected]
    my-react (react components with figma embed and scss)
    my-entity (entity component)  <-- this is the one we are looking for

Hozzuk létre a megosztott entitás (típus) összetevőt:

$ bit creat emy-entity entities/dad

Erre az összetevőre mint entitásra hivatkozunk, nem típusra. A fő különbség az, hogy egy olyan komponenst hozunk létre, amely az adatokat is képes manipulálni (ES6 osztályok használatával). Ennek eredményeként szabványosított módunk van az objektum manipulálására, és tisztább elő- és háttérkódot kapunk.

Ezenkívül, ha valakinek manipulálnia kell az adatokat, frissítheti az entitáskomponenst, és bárki más, akinek szüksége van rá, szintén előnyös lesz (mivel nem kell megismételnie a logikát).

Nézzük meg az „entitáskomponenst”:

export type DadProps = {
  name: string;
  age: number;
  children: number;
  readonly spendWithChild?: number;
};
export class Dad implements DadProps {
  constructor(
    readonly name: DadProps['name'],
    readonly age: DadProps['age'],
    readonly children: DadProps['children']
  ) {}
  get spendWithChild() {
    return 100 / this.children;
  }
  static fromObject(plainDad: DadProps) {
    return new Dad(plainDad.name, plainDad.age, plainDad.children);
  }
  toObject() {
    return {
      name: this.name,
      age: this.age,
      children: this.children,
    };
  }
}

A spendWithChild most egy getter módszer, amely visszaadja, hogy apa hány percet tölt a gyerekeivel.

Ezután használhatjuk a dad.spendWithChild-t a frontend komponensünkben, és élvezhetjük a "sokkal tisztább kódbázist":

import React, { useEffect } from 'react';
import { Dad } from '@nitsan770/shared-types.entities.dad';
export function FetchDadApp() {
  const [dad, setDad] = React.useState<Dad | null>(null);
  useEffect(() => {
    fetch('http://localhost:3001/spend-time-with-daddy')
      .then((res) => res.json())
      .then((data) => setDad(data));
  }, []);
  return (
    <>
      <div>
        The time you have with your Dad is:
        {dad?.spendWithChild} minutes
      </div>
    </>
  );
}

Ezenkívül hozzá kell adnunk az új entitást a háttérrendszerünkhöz:

import type { Route } from './route';
import { Dad } from '@nitsan770/shared-types.entities.Dad';
export function getDadRoute(): Route {
  const dad: Dad = Dad.fromObject({
    name: 'Nitsan Cohen',
    age: 33,
    children: 5,
  });
  return {
    method: 'get',
    route: '/spend-time-with-daddy',
    middlewares: [async (req, res) => res.send(dad)],
  };
}

Kigúnyolta apát

Most, hogy van egy megosztott entitáskomponensünk a frontend és a háttérrendszer számára, kigúnyolhatjuk.

Van néhány előnye annak, ha a makett az entitáskomponensben szerepel:

  • A fejlesztőknek többé nem szükséges kigúnyolniuk teszt-/megjelenítési adataikat, amikor funkciókat építenek ezek köré az entitások köré.
  • A mockok lehetővé teszik tesztek hozzáadását az entitáskomponensekhez, így az API-módosítások könnyebben láthatók lesznek. Nem kell arra várni, hogy valami eltörjön a többi csapatban, hogy rájöjjünk, mi hibázott.

Az entitáskomponens gúnyolásának módja a következő:

import { DadProps } from './Dad';
export const mockDad: DadProps = {
  name: 'Nitsan Cohen',
  age: 33,
  children: 5,
};

Adjunk hozzá egy tesztet az entitáskomponenshez:

import { Dad } from './Dad';
import { mockDad } from './Dad.mock';
describe('Dad', () => {
  it('should not spend more than 20 minutes per child', () => {
    const Dad = Dad.fromObject(mockDad);
    expect(Dad.spendWithChildren).toBeLessThanOrEqual(60);
  });
});

Ha futtatja a tesztet, látni fogja, hogy az entitáskomponens működik:

$ bit test

Sikerült a teszt:

PASS  shared-types/entities/Dad/Dad.spec.ts
  Dad
    ✓ should not spend more than 60 minutes with the children (1 ms)
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.598 s, estimated 1 s
Ran all test suites.

Ily módon, ha valami elromlik, nem adjuk ki, vagy legalább értesíteni tudjuk az entitás fogyasztót a megfelelő félverzióval.

Egy másik jelentős előny a modell exportálása és a háttér- és előtér-tesztekben való felhasználása. Ennek eredményeként a többi csapat fejlesztői sok időt takarítanak meg.

Összetevők címkézése és exportálása

Most, hogy kifejlesztette az összetevőket, itt az ideje, hogy megcímkézze és kiadja őket a közösségnek (vagy csak a szervezetének, ha akarja).

A címkézési folyamat során az összetevő a következő lépéseken megy keresztül:

  • A bit zárolja az összetevő aktuális állapotát (mint a git commit, de a tárolók helyett az összetevők esetében).
  • Az összetevőtesztek lefutnak, és látni fogja az eredményeket.
  • A komponensét úgy építjük és csomagoljuk, hogy bárki telepíthesse projektjeibe.
  • Az alkalmazástípusú összetevőkben az összetevő ugyancsak telepítésre kerül, ahol meghatározza.

Vegye figyelembe, hogy ezek a fázisok teljesen testreszabhatók, és bármilyen lépést hozzáadhat.

$ bit tag — message first release

A következő kimenetet fogja látni:

new components
(first version for components)
> backend/[email protected]
> entities/[email protected]
> frontend/[email protected]

A következő lépés az alkatrészek „exportálása”. Itt exportáljuk a "bit.cloud"-ba. Ingyenes és egyszerű. Ha akarja, exportálhatja őket „saját szerverére”.

$ bit export

Siker!

exported the following 3 component(s):
nitsan770.shared-types/backend/server
nitsan770.shared-types/entities/dad
nitsan770.shared-types/frontend/fetch-Dad

Az összetevők most a bit.cloud-on vannak. Telepítheti őket bármelyik csomagkezelővel, vagy megoszthatja őket csapatával.

Függőségek kezelése

Az ilyen típusú Bit használatával való megosztás igazi szépsége a függőségek egyszerű kezelésében rejlik.

Képzeld el, hogy később új gyermekkel bővült a család. Az összeg 20 perces tartása érdekében frissíteni szeretnénk a spendWithChild getter módszert.

export type DadProps = {
  name: string;
  age: number;
  children: number;
  readonly spendWithChild?: number;
};
export class Dad implements DadProps {
  constructor(
    readonly name: DadProps['name'],
    readonly age: DadProps['age'],
    readonly children: DadProps['children']
  ) {}
  get spendWithChild() {
    return 120 / this.children; // 20 minutes per child
  }
  static fromObject(plainDad: DadProps) {
    return new Dad(plainDad.name, plainDad.age, plainDad.children);
  }
  toObject() {
    return {
      name: this.name,
      age: this.age,
      children: this.children,
    };
  }
}

Jelöljük meg az összetevőt egy új verzióval:

$ bit tag — message refactor spendWithChildren

És itt van a címke kimenete:

changed components
(components that got a version bump)
     > nitsan770.shared-types/entities/[email protected]
       auto-tagged dependents:
            nitsan770.shared-types/backend/[email protected]
            nitsan770.shared-types/frontend/[email protected]

Nem változtattunk semmit sem a frontenden, sem a háttérben. Hogyan címkézték meg őket? Az összetevők automatikusan meg lettek címkézve, ha jobban megnézi. A Bit észlelte, hogy ezek a komponensek az entitáskomponenst függőségként használják, és mivel módosították és megcímkézték, a függőek is.

Vegye figyelembe, hogy az automatikus címkézési folyamat csak akkor működik, ha minden összetevő ugyanabban a munkaterületen található. De ne aggódj. Hamarosan elindítjuk a Ripple-t. Amikor exportál egy összetevőt a bit.cloudba, a Ripple automatikusan észleli, hogy mely összetevők használják, és frissíti a verzióikat (és értesíti, ha valami hiba történt).

Lehetséges lenne a frissítéseket automatikusra állítani, ha kisebb frissítésről van szó, és kézire, ha törő változásról van szó. Ennek eredményeként mindig Ön irányítja az alkatrészeket, és biztosíthatja azok megfelelő működését.

Összegzés

Láttuk, milyen egyszerű megosztott entitáskomponenst készíteni, amely bármilyen projektben használható. Ha megosztott entitáskomponenst használ a frontend és a háttérrendszer között, az számos jelentős előnnyel jár:

  • Független, és saját kiadási ciklusa van.
  • Az integráció megkönnyítése a fejlesztők számára.
  • Az igazság egyetlen forrása arról, hogy mi is az entitás.
  • Tervezési időben történő integráció helyi visszajelzésekkel az IDE-ben az integráció során.
  • Frissítések a komponensbe foglalt entitás változásairól.
  • A fogyasztók tesztelhetik a hamis adatokkal szemben.
  • Típusokat és interfészeket biztosít a távoli API-khoz.
  • Tesztek, dokumentációk, használati példák.

Tudj meg többet