styled-componentsの不思議な仕組み(タグ名+文字列)を簡単なサンプルで再現


動作サンプル

  • styled-componentの「タグ名`cssのスタイル定義`」という書き方が気になるので仕組みを調べました。

 タグ名の後ろに括弧もなく突然`テンプレート文字文字列`が現れるやつです。

const TestDiv = styled.div`
  color:blue;
  font-size:20pt;
`;`

これはタグ付きテンプレートという構文で、関数に文字列を渡すのとほぼ同じ事ができるようです。詳細はリンク先でご確認ください。

function tag(strings) {
  console.log(strings.raw[0]);
}
tag`string text line 1 \n string text line 2`;
// tag('文字列');

styled-componentsの仕組みを再現する最小限のサンプルコード

こんな感じで使える「なんちゃってstyled-componsnts」を作ってみます。

// 「なんちゃってstyled-componsnts」を関数として呼び出して、コンポーネントを取得
const Tag1 = pTag('color:red;');

// 「なんちゃってstyled-componsnts」をタグ付きテンプレートとして呼び出す
const Tag2 = pTag`
color:blue;
font-size:20pt;
`;

// Tag2はborderをprops経由で渡す
return (
  <div className="App">
    <Tag1>Tag1</Tag1>
    <Tag2 style={{border:'1px solid green'}}>Tag2</Tag2>
  </div>
);
  • ソース全体

    • 仕組み自体は難しくありません。<style>タグと<p>タグをセットで作成し、ユニークなclass名をつけて、styleの適用をしています
let seq = 0; // classNameを重複させないためのシーケンス番号

// 「<p>タグ+スタイル設定」コンポーネントを返す「なんちゃってstyled-componsnts」
const pTag = (styles: ReadonlyArray<string> | string) => {
  return (props: any) => {
    // class名を生成(重複しないようにseqをカウントアップ)
    const clsNm = `clsNm${seq++}`;

    // <style>タグを<p>をセットで生成する
    // 重複しないclass名でタグとセレクタを作成する
    // propsはpタグに引き渡す
    return (
      <>
        <style>.{clsNm} {'{'}{styles}{'}'}</style>
        <p className={`${clsNm}`} {...props}>{props.children}</p>
      </>    
    )
  }
};

// 「なんちゃってstyled-componsnts」を関数として呼び出して、コンポーネントを取得
const Tag1 = pTag('color:red;');

// 「なんちゃってstyled-componsnts」をタグ付きテンプレートとして呼び出す
const Tag2 = pTag`
color:blue;
font-size:20pt;
`;

// 生成したコンポーネントを表示する
function App() {
  return (
    <div className="App">
      <Tag1>Tag1 props.childrenに渡す文字列</Tag1>
      <Tag2 style={{border:'1px solid green'}}>Tag2 props.childrenに渡す文字列</Tag2>
    </div>
  );
}

export default App;

  • 生成した画面イメージ

指定したスタイルが適用されていることがわかります。

  • 生成したDOM

参考ページ

Demystifying styled-components