Skip to content

Instantly share code, notes, and snippets.

@lovetingyuan
Created December 22, 2020 07:15
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save lovetingyuan/a231c45d795082810a763008dd6730c4 to your computer and use it in GitHub Desktop.
Save lovetingyuan/a231c45d795082810a763008dd6730c4 to your computer and use it in GitHub Desktop.
use jsdom to prerender simple page.
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
const path = require('path')
const fetch = require('node-fetch')
const finalhandler = require('finalhandler')
const http = require('http')
const serveStatic = require('serve-static')
function createServer (port, root = path.join(__dirname, 'dist')) {
const serve = serveStatic(root)
const server = http.createServer((req, res) => {
serve(req, res, finalhandler(req, res))
})
server.listen(port)
return server
}
module.exports = async () => {
const port = process.env.PORT || 3333
const host = `http://localhost:${port}`
const server = createServer(port)
const { promise, resolve } = new function () {
this.promise = new Promise((resolve) => {
this.resolve = resolve
})
}
const virtualConsole = new jsdom.VirtualConsole();
virtualConsole.on('error', (...e) => { console.error(...e) })
const buildTime = function () {
var d = new Date(TIME);
console.log('build: ' + d.toLocaleDateString() + ' ' + d.toLocaleTimeString());
}
const dom = await JSDOM.fromURL(host, {
runScripts: 'dangerously',
resources: 'usable',
virtualConsole,
pretendToBeVisual: true,
beforeParse(window) {
window.fetch = window.fetch || ((url, option) => {
return fetch(host + url, option)
});
window.__prerender = resolve
}
})
await promise
const doc = dom.window.document
const injectScript = doc.createElement('script')
injectScript.textContent = [
`(${buildTime.toString().replace('TIME', Date.now()).replace(/\s{2,}/g, ' ')})()`
].join(';')
doc.head.appendChild(injectScript)
server.close()
const html = dom.serialize()
dom.window.close()
return html
}
if (require.main === module) {
console.log('Start prerendering...')
module.exports().then((renderedHtml) => {
console.log('Prerender done.', renderedHtml)
}).catch(e => {
console.error('prerender failed.', e)
})
}
@loganpowell
Copy link

I was looking for something just like this! QQ: what is window.__prerender?

@lovetingyuan
Copy link
Author

For example, you want to fetch api in your page, and you want to wait the api data is rendered and after that prerender the html
To do that, you need to call __prerender after the api data is fetched, and the output html will include the server data.
__prerender is just the promise resolve function, you can use any other name.
By the way, the solution in this gist is just for simple pages, if you use any specific frontend framework, eg vue or react, I suggest to use their SSR function to get static rendered html.

@loganpowell
Copy link

That's what I was hoping!! Brilliant. Wonderful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment