概要
何かをランダムで表示するときに、SSR と CSR でそれぞれランダムにしてしまうと、 Mismatching childNodes vs. VNodes
に遭遇してしまう。
VDOM にズレが生じてよろしくない。
対策
対策は多分二つある。
1. asyncData
asyncData はサーバーでしか実行されないので、コンポーネントに密な要素はここで作ってしまえば一番手っ取り早い。
2. リクエストごとにランダム用のシードを用意する
Vuex の getter を computed 経由で呼び出すような場合は、クライアントでもしっかり再実行されてしまう。
そこで以下のように randomSeed
を生成する。
export const state = () => ({
randomSeed: new Date().getTime().toString(),
});
export type RootState = ReturnType<typeof state>;
state
関数はリクエストごとに実行され、初期化された新しい state を返す。
ここになんでも良いのでシードを入れておく。
これをランダム生成の際に使えば、リクエストごとに異なる結果を得られるが、サーバーとクライアントで同じ結果を得られるようになる。
以下は、配列をシャッフルする例。
shuffled.tsimport cloneDeep from 'clone-deep';
import seedrandom from 'seedrandom';
export default <T>(ary: Array<T>, seed?: string): Array<T> => {
const cp = cloneDeep(ary);
const random = seedrandom(seed);
// Fisher-Yates
for (let i = cp.length - 1; i > 0; i -= 1) {
const r = Math.floor(random() * (i + 1));
const tmp = cp[i];
cp[i] = cp[r];
cp[r] = tmp;
}
return cp;
};
hogeStoreModule.tsexport const getters: GetterTree<HogeState, RootState> = {
randomArray(state: HogeState, _, rootState: RootState) {
return shuffled(state.hoges, rootState.randomSeed);
},
}
まとめ
シードをとって再現性のあるランダムが作れるようにしておけば、テストもしやすいし良い。