まず結論
vue-style-loader はSFC(拡張子 .vue) ファイルの<style />
タグしか、SSRレンダリングしてくれない。
だもんで多分、Nuxtに限った話でなく、VueでSSRしたい時にぶつかる問題。
てっとり早く解決したければSFCでstyleタグに書くべし。
事の経緯
最近、Nuxtのページコンポーネントもjsx(tsx)で書くようになった。
だもんで、ページ毎のスタイルは全部ページコンポーネントの冒頭で、
import './hoge.scss';
みたいなやりくちで読み込む事にしていた。
ローカル開発中はまったくこれで問題なかった。
だけど、どういう事でしょう。本番ビルドでNuxtがレンダリングするhtmlにはどこにもこのstyleの記述が見当たらない。
こういう事らしい
斜め翻訳 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するパターン」
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 = { ... };
}
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書く事にした。