1
1

More than 3 years have passed since last update.

NuxtでSSRやっててプロダクションビルドでだけ画面初期表示時に一瞬ページコンポーネントのCSSがあたっていなくても泣かないために

Posted at

まず結論

vue-style-loader はSFC(拡張子 .vue) ファイルの<style />タグしか、SSRレンダリングしてくれない。
だもんで多分、Nuxtに限った話でなく、VueでSSRしたい時にぶつかる問題。
てっとり早く解決したければSFCでstyleタグに書くべし。

事の経緯

最近、Nuxtのページコンポーネントもjsx(tsx)で書くようになった。
だもんで、ページ毎のスタイルは全部ページコンポーネントの冒頭で、


import './hoge.scss';

みたいなやりくちで読み込む事にしていた。
ローカル開発中はまったくこれで問題なかった。
だけど、どういう事でしょう。本番ビルドでNuxtがレンダリングするhtmlにはどこにもこのstyleの記述が見当たらない。

こういう事らしい

manualInject オプション

斜め翻訳 by わたし
非vueファイルからスタイルを読み込む時は、単純にjsのバンドルにスタイルが埋め込まれるだけだから、手動でssrコンテキストにぶちこんでね。

ほうほうそういう事か。ばっちこい!、、と思って importしたstyleにきっと __inject__ っていうメソッドがSSR側ではあるんだろうなぁ、と思ってやってみたものの、、ない。


import hoge from './hoge.scss';
console.log(hoge); // >>> {}

なんだこれ。

どうもこれ、流れとしては、
- Nuxtの内部のwebpackデフォ設定ではスタイルのimportは必ず vue-style-loader を通るようになっている
- vue-style-loader はデフォでは必ずjsバンドルにスタイルを埋め込んで終了する
- __inject__ を自前で使いたければ manualInject オプション設定しなさい。

って事みたい。
で、Nuxt.jsのローダー設定オプションで manualInject をtrueで設定してみる。すると、、
まあ、もう、当たり前なんですけどページコンポーネント以外の全てのコンポーネントファイル内でimportしていたスタイルが全て撲滅で読み込まれない形となる。
で、、 ~/components 配下のスタイルimportだけはmanualじゃなくてautoで読み込んでね、ってしたいけどそんなオプションはない。
プロジェクト自前で一個、「tsxファイル、かつvueコンポーネントみたいなものをexportしているファイルでスタイルをロードしようとしていたら、beforeCreateに強制的にスタイルインジェクトをさせる」webpackのローダーを書くべきか悩んで、
「それって、そのts(x)ファイルが本当にvueコンポーネントファイルなのかどうかって、怪しいよな」と思って、今時点では素直にSFCで書いて、SFCからexportして他ファイルで参照したい情報はSFCの外部に書いてシェアする事にした。
これ機会は少ないけどもろに発生するのが「親ページコンポーネントの何かを子ページにinjectするパターン」

parent.tsx
import Vue from 'vue';
import { Component } from 'nuxt-property-decorator';

interafce SomeParentInfo {
  hoge: string;
  fuga?: number;
}

@Component<ParentView>({
  provide() {
    const { info } = this;
    return {
      parentInfo: info,
    }
  },
  render() {
    return (
      <div>
        ...親コンテンツ
      </div>
    );
  },
})
export default class ParentView extends Vue {
  info: SomeParentInfo = { ... };
}
child.tsx
import Vue from 'vue';
import { Component } from 'nuxt-property-decorator';
import { SomeParentInfo } from './parent.tsx';

@Component<ChildView>({
  inject: ['parentInfo'],
  render() {
    return (
      <div>
        {JSON.stringfy(this.parentInfo)}
        ...子コンテンツ
      </div>
    );
  },
})
export default class ChildView extends Vue {
  readonly parentInfo!: SomeParentInfo;
}

こういう時に SomeParentInfo がsfcの中でコーディングされていると、TypeScriptはそでその中身を抽出しようとするとvue → d.ts のコンバータを利用する以外手がなくなる。
ただもって、今時点で割とビルドパフォーマンスがヘビーなのもあってそのコンバーター用意するよりかは、
素直に SomeParentInfo を外だしして、親と子で双方そのインターフェースを参照する方が早いかなと思い、それで解決した。

ちょっとうっすら思っている事

ページコンポーネントのtsxファイルの中で、


import '!hoge-loader[xxxxx]./hoge.scss';

みたいにして、いい感じにmanualInjectを渡したらページコンポーネント側でだけmanualInjectしたい、、ってのは解決しそうな気も少しする。
、、けど、ページ毎に個別にwebpackのloaderオプション書くのもそれはそれで大変だし、
「そのファイルvueコンポーネントですか?」は結局解決しない。(そもそもVue sfcはdefault export = vueコンポーネントって思想)なので、素直にSFC書く事にした。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1