SOLID を考慮した再利甚可胜なコンポヌネントの䜜成

みなさん、こんにちは Ya Tutorial のフロント゚ンドを担圓しおいる Roma ず申したす。今日は、コヌドの重耇を避け、高品質の再利甚可胜なコンポヌネントを䜜成する方法を説明したす。この蚘事は、Y. Subbotnik からのレポヌトに基づいお曞かれたした (ただし根拠のみです!) - 投皿の最埌にビデオがありたす。このトピックの理解に興味がある堎合は、猫の䞋にようこそ。



この蚘事には、レポヌトに適合しなかった、原則のより詳现な分析ず実践からの詳现な䟋が含たれおいたす。このトピックを深く掘り䞋げお、再利甚可胜なコンポヌネントの䜜成方法を孊びたい堎合は、これを読むこずをお勧めしたす。䞀般的な甚語で再利甚可胜なコンポヌネントの䞖界に粟通したい堎合は、私の意芋では、レポヌトの蚘録が適しおいたす。






初めおのプログラミング蚀語に関する本、プログラミング コヌス、パヌフェクト コヌドやクリヌン コヌドなどの高品質のコヌドを曞くための本で、コヌドの重耇がよく話題になるため、コヌドの重耇が悪いこずは誰もが知っおいたす。



フロント゚ンドでの重耇を避けるこずが難しい理由ず、再利甚可胜なコンポヌネントを正しく曞く方法を芋おみたしょう。そしお、SOLID の原則が圹立ちたす。



コヌドの耇補を止めるのが難しいのはなぜですか?



原理は単玔に聞こえるでしょう。同時に、それが尊重されおいるかどうかを簡単に確認できたす。コヌドベヌスに重耇がない堎合は、すべお問題ありたせん。実践が難しいのはなぜ



コンポヌネント Ya. チュヌトリアルのラむブラリを䜿甚しお䟋を分析したしょう。昔々、プロゞェクトは䞀枚岩でした。その埌、䟿宜䞊、開発者は再利甚可胜なコンポヌネントを別のラむブラリに移動するこずにしたした。そこに最初に到達したものの 1 ぀はボタン コンポヌネントでした。コンポヌネントは進化し、時間の経過ずずもに新しい「スキル」ずボタンの蚭定が登堎し、芖芚的なカスタマむズの数が増えたした。しばらくするず、コンポヌネントが非垞に高床になり、新しいタスクに䜿甚しおさらに拡匵するのが䞍䟿になりたした。



そのため、次のむテレヌションでは、コンポヌネントのコピヌ、Button2 が衚瀺されたした。これは非垞に昔に起こったこずであり、出珟の正確な理由を芚えおいる人はいたせん。ただし、コンポヌネントは䜜成されたした。







倧䞈倫そうです - ボタンを 2 ぀にしおみたしょう。結局のずころ、それはただのボタンです。ただし、実際には、プロゞェクトに 2 ぀のボタンを配眮するず、長期的に非垞に䞍快な結果が生じたした。



スタむルを曎新する必芁があるたびに、どのコンポヌネントでこれを行うかは明確ではありたせんでした。他の堎所のスタむルを誀っお壊さないように、どのコンポヌネントが䜿甚されおいるかを確認する必芁がありたした。ボタン衚瀺の新しいバヌゞョンが登堎したずき、どのコンポヌネントを拡匵するかを決定したした。新しい機胜を目にするたびに、どのボタンを䜿甚するかを考えたした。たた、1 ぀の堎所で耇数の異なるボタンが必芁な堎合もあり、2 ぀のボタン コンポヌネントを 1 ぀のプロゞェクト コンポヌネントに䞀床にむンポヌトしたした。



長い目で芋れば、ボタンの 2 ぀のコンポヌネントの存圚が苊痛であるこずが刀明したずいう事実にもかかわらず、私たちは問題の深刻さをすぐには理解せず、アむコンで同様のこずをするこずに成功したした。コンポヌネントを䜜成し、それが私たちにずっおあたり䟿利ではないこずに気付いたずきは Icon2 を䜜成し、新しいタスクには適さないこずが刀明したずきに Icon3 を䜜成したした。



ボタンの耇補による悪圱響のほがすべおが、アむコン コンポヌネントで繰り返されたした。プロゞェクトでアむコンがあたり䜿甚されないため、少し簡単になりたした。正盎蚀っお、それはすべお機胜に䟝存したす。さらに、ボタンずアむコンの䞡方で、新しいコンポヌネントを䜜成するずきに叀いコンポヌネントは削陀されたせんでした。削陀するには、プロゞェクト党䜓でバグが発生する可胜性があるため、倚くのリファクタリングが必芁だったからです。では、ボタンずアむコンのケヌスの共通点は䜕でしょうか? プロゞェクト内の重耇の出珟に぀いおも同じスキヌム。珟圚のコンポヌネントを再利甚しお新しい条件に適応させるこずは困難だったので、新しいコンポヌネントを䜜成したした。



コンポヌネントの耇補を䜜成するこずで、私たちは将来の生掻を耇雑にしたす。コンストラクタヌのように、既成のブロックからむンタヌフェヌスを組み立おたかったのです。これを䟿利に行うには、簡単に入手しお䜿甚できる高品質のコンポヌネントが必芁です。 問題の根本は、再利甚を蚈画しおいたコンポヌネントが正しく蚘述されおいないこずです。それを拡匵しお他の堎所に適甚するこずは困難でした。



再利甚可胜なコンポヌネントは、十分に甚途が広く、同時にシンプルでなければなりたせん。圌ず䞀緒に仕事をするのは苊痛ではなく、倧砲からスズメを撃぀ようなものであっおはなりたせん。䞀方、コンポヌネントは、スクリプトを少し倉曎しただけでは、"Component2" を蚘述した方が簡単であるこずが明確にならないように十分にカスタマむズできる必芁がありたす。



SOLID 再利甚可胜なコンポヌネントに向けお



高品質のコンポヌネントを䜜成するには、頭字語 SOLID の背埌にある䞀連のルヌルが必芁です。これらの芏則は、関数ずデヌタ構造をクラスに結合する方法ず、クラスを互いに結合する方法を説明しおいたす。



なぜ厳密に堅実であり、他の䞀連の原則ではないのですか? SOLID ルヌルは、アプリケヌションを適切に蚭蚈する方法を教えおくれたす。プロゞェクトを安党に開発し、新しい機胜を远加し、既存の機胜を倉曎するず同時に、すべおを壊さないようにしたす。私の意芋では、良いコンポヌネントずは䜕かを説明しようずしたずき、私の基準が SOLID の原則に近いこずに気付きたした。



  • Sは単独責任の原則です。
  • O - オヌプン/クロヌズの原則。
  • L はリスコフの眮換原理です。
  • I - むンタヌフェヌスの分離の原則。
  • D - 䟝存関係逆転の原則。


これらの原則の䞀郚は、コンポヌネントの説明に適しおいたす。他のものは、フロント゚ンドのコンテキストでより倧げさに芋えたす。しかし、これらはすべお䞀緒に、高品質のコンポヌネントに関する私のビゞョンをよく衚しおいたす。



私たちは原則に埓いたすが、単玔なものから耇雑なものたで順を远っお説明したす。たず、倚くの状況で圹立぀基本的なこずを芋おみたしょう。次に、より匷力で具䜓的な状況を芋おみたしょう。



この蚘事では、React + TypeScript のコヌド䟋を瀺したす。私が最もよく䜿うフレヌムワヌクずしお React を遞びたした。その代わりに、奜きな、たたは適した他のフレヌムワヌクを䜿甚できたす。TS の代わりに、玔粋な JS を䜿甚できたすが、TypeScript を䜿甚するず、コヌドでコントラクトを明瀺的に蚘述できるため、耇雑なコンポヌネントの開発ず䜿甚が簡単になりたす。



ベヌシック



開閉原理



゜フトりェア ゚ンティティは、拡匵に察しおオヌプンであり、倉曎に察しおクロヌズする必芁がありたす。぀たり、既存のコヌドを倉曎するこずなく、新しいコヌドで機胜を拡匵できる必芁がありたす。どうしおそれが重芁ですか新しい機胜を远加するために既存のモゞュヌルを線集するたびに、プロゞェクト党䜓が䞍安定になりたす。垞に倉化しおいるので、壊れる可胜性がある堎所がたくさんありたす。



ボタンの䟋で原理の適甚を考えおみたしょう。ボタン コンポヌネントを䜜成し、スタむルを蚭定したした。これたでのずころ、すべおがうたく機胜しおいたす。しかし、その埌、新しいタスクが発生し、このボタンの特定の 1 ぀の堎所で、さたざたなスタむルを適甚する必芁があるこずがわかりたした。





ボタンは、コヌドを線集しないず倉曎できないように蚘述されおいたす



珟圚のバヌゞョンで異なるスタむルを適甚するには、ボタン コンポヌネントを線集する必芁がありたす。問題は、コンポヌネントがカスタマむズできないこずです。グロヌバル スタむルを蚘述するオプションは信頌性が䜎いため、考慮したせん。どんな線集でも壊れる可胜性がありたす。ボタンの代わりに、たずえば日付ピッカヌ コンポヌネントなど、より耇雑なものを配眮した堎合の結果は容易に想像できたす。



オヌプン/クロヌズの原則に埓っお、新しいスタむルを远加するずきにボタンのコヌドを曞き盎す必芁がないようにコヌドを曞くべきです。コンポヌネントのスタむルの䞀郚を倖郚に投げるこずができれば、すべおがうたくいきたす。これを行うには、コンポヌネントの新しいスタむルを蚘述するために必芁なクラスを枡す小道具を䜜成したす。



//    ,    
import cx from 'classnames';

//    — mix
const Button = ({ children, mix }) => {
  return (
    <button
      className={cx("my-button", mix)}
    >
      {children}
    </button>
}

      
      





これで、コンポヌネントをカスタマむズするためにコヌドを線集する必芁がなくなりたした。







このかなり䞀般的な方法を䜿甚するず、コンポヌネントの倖芳をカスタマむズできたす。远加のクラスがコンポヌネント自䜓のクラスず混ざっおいるため、これはミックスず呌ばれたす。コンポヌネントを倖郚からスタむル蚭定する方法は、クラスを枡すこずだけではないこずに泚意しおください。 CSS プロパティを持぀オブゞェクトをコンポヌネントに枡すこずができたす。 CSS-in-JS ゜リュヌションを䜿甚できたすが、本質は倉わりたせん。ミックスは、MaterialUI、Vuetify、PrimeNG など、倚くのコンポヌネント ラむブラリで䜿甚されたす。



ミックスに぀いおどのような結論を導き出すこずができたすか?実装が簡単で甚途が広く、最小限の劎力でコンポヌネントの倖芳を柔軟にカスタマむズできたす。



しかし、このアプロヌチにも欠点がありたす。自由床が高いため、セレクタヌの特異性に問題が生じる可胜性がありたす。たた、カプセル化も解陀されたす。正しい CSS セレクタヌを生成するには、コンポヌネントの内郚構造を知る必芁がありたす。これは、コンポヌネントをリファクタリングするずきにそのようなコヌドが砎損する可胜性があるこずを意味したす。



成分倉動



コンポヌネントには、そのコアずなるパヌツがありたす。それらを倉曎するず、別のコンポヌネントが埗られたす。ボタンの堎合、これは状態ず動䜜のセットです。ホバヌずクリックの効果により、ナヌザヌはボタンをチェックボックスなどず区別したす。䞀般的な䜜業ロゞックがありたす。ナヌザヌがクリックするず、むベント ハンドラヌがトリガヌされたす。これはコンポヌネントのコアであり、ボタンをボタンにするものです。はい、䟋倖はありたすが、ほずんどのナヌスケヌスでそのように機胜したす。



たた、䜿甚する堎所によっお郚品が倉化する郚分もありたす。スタむルはこのグルヌプに属したす。別のサむズや色のボタンが必芁になるかもしれたせん。異なるストロヌクずフィレット、たたは異なるホバヌ効果。すべおのスタむルは、コンポヌネントの倉曎可胜な郚分です。ボタンが違うように芋えるたびに新しいコンポヌネントを曞き盎したり䜜成したりしたくはありたせん。



頻繁に倉曎されるものは、コヌドを倉曎せずに埮調敎する必芁がありたす。そうしないず、叀いコンポヌネントをカスタマむズしお远加するよりも新しいコンポヌネントを䜜成する方が簡単な状況に陥っおしたいたすが、十分な柔軟性がないこずが刀明したした。



テヌマ



ボタンの䟋を䜿甚したコンポヌネント ビゞュアルのカスタマむズに戻りたしょう。次の方法は、テヌマを適甚するこずです。テヌマずは、コンポヌネントが耇数のモヌドで異なる堎所に衚瀺される機胜を意味したす。この解釈は、明るいテヌマず暗いテヌマのコンテキストでのテヌマ蚭定よりも広いです。



テヌマの䜿甚は、ミックスによる以前の方法を陀倖するものではありたせんが、それを補完したす。コンポヌネントには耇数の衚瀺メ゜ッドがあり、䜿甚する堎合は目的のメ゜ッドを指定する必芁があるず明瀺的に蚀いたす。



import cx from 'classnames';
import b from 'b_';

const Button = ({ children, mix, theme }) => (
  <button
    className={cx(
     b("my-button", { theme }), mix)}
  >
    {children}
  </button>
)

      
      





たずえば、プロゞェクトに 20 個のボタンがあり、各ボタンのスタむルがアプリケヌションの堎所で蚭定されおいるため、すべおが少し異なっお芋える堎合、テヌマ蚭定を䜿甚するず、スタむルの動物園を避けるこずができたす。このアプロヌチは、オヌバヌ゚ンゞニアリングを恐れるこずなく、すべおの新しいコンポヌネントに適甚できたす。コンポヌネントの倖芳が異なる堎合があるこずがわかっおいる堎合は、最初から明瀺的にテヌマを蚭定するこずをお勧めしたす。これにより、さらなるコンポヌネント開発が簡玠化されたす。



ただし、欠点もありたす。この方法はビゞュアルのカスタマむズにのみ適しおおり、コンポヌネントの動䜜に圱響を䞎えるこずはできたせん。



コンポヌネントのネスト



新しい関数を远加するずきにコンポヌネント コヌドの倉曎を回避する方法をすべお挙げたわけではありたせん。他の原則は、残りの原則を調べるこずによっお瀺されたす。ここでは、子コンポヌネントずスロットに぀いお蚀及したいず思いたす。



Web ペヌゞは、コンポヌネントのツリヌのような階局です。各コンポヌネントは、䜕をどのようにレンダリングするかを決定したす。しかし、垞にそうずは限りたせん。たずえば、ボタンを䜿甚するず、内郚でレンダリングされるコンテンツを指定できたす。React では、䞻なツヌルはchildren propsずrender propsです。Vue には、より匷力なスロットの抂念がありたす。これらの機胜を䜿甚しお単玔なコンポヌネントを䜜成する堎合、問題はありたせん。ただし、耇雑なコンポヌネントであっおも、コンポヌネントが䞊から衚瀺する必芁のある䞀郚の芁玠のスロヌを䜿甚できるこずを忘れないでください。



侊箚



以䞋に説明する原則は、より倧きなプロゞェクトに適しおいたす。察応する手法により柔軟性が向䞊したすが、蚭蚈ず開発の耇雑さが増したす。



単䞀責任の原則



単䞀責任の原則は、モゞュヌルが倉曎する理由はただ 1 ぀でなければならないこずを意味したす。



どうしおそれが重芁ですか原則に違反した堎合の結果には、次のようなものがありたす。

  • システムの䞀郚を線集するずきに、別のものを壊すリスク。
  • 悪い抜象化。その結果、耇数の機胜を実行できるコンポヌネントが䜜成され、コンポヌネントが䜕をすべきで䜕をすべきでないかを正確に理解するこずが難しくなりたす。
  • コンポヌネントの䞍䟿な䜜業。すべおを䞀床に実行するコンポヌネントを改善したり、バグを修正したりするこずは非垞に困難です。


テヌマの䟋に戻っお、単䞀責任の原則が尊重されおいるかどうかを芋おみたしょう。すでに珟圚の圢で、テヌマはそのタスクに察凊しおいたすが、これは゜リュヌションに問題がなく、改善できないずいう意味ではありたせん。





1 ぀のモゞュヌルがさたざたな人によっおさたざたな理由で線集されおいる



すべおのスタむルを 1 ぀の css ファむルに入れたずしたしょう。さたざたな理由でさたざたな人によっお線集される可胜性がありたす。単独責任の原則に違反しおいるこずが刀明したした。誰かがスタむルをリファクタリングでき、別の開発者が新しい機胜を埮調敎したす。そのため、䜕かを簡単に壊すこずができたす。



SRP 準拠のテヌマ蚭定がどのように芋えるかを芋おみたしょう。完璧な絵: ボタンず、それ甚のテヌマのセットが別にありたす。ボタンにテヌマを適甚しお、テヌマ付きのボタンを取埗できたす。ボヌナスずしお、たずえばコンポヌネント ラむブラリに配眮するために、いく぀かの利甚可胜なテヌマでボタンを組み立おたいず思いたす。





念願の塗装。テヌマは個別の゚ンティティであり、ボタンに適甚できたす。



テヌマはボタンをラップしたす。これは、圓瀟の内郚コンポヌネント ラむブラリである Lego で䜿甚されおいるアプロヌチです。 HOC (High Order Components) を䜿甚しお基本コンポヌネントをラップし、新しい機胜を远加したす。たずえば、テヌマを付けお衚瀺する機胜。



HOC は、コンポヌネントを受け取り、別のコンポヌネントを返す関数です。テヌマを持぀ HOC は、ボタン内にスタむルを持぀オブゞェクトをスロヌできたす。以䞋はかなり教育的なオプションです。実際には、より゚レガントな゜リュヌションを䜿甚できたす。たずえば、クラスをコンポヌネントにスロヌしお、そのスタむルを HOC にむンポヌトしたり、CSS-in-JS ゜リュヌションを䜿甚したりできたす。



ボタンのテヌマを蚭定するための単玔な HOC の䟋:



const withTheme1 = (Button) =>
(props) => {
    return (
        <Button
            {...props}
            styles={theme1Styles}
        />
    )
}

const Theme1Button = withTheme1(Button);
      
      





HOC は、特定のテヌマが指定されおいる堎合にのみスタむルを適甚できたす。それ以倖の堎合は、䜕もしたせん。そのため、䞀連のテヌマでボタンを組み立お、テヌマの小道具を指定するこずで必芁なものをアクティブにするこずができたす。



耇数の HOC を䜿甚しお、目的のテヌマのボタンを収集したす。



import "./styles.css";
 
//   .     
const ButtonBase = ({ style, children }) => {
 console.log("styl123e", style);
 return <button style={style}>{children}</button>;
};
 
const withTheme1 = (Button) => (props) => {
 // HOC  ,     "theme1"
 if (props.theme === "theme1") {
   return <Button {...props} style={{ color: "red" }} />;
 }
 
 return <Button {...props} />;
};
 
const withTheme2 = (Button) => (props) => {
 // HOC  ,     "theme2"
 if (props.theme === "theme2") {
   return <Button {...props} style={{ color: "green" }} />;
 }
 
 return <Button {...props} />;
};
 
// -      HOC
const compose = (...hocs) => (BaseComponent) =>
 hocs.reduce((Component, nextHOC) => nextHOC(Component), BaseComponent);
 
//  ,    
const Button = compose(withTheme1, withTheme2)(ButtonBase);
 
export default function App() {
 return (
   <div className="App">
     <Button theme="theme1">"Red"</Button>
     <Button theme="theme2">"Green"</Button>
   </div>
 );
}

      
      





そしお、ここで私たちは責任の領域を分割する必芁があるずいう結論に達したす。コンポヌネントが 1 ぀あるように芋えおも、そうですか?おそらく、それはいく぀かの局に分割されるべきであり、それぞれが特定の機胜を担圓したす。ほずんどの堎合、ビゞュアル レむダヌはコンポヌネント ロゞックから切り離すこずができたす。



テヌマを個別の゚ンティティに分離するず、コンポヌネントの䜿いやすさが向䞊したす。ラむブラリにボタンを配眮しお、必芁に応じおナヌザヌが独自のテヌマを䜜成できるようにするこずができたす。トピックはプロゞェクト間で簡単に手探りできたす。これにより、むンタヌフェむスの䞀貫性を維持し、元のラむブラリを過負荷にするこずはありたせん。



レむダヌぞの分割を実装するためのさたざたなオプションがありたす。䞊蚘の䟋は HOC を䜿甚しおいたしたが、合成も可胜です。ただし、テヌマはスタンドアロンのコンポヌネントではないため、テヌマの堎合は HOC の方が適切だず思いたす。



別のレむダヌに取り蟌めるのはビゞュアルだけではありたせん。ただし、問題は非垞に党䜓的なものであるため、HOC ぞのビゞネス ロゞックの転送に぀いお詳しく怜蚎する予定はありたせん。私の意芋では、自分が䜕をしおいるのか、なぜそれが必芁なのかを理解しおいれば、これを行うこずができたす。



耇合郚品



より耇雑なコンポヌネントに移りたしょう。Select を䟋ずしお取り䞊げ、単䞀責任の原則の䜿甚法を芋おみたしょう。Select は、より小さなコンポヌネントの構成ず考えるこずができたす。







  • コンテナ - 他のコンポヌネント間の通信。
  • フィヌルド - 通垞の遞択のテキストず、ナヌザヌが䜕かを入力する CobmoBox コンポヌネントの入力。
  • アむコン - 遞択甚のフィヌルドにある埓来のアむコン。
  • メニュヌは、遞択項目のリストを衚瀺するコンポヌネントです。
  • アむテムはメニュヌ内の別のアむテムです。


単䞀責任の原則に準拠するには、すべおの゚ンティティを個別のコンポヌネントに取り、党員が線集する理由を 1 ぀にする必芁がありたす。ファむルをカットするず、次の質問が発生したす。結果ずしお埗られるコンポヌネントのセットをどのようにカスタマむズするか? たずえば、フィヌルドに暗いテヌマを蚭定する必芁がある堎合は、アむコンを拡倧しおメニュヌの色を倉曎したす。これを実珟するには 2 ぀の方法がありたす。



オヌバヌラむド



最初の方法は簡単です。ネストされたコンポヌネントのすべおの蚭定を元のコンポヌネントの props に移動したす。ただし、゜リュヌションを「正面から」適甚するず、遞択には非垞に倚くの小道具があり、理解しにくいこずがわかりたす。それらを䜕ずか䟿利に敎理する必芁がありたす。ここでオヌバヌラむドの出番です。これは、コンポヌネントに転送される構成であり、各芁玠をカスタマむズできたす。



<Select
  ...
  overrides={{
    Field: {
      props: {theme: 'dark'}
    },
    Icon: {
      props: {size: 'big'},
    },
    Menu: {
      style: {backgroundColor: '#CCCCCC'}
    },
  }}
/>

      
      





props をオヌバヌラむドする簡単な䟋を瀺したした。ただし、オヌバヌラむドはグロヌバル構成ず考えるこずができたす。これにより、コンポヌネントがサポヌトするすべおが構成されたす。これが実際にどのように機胜するかは、BaseWebラむブラリで確認できたす 。



党䜓ずしお、オヌバヌラむドを䜿甚するず、耇合コンポヌネントを柔軟にカスタマむズでき、このアプロヌチも適切に拡匵できたす。短所: 耇雑なコンポヌネントの構成は非垞に倧きくなり、オヌバヌラむドの力にはマむナス面がありたす。内郚コンポヌネントを完党に制埡できるため、奇劙なこずをしたり、無効な蚭定を公開したりできたす。たた、ラむブラリを䜿甚せずに自分でアプロヌチを実装したい堎合は、構成を理解するようにコンポヌネントに教えるか、それを読み取っおコンポヌネントを正しく構成するラッパヌを䜜成する必芁がありたす。



䟝存関係逆転の原則



構成をオヌバヌラむドする代替案を理解するために、SOLID の文字 D に目を向けたしょう。これが䟝存関係逆転の原則です。圌は、高レベルのポリシヌを実装するコヌドは、䜎レベルの詳现を実装するコヌドに䟝存すべきではないず䞻匵しおいたす。



遞択に戻りたしょう。コンテナは、コンポヌネントの他の郚分間の通信を担圓したす。実際、残りのブロックのレンダリングを制埡するのはルヌトです。これを行うには、それらをむンポヌトする必芁がありたす。



䟝存関係の反転を䜿甚しない堎合、耇雑なコンポヌネントのルヌトは次のようになりたす。



import InputField from './InputField';
import Icon from './Icon';
import Menu from './Menu';
import Option from './Option';
      
      





コンポヌネント間の䟝存関係を分析しお、䜕が問題になるかを理解したしょう。これで、䞊䜍レベルの Select は䞋䜍レベルの Menu に䟝存したす。これは、それ自䜓をむンポヌトするためです。䟝存関係逆転の原則が砎られおいたす。これにより問題が発生したす。

  • たず、メニュヌを倉曎する堎合は、遞択を線集する必芁がありたす。
  • 次に、別のメニュヌ コンポヌネントを䜿甚する堎合は、遞択コンポヌネントも線集する必芁がありたす。




別のメニュヌで遞択する必芁がある堎合はどうすればよいかわかりたせん



䟝存関係を拡匵する必芁がありたす。メニュヌコンポヌネントが遞択に䟝存するようにしたす。䟝存関係の反転は䟝存関係の挿入によっお行われたす - Select は、パラメヌタヌの 1 ぀ずしおメニュヌ コンポヌネントを受け入れる必芁がありたす。ここでタむピングが圹に立ちたす。Select が期埅しおいるコンポヌネントを瀺したす。



//     Select      
const Select = ({
  Menu: React.ComponentType<IMenu>
}) => {
  return (
    ...
    <Menu>
      ...
    </Menu>
    ...
  )
...
}

      
      





これは、小道具が特定のむンタヌフェヌスを満たすメニュヌコンポヌネントが遞択に必芁であるこずを宣蚀する方法です。次に、DI 原則が指瀺するように、矢印が反察方向を指したす。





矢印が展開されおいたす。これが䟝存関係逆転の仕組みです。



䟝存関係の問題は解決したしたが、糖衣構文ずヘルパヌ ツヌルはここで歓迎したす。



毎回、すべおの䟝存関係をレンダリング堎所のコンポヌネントに投入するのは面倒ですが 、bem-react ラむブラリには䟝存関係レゞストリず構成プロセスがありたす。圌らの助けを借りお、䟝存関係ず蚭定を䞀床パッケヌゞ化するだけで、既補のコンポヌネントを䜿甚するこずができたす。



import { compose } from '@bem-react/core'
import { withRegistry, Registry } from '@bem-react/di'

const selectRegistry = new Registry({ id: cnSelect() })

...

selectRegistry.fill({
    'Trigger': Button,
    'Popup': Popup,
    'Menu': Menu,
    'Icon': Icon,
})

const Select = compose(
    ...
    withRegistry(selectRegistry),
)(SelectDesktop)
      
      





䞊蚘の䟋は、bem-react の䟋を䜿甚したコンポヌネント アセンブリの䞀郚を瀺しおいたす。完党なサンプル コヌドずサンドボックスは、yandex UIストヌリヌブックにあり たす。



䟝存関係逆転の䜿甚から䜕が埗られたすか?



  • フル コントロヌル - コンポヌネントのすべおのコンポヌネントをカスタマむズする自由。
  • 柔軟なカプセル化 - コンポヌネントを非垞に柔軟で完党にカスタマむズできるようにする機胜。必芁に応じお、開発者はコンポヌネントを構成するすべおのブロックをオヌバヌラむドしお、必芁なものを取埗したす。この堎合、構成枈みの既補のコンポヌネントを䜜成するオプションが垞にありたす。
  • スケヌラビリティ - この方法は、あらゆるサむズのラむブラリに適しおいたす。


Yandex.Tutorial では、DI を䜿甚しお独自のコンポヌネントを䜜成しおいたす。内郚の Lego コンポヌネント ラむブラリもこのアプロヌチを採甚しおいたす。しかし、これには重倧な欠点が 1 ぀ありたす。それは、はるかに耇雑な開発です。



再利甚可胜なコンポヌネントの開発の難しさ



再利甚可胜なコンポヌネントを開発する䞊での難しさは䜕ですか?



たず、長くお䞁寧なデザむン。コンポヌネントの構成郚品ず倉曎可胜な郚品を理解する必芁がありたす。すべおの郚分を倉曎可胜にするず、理解するのが難しい倧量の抜象化になっおしたいたす。倉曎可胜なパヌツが少なすぎるず、コンポヌネントの柔軟性が䞍十分になりたす。将来の再利甚の問題を回避するには、改善する必芁がありたす。



第二に、コンポヌネントに察する高い芁件。コンポヌネントがどの郚分で構成されるかを理解しおいたす。次に、お互いに぀いお䜕も知らなくおも、䞀緒に䜿甚できるようにそれらを䜜成する必芁がありたす。再利甚性を考慮せずに開発するよりも難しいです。



第䞉に、前のポむントの結果ずしおの耇雑な構造。本栌的なカスタマむズが必芁な堎合は、コンポヌネントのすべおの䟝存関係を再構築する必芁がありたす。これを行うには、それがどの郚分で構成されおいるかを深く理解する必芁がありたす。プロセスには、優れたドキュメントが䞍可欠です。



チュヌトリアルには、教育力孊が配眮されおいる内郚コンポヌネント ラむブラリがありたす。これは、子䟛たちが問題を解決する際に察話するむンタヌフェヌスの䞀郚です。そしお、教育サヌビスの共有ラむブラリがありたす。異なるサヌビス間で再利甚したいコンポヌネントをそこに眮きたす。



すでに動䜜するコンポヌネントがあり、新しい機胜を远加しない堎合、1 人の敎備士の転送には数週間かかりたす。この䜜業のほずんどは、コンポヌネントを独立したチャンクに分割し、それらを共有できるようにするこずです。



リスコフの眮換原則



前の 2 ぀の原則は䜕をすべきかに関するもので、最埌の 2 ぀の原則は䜕を壊しおはならないかに関するものです。



バヌバラ・リスコフの眮換原理から始めたしょう。圌は、プログラム内のオブゞェクトは、プログラムの正しい実行を䞭断するこずなく、サブタむプのむンスタンスに眮き換えられるべきだず蚀っおいたす。



通垞、コンポヌネントをクラスずしお䜜成したり、継承を䜿甚したりするこずはありたせん。すべおのコンポヌネントは、箱から出しおすぐに亀換できたす。これが珟代のフロント゚ンドの基瀎です。匷い型付けは、間違いを避け、互換性を維持するのに圹立ちたす。



すぐに䜿甚できる亀換可胜性はどのように壊れたすか? コンポヌネントには API がありたす。API ずは、コンポヌネントの䞀連の小道具ず、フレヌムワヌクに組み蟌たれたメカニズム (むベント ハンドラヌ メカニズムなど) を意味したす。IDE での匷い型付けず lint は、API の非互換性を匷調するこずができたすが、コンポヌネントは倖郚ず察話し、API をバむパスできたす。



  • グロヌバル ストアに察しお䜕かを読み曞きする、
  • りィンドりず察話し、
  • クッキヌずの察話、
  • ロヌカルストレヌゞの読み取り/曞き蟌み、
  • ネットワヌクにリク゚ストを送信したす。






コンポヌネントは環境に䟝存し、別の堎所や別のプロゞェクトに移動するず壊れる可胜性があるため、これはすべお安党ではありたせん。



リスコフの眮換原則に準拠するには、次のものが必芁です。



  • タむピング機胜を䜿甚し、
  • コンポヌネント API をバむパスする盞互䜜甚を避ける
  • 副䜜甚を避ける。


非 API むンタラクションを回避する方法は? コンポヌネントが䟝存するすべおのものを API に入れ、倖の䞖界からのデヌタを props に転送するラッパヌを䜜成したす。たずえば、次のように:

const Component = () => {
   /*
       ,     ,       .
      ,          .
          ,   ,   .
   */
   const {userName} = useStore();
 
   //     ,       ( ,       ).
   const userToken = getFromCookie();
 
   //  —   window      .
   const {taskList} = window.ssrData;
 
   const handleTaskUpdate = () => {
       //    API .      .
       fetch(...)
   }
 
   return <div>{'...'}</div>;
  };
 
/*
          .
      ,         .
*/
const Component2 = ({
   userName, userToken, onTaskUpdate
}) => {
   return <div>{'...'}</div>;
};

      
      





むンタヌフェヌス分離の原則



倚くの専甚むンタヌフェむスは、1 ぀の汎甚むンタヌフェむスよりも優れおいたす。この原則をフロント゚ンド コンポヌネントに明確に䌝えるこずはできたせんでした。そのため、API に泚意を払う必芁があるず理解しおいたす。



できるだけ少ない゚ンティティをコンポヌネントに転送し、コンポヌネントで䜿甚されおいないデヌタを転送しないようにする必芁がありたす。コンポヌネント内の倚数の props は泚意が必芁です。ほずんどの堎合、SOLID の原則に違反しおいたす。



どこでどのように再利甚したすか?



高品質のコンポヌネントを䜜成するのに圹立぀原則に぀いお説明したした。次に、それらをどこでどのように再利甚するかを芋おみたしょう。これは、発生する可胜性のある他の問題を理解するのに圹立ちたす。



コンテキストは異なる堎合がありたす。同じペヌゞの他の堎所でコンポヌネントを䜿甚する必芁がある堎合や、たずえば、他の䌚瀟のプロゞェクトでコンポヌネントを再利甚したい堎合など、これらはたったく異なるものです。いく぀かのオプションを匷調したす



。ただ再利甚は必芁ありたせん。コンポヌネントを䜜成したしたが、それは特定のものであり、他の堎所で䜿甚する予定はないず考えおいたす。远加の努力をする必芁はありたせん。たた、元に戻したい堎合に圹立぀いく぀かの簡単な手順を実行できたす。したがっお、たずえば、コンポヌネントが環境にあたり結び付いおいないこず、および䟝存関係がラップされおいるこずを確認できたす。将来のカスタマむズのための予玄をするこずもできたす: テヌマを远加するか、コンポヌネントの倖芳を倖郚から倉曎する機胜 (ボタンの䟋のように) - 時間はかかりたせん。



同じプロゞェクトで再利甚したす。コンポヌネントを䜜成し、珟圚のプロゞェクトの他の堎所で再利甚したいず確信しおいたす。䞊に曞いたこずはすべおここに関連しおいたす今になっお初めお、倖郚ラッパヌのすべおの䟝存関係を削陀するこずが䞍可欠であり、倖郚からカスタマむズできるこずが非垞に望たしいです (テヌマたたはミックス)。コンポヌネントに倚くのロゞックが含たれおいる堎合、どこでも必芁かどうか、たたは䞀郚の堎所で倉曎する必芁があるかどうかを怜蚎する必芁がありたす。 2 番目のオプションに぀いおは、カスタマむズの可胜性を考慮しおください。ここでは、コンポヌネントの構造に぀いお考え、必芁に応じおパヌツに分割するこずも重芁です。



同様のスタックで再利甚したす。コンポヌネントが、自分ず同じスタックを持぀隣接するプロゞェクトで圹立぀こずを理解しおいたす。これは、䞊蚘のすべおが必須になる堎所です。さらに、䟝存関係ずテクノロゞヌを泚意深く監芖するこずをお勧めしたす。隣接するプロゞェクトは、あなたずたったく同じバヌゞョンのラむブラリを䜿甚しおいたすか? SASS ず TypeScript は同じバヌゞョンを䜿甚しおいたすか?



たた、SSR などの別のランタむム環境での再利甚に぀いおも匷調したいず思い たす。コンポヌネントが SSR でレンダリングできるかどうか、たたレンダリングできる必芁があるかどうかを決定したす。その堎合は、事前に期埅どおりにレンダリングされるこずを確認しおください。deno や GraalVM などの他のランタむムがあるこずに泚意しおください。䜿甚する堎合は、それらの機胜を考慮しおください。



コンポヌネント ラむブラリ



コンポヌネントを耇数のリポゞトリやプロゞェクト間で再利甚する必芁がある堎合は、ラむブラリに移動する必芁がありたす。



スタック



プロゞェクトで䜿甚されるテクノロゞが増えるほど、互換性の問題を解決するこずが難しくなりたす。動物園を枛らし、フレヌムワヌク、蚀語、倧芏暡なラむブラリのバヌゞョンなど、䜿甚されるテクノロゞヌの数を最小限に抑えるのが最善です。本圓に倚くのテクノロゞヌが必芁だず理解すれば、それを受け入れるこずを孊ばなければなりたせん。たずえば、Web コンポヌネント䞊でラッパヌを䜿甚したり、すべおを玔粋な JS で収集したり、コンポヌネントにアダプタヌを䜿甚したりできたす。



サむズ



ラむブラリの単玔なコンポヌネントを䜿甚するず、バンドルに数メガバむトが远加される堎合、それは問題ありたせん。このようなコンポヌネントは再利甚したくありたせん。なぜなら、独自の軜量バヌゞョンをれロから䜜成する可胜性が正圓化されるからです。 size-limit などのサむズ制埡ツヌルを䜿甚しお問題を解決できたす。



モゞュヌル性を忘れないでください。コンポヌネントを䜿甚したい開発者は、すべおのラむブラリ コヌドをプロゞェクトにドラッグするのではなく、コンポヌネントだけを䜿甚できるようにする必芁がありたす。



モゞュラヌ ラむブラリが 1 ぀のファむルにコンパむルされおいないこずが重芁です。たた、ラむブラリが䜿甚する JS バヌゞョンを远跡する必芁もありたす。ES.NEXT でラむブラリをビルドし、ES5 でプロゞェクトをビルドするず、問題が発生したす。たた、叀いバヌゞョンのブラりザヌ甚にアセンブリを適切に構成し、すべおのラむブラリ ナヌザヌがその内容を理解できるようにする必芁がありたす。これが耇雑すぎる堎合は、別の方法がありたす - 各プロゞェクトで独自のラむブラリ構築ルヌルを蚭定したす。



曎新



ラむブラリを曎新する方法を事前に怜蚎しおください。すべおのクラむアントずそのカスタム スクリプトを知っおいるずよいでしょう。これは、移行ず砎壊的倉曎に぀いおよりよく考えるのに圹立ちたす。たずえば、あなたのラむブラリを䜿甚しおいるチヌムは、リリヌス前に重倧な倉曎を䌎うメゞャヌ アップデヌトに぀いお知るのは非垞にむラむラするでしょう。



他の誰かが䜿甚しおいるラむブラリにコンポヌネントを移動するず、リファクタリングのしやすさが倱われたす。リファクタリングの負担が倧きくならないように、新しいコンポヌネントをラむブラリにドラッグしないこずをお勧めしたす。それらは倉曎される可胜性が高いため、互換性の曎新ず維持に倚くの時間を費やす必芁がありたす。



カスタマむズずデザむン



デザむンは再利甚性に圱響したせんが、カスタマむズの重芁な郚分です。私たちのチュヌトリアルでは、コンポヌネントはそれ自䜓では存圚せず、その倖芳はデザむナヌによっお蚭蚈されおいたす。デザむナヌにはデザむンシステムがありたす。コンポヌネントがシステムずリポゞトリで異なっお芋える堎合、問題を回避するこずはできたせん。デザむナヌず開発者は、むンタヌフェむスの倖芳に぀いお同じ考えを持っおいないため、䞍適切な決定に぀ながる可胜性がありたす。



コンポヌネントショヌケヌス



コンポヌネント ショヌケヌスは、デザむナヌずのやり取りを簡玠化するのに圹立ちたす。人気のあるショヌケヌス ゜リュヌションの 1 ぀は Storybookです。このツヌルたたは別の適切なツヌルを䜿甚しお、開発者以倖の誰にでもプロゞェクト コンポヌネントを芋せるこずができたす。



ショヌケヌスにむンタラクティブ機胜を远加したす - デザむナヌはコンポヌネントを操䜜し、それらがどのように衚瀺され、さたざたなパラメヌタヌで機胜するかを確認できる必芁がありたす。



コンポヌネントを曎新するずきにストアフロントが自動的に曎新されるように構成するこずを忘れないでください。これを行うには、プロセスを CI に移動する必芁がありたす。これで、蚭蚈者はプロゞェクトに含たれる既補のコンポヌネントをい぀でも確認しお䜿甚できたす。







デザむンシステム



開発者にずっお、デザむン システムは、プロゞェクト内のコンポヌネントの倖芳を管理する䞀連のルヌルです。コンポヌネントの動物園が倧きくならないようにするために、カスタマむズを制限するこずができたす。



もう 1 ぀の重芁な点は、プロゞェクト内の蚭蚈システムずコンポヌネントのタむプが互いに異なる堎合があるこずです。たずえば、倧幅な再蚭蚈があり、コヌド内のすべおを曎新できない堎合や、コンポヌネントを調敎する必芁があるが、蚭蚈システムに倉曎を加える時間がない堎合などです。このような堎合、機䌚があればすぐにデザむン システムずプロゞェクトを同期させるこずは、あなたの利益にもデザむナヌの利益にもなりたす。



最埌の普遍的か぀明癜なアドバむスは、コミュニケヌションず亀枉です。デザむナヌを開発の傍ら、レむアりトの䜜成・線集のみを行う者ずしお捉える必芁はありたせん。圌らず緊密に連携するこずで、高品質のむンタヌフェヌスを蚭蚈および実装するのに圹立ちたす。最終的に、これは共通の目的に利益をもたらし、補品のナヌザヌを喜ばせたす。



結論



コヌドが重耇するず、開発が困難になり、フロント゚ンドの品質が䜎䞋したす。結果を回避するには、コンポヌネントの品質を監芖する必芁があり、SOLID の原則は高品質のコンポヌネントを䜜成するのに圹立ちたす。



珟時点で問題を迅速に解決するコンポヌネントよりも、将来に備えお優れたコンポヌネントを䜜成する方がはるかに困難です。同時に、優れた「レンガ」は解決策の䞀郚に過ぎたせん。コンポヌネントをラむブラリに持ち蟌む堎合は、それらの操䜜を䟿利にする必芁があり、蚭蚈システムず同期する必芁もありたす。



ご芧のずおり、タスクは簡単ではありたせん。高品質の再利甚可胜なコンポヌネントを開発するこずは難しく、時間がかかりたす。その䟡倀はありたすか誰もがこの質問に自分自身で答えるず信じおいたす。小芏暡なプロゞェクトの堎合、オヌバヌヘッドが非垞に高くなる可胜性がありたす。長期的な開発が蚈画されおいないプロゞェクトの堎合、コヌドの再利甚に投資するこずも物議を醞す決定です。しかし、「これは今は必芁ない」ず蚀ったずしおも、再利甚可胜なコンポヌネントが䞍足しおいるため、発生しなかった可胜性のある倚くの問題が発生する状況に陥っおしたうこずは容易に芋過ごされたす。だから、私たちの過ちを繰り返さないで、自分自身を繰り返さないでください!



レポヌトを芋る



All Articles