Puppeteer と React でコンポーネントを js と一緒にサーバサイドレンダリングする
2019/12/10 17:36:042021/11/12 16:14:53
"renderToStaticMarkup だとテンプレートにデータ投入できるけど、じゃあコンポーネントまで含んだフルの html を構成してやろうとするとデータを投入する手筈が考えつかなくなった" - @ci7lus - 12:05pm · 10 Dec 2019
renderToStaticMarkup を使うと React のコンポーネントを型を伴いながら簡単に html に変換することができて大変便利です。これを用いて生成した html を Puppeteer でパシャっとしてやると引数で渡した情報を簡単に画像にすることができます。使ったことないですが OGP タグも生成できるのではないでしょうか。 renderToStaticMarkup では非同期処理を待つことができませんが、時には投入したデータに対して Google Charts を用いたグラフが生成されてほしいときもあります。 const data = JSON.parse(\{JSON.stringify(data)}\) とかになってしまいます。 renderToStaticMarkup で生成するテキスト内にうまいこと js を埋め込む。 src/template/index.tsx declare global {
interface Window {
render: (payload: Payload, element: Element | null) => void
}
}
export type Payload = {
[age: number]: number
}
export const MyComponent: React.FC<{
payload: Payload
}> = ({ payload }) => {
return (
<div className="container">
<Chart
chartType="ScatterChart"
data={[["Age", "Weight"], ...Object.entries(payload)]}
width="100%"
height="400px"
/>
</div>
)
}
if (window != undefined) {
window.render = (payload, element) => {
ReactDOM.render(<MyComponent payload={payload} />, element)
}
}window.render として呼び出せるようになってかなり平和。 bundle.js にビルドする。 webpack.config.js でビルドを行うかはおそらく問いません。 src/index.ts await new Promise((res, rej) => {
webpack(
{
mode: "development",
entry: "./src/template/index.tsx",
output: {
filename: "./bundle.js",
},
module: {
rules: [{ test: /\.tsx?$/, use: "ts-loader" }],
},
resolve: {
extensions: [".ts", ".tsx", ".js", ".json"],
},
},
(err, stats) => {
if (err || stats.hasErrors()) {
rej(err || stats.hasErrors())
} else {
res(stats)
}
}
)
})page.setContentでロードさせる。 src/index.ts#content.html <div id="app"></div>page.addScriptTag で bundle.js を読み込ませる。 src/index.ts await page.addScriptTag({ path: require.resolve(`${__dirname}/../dist/bundle.js`) })page.evaluate にデータを投入、定義しておいた render 関数でレンダリングする。 src/index.ts onst data: Payload = {
4: 5.5,
8: 12,
} // 渡したいデータ
await page.evaluate((payload: Payload) => { // 後ろで渡したデータに型を付けている
window.render(payload, document.getElementById("app")) // bundle 内で露出しておいた関数を実行
}, data) // 渡したいデータは後ろにpage.waitForNavigation が効かないようで、page.waitFor しておかないとグラフが現れる前に撮られてしまったりします。事前に指定しておいた dom の出現を待つ方法もあるらしいんですが、画像リソースなどのロードはどうなってしまうんでしょうか。 Generated from
Puppeteer と React でコンポーネントを js と一緒にサーバサイドレンダリングする