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 と一緒にサーバサイドレンダリングする