UGA Boxxx

つぶやきの延長のつもりで、知ったこと思ったこと書いてます

【フロントエンド開発】styled-componentsからlinariaに書き換えてみる

注意
この記事の内容は作業メモですが、作業したのがlinariaバージョン4の時(現在はバージョン6が最新)のため、若干イントールするものやセットアップ方法が異なります。最新の情報はlinariaのドキュメントを参照してください。

これまでNext.jsではない単純なReactプロジェクトでstyled-componentsを使ってCSSをあてていたが、styled-componentsのパフォーマンスが気になるのでzero-runtimeのライブラリを導入したい

zero-runtimeのライブラリは有名なのがいくつかって、その中でvanilla-extractとlinariaを以前調べたことがあった

2つには書き方に大きな違いがあって、

vanilla-extractの場合はcssプロパティをパスカルケースで記述し、

export const container = style({
  textAlign: 'center'
})

linairiaの場合はcssプロパティをstyled-componentsやCSSと同じケバブケースで記述する

export const container = style.div`
  text-align: 'center'
`

悩んだ結果、移行前となるべく書き方が変わらないようstyled-componentの記法により近いlinariaにすることにした

準備

環境は以下

  • linaria: v4
  • webpack: v5
  • typescript

インストール

$ npm i -D @linaria/babel-preset @linaria/core @linaria/react @linaria/webpack5-loader mini-css-extract-plugin

.babelrcに以下を記述する

{
  "presets": {
    ...
    "@linaria"
  }
}

ビルドツールにはwebpackを使っており、webpack.config.jsにloaderを記載する

typescriptを導入している場合は、{ loader: 'babel-loader' }{ loader: 'ts-loader' }の間に@linaria/webpack5-loaderの記述する

module.exports = {
  ...
  module: {
    rules: [
      ...,
      {
        test: /\.(js|ts|tsx)$/,
        exclude: /node_modules/,
        use: [
          { loader: 'babel-loader' },
          {
            loader: '@linaria/webpack5-loader',
            options: {
              sourceMap: !prod,
            },
          },
          { loader: 'ts-loader' },
        ],
      },
      {
        test: /\.css$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
          },
          {
            loader: 'css-loader',
            options: {
              sourceMap: !prod,
            },
          },
        ],
      },
    ],
  },
  plugins: [
    ...
    new MiniCssExtractPlugin({
      filename: 'styles-[contenthash].css',
    }),
  ],
}

これでlinariaでスタイリングできるようになった

マイグレーション

styled-componentsで書かれた既存のスタイルはあるので、これをうまく置換してlinariaのフォーマットに変換する

styled-componentsでは以下のように書かれていて

export const Container = styled('div', props)`
   color: red;
`

linariaでは以下のように記述するのだが、見た通り基本的にはほとんど正規表現を使った置換でいける

export const Container = styled.div`
   color: red;
`

ただ、動的な部分は工夫が必要で、例えば、styled-componentsでは以下のようにpropsとして渡されたisErrorでスタイルを動的に切り替えているが、これはlinariaではできない

export const Container = styled('div', props)`
   color: ${props => props.isError ? "red" : "blue"};
`

そこで、classにisErrorがついていたら、つまり、.isErrorがついていたらスタイルを切り替えるようにする

これはcss-modulesの考え方と同じ

先ほどのスタイルは以下のように記述し、

export const Container = styled.div`
    color: blue;
    &.isError {
      color: red;
    }
`

jsx側では以下のようにする(classNameの生成にはclsxを使用している)

<Container className={clsx(isError && "isError")}>text</Container>

手作業にはなってしまうが、これで動的な部分もマイグレーションすることができた