Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

React+styled-componentsのSSRだと部分的にレンダリングされない?

More than 1 year has passed since last update.

前提

  • React/Reduxで動くSPAのWebサービス
  • ページの構造は簡単なヘッダ、フッタ、サイドバーとメインのビュー構造
  • node.jsで、SSRしてstreamで返すようにしてる
  • styled-componentsでstyleをあててる

現象

SSRされたページのうち、サイドバーとフッタはstyleがあたらない。
正確には、SSRされたコンポーネントはヘッダとメインのリスト表示のみstyleがあたった状態で、サイドバー(右)とフッタはstyleがあたっていない状態で配信されてしまう。ただしその後クライアント側でhydrateされる段階で、styled-componentsのstyleがhtml上に展開されると全てのコンポーネントにstyleがあたる。このため、一瞬styleがあたっていない状態が見えるようなことが発生する。

原因

styled-componentsでstreamを使ったSSRを行う場合には、interleaveWithNodeStream()を利用する。これは、reactがSSRする際のreadableStreamをフックするような形で、streamの区切り(chunk)ごとにstyleをあてるtransform関数を適用している。

参考:Github: styled-components transform

しかし、streamのchunkはhighWatermarkの単位で分割されるわけだが、最初のchunkにstyled-componentのtransformが適用されると、それ以降のコンポーネントに対してstyleの適用がsealedされてしまい、そのままSSRされて配信されるためだと思われる。

styled-components v3.1.0の更新が関係してると思われるが、SSRされなくなるといった記載は見当たらない。
ちなみにこの更新は、insertRulesできるようになってjsでstyleを動的に追加するようにしたっていうのと、chunk的に分割して読み込めるようにすることで、SSRされる速度を高速化すると書いてあるように読めた。

参考:v3-1-0-such-perf-wow-many-streams

ちなみにstreamのchunkで分けられるサイズが大きければ問題が発生しなくなるか確認するために、試しに以下(streamのchunkサイズ)を2倍に変えたら、SSRで全てのコンポーネントにstyleがきくようになった。

参考:Github: ReactDOMNodeStreamRenderer.js#L23

this.push(this.partialRenderer.read(size * 2));

解決策

パッと思いつく方法は3つほどある。

  1. あててるstyleを減らす
    適用しようとしてるstyleの数が多いからchunkで分けられてしまうのであって、単純にcss部分を減らすことができれば問題は起きないと思われる。

  2. 何もしない
    First-interactiveまでの時間が高速化されているのは事実なわけだし、SSRされたHTMLを開いた瞬間は確かにstyleがあたっていないけど、クライアント側でhydrateされるときにはきちんと表示されるわけだし、何もしないという手もある。

  3. 無理やり全てのstyleをあてる
    今回は一応、これを選んで試してみてる。具体的には、interleaveWithNodeStream()で受け取ったstreamをstream.on('end' ,...)で読み終えてから、getStyleTags()で改めてcssを取得する。取得したstyleをinlineにHTMLに展開し、そのHTMLコンポーネントをrenderToStaticNodeStream()して配信する。こうすることで、全コンポーネントに対するstyleがきくようになった。

まとめ

  • styled-componentsでSSR難しい&ややこしい
  • 原因や解決策が本当にあっているのか自信があるわけではないので、何か間違っていることなどあればご指摘いただけると嬉しいです。

2018/09/12 追記

  • 上記styled-componentsのバグは修正されました(v3.4.6)!
  • テストが通らないことをIssue/PRとして出したら、修正してもらえました。
  • どうやらServerStyleSheetのinstanceが持つtagMapに問題があったようです。
    • SSRするタイミングでスタイルの読み込みをchunkごとにシールドしてtagMapに入れるはずが、
    • コンポーネントを読みこんだ時点でtagMapを作ってしまっていたため、2つ目以降のchunkのstyleを取り込めなかった。
    • 例えば、以下のようにすると動いた。
    • 修正は、tagごとにchunkに取り込んだかどうかのフラグを持たせるようにしたみたいですね。
    const sheet = new ServerStyleSheet()
+   sheet.instance.tagMap = {}
    const jsx = sheet.collectStyles(
ka2jun8
recruitlifestyle
飲食・美容・旅行領域の情報サイトや『Airレジ』などの業務支援サービスなど、日常消費領域に関わるサービスの提供するリクルートグループの中核企業
http://www.recruit-lifestyle.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away