18
18
19
19
import fs from 'fs' ;
20
20
import bodyParser from 'body-parser' ;
21
+ import url from 'url' ;
22
+ const URL = url . URL ;
21
23
// import compression from 'compression';
22
24
// import minify from 'express-minify';
23
25
import express from 'express' ;
@@ -42,21 +44,56 @@ function updateRSSFeedsDaily() {
42
44
setTimeout ( updateRSSFeedsDaily , dayInMilliseconds ) ;
43
45
}
44
46
45
- async function ssr ( url ) {
47
+ /**
48
+ *
49
+ * @param url {string} URL to prerender.
50
+ * @param onlyCriticalRequests {boolean} Reduces the number of requests the
51
+ * browser makes by aborting requests that are non-critical to rendering
52
+ * the DOM of the page (stylesheets, images, media). True by default.
53
+ * @return {string } Serialized page output as an html string.
54
+ */
55
+ async function ssr ( url , onlyCriticalRequests = true ) {
46
56
if ( RENDER_CACHE . has ( url ) ) {
47
57
return RENDER_CACHE . get ( url ) ;
48
58
}
49
59
60
+ // Add param so client-side page can know it's being rendered by headless on the server.
61
+ const urlToFetch = new URL ( url ) ;
62
+ urlToFetch . searchParams . set ( 'headless' , '' ) ;
63
+
50
64
const tic = Date . now ( ) ;
51
65
const browser = await puppeteer . launch ( {
52
- args : [ '--disable-dev-shm-usage' ]
66
+ args : [ '--disable-dev-shm-usage' ] ,
53
67
} ) ;
54
68
const page = await browser . newPage ( ) ;
55
- await page . goto ( url , { waitUntil : 'domcontentloaded' } ) ;
69
+
70
+ // Small optimization. Since we only care about rendered DOM, ignore images,
71
+ // css, and other media that don't produce markup.
72
+ if ( onlyCriticalRequests ) {
73
+ await page . setRequestInterception ( true ) ;
74
+ page . on ( 'request' , req => {
75
+ const whitelist = [ 'document' , 'script' , 'xhr' , 'fetch' , 'websocket' ] ;
76
+ if ( whitelist . includes ( req . resourceType ( ) ) ) {
77
+ req . continue ( ) ;
78
+ } else {
79
+ req . abort ( ) ;
80
+ // req.respond({
81
+ // status: 200,
82
+ // contentType: 'text/plain',
83
+ // body: ''
84
+ // });
85
+ }
86
+ } ) ;
87
+ }
88
+
89
+ // TODO: another optimization may be to take enter page out of rendering pipeline.
90
+ // Add html { display: none } to page.
91
+
92
+ await page . goto ( urlToFetch . href , { waitUntil : 'domcontentloaded' } ) ;
56
93
await page . waitForSelector ( '#posts' ) ; // wait for posts to be in filled in page.
57
- const html = await page . content ( ) ; // Browser "SSR" page for us! Get serialized DOM.
94
+ const html = await page . content ( ) ; // Use browser to prerender page, get serialized DOM output!
58
95
await browser . close ( ) ;
59
- console . info ( `Headless chrome render time : ${ Date . now ( ) - tic } ms` ) ;
96
+ console . info ( `Headless rendered page in : ${ Date . now ( ) - tic } ms` ) ;
60
97
61
98
RENDER_CACHE . set ( url , html ) ; // cache rendered page.
62
99
@@ -99,7 +136,8 @@ app.use(express.static('node_modules/lit-html'));
99
136
// });
100
137
101
138
app . get ( '/ssr' , async ( req , res ) => {
102
- const html = await ssr ( req . getOrigin ( ) ) ;
139
+ const optimizeReqs = 'noreduce' in req . query ? false : true ;
140
+ const html = await ssr ( req . getOrigin ( ) , optimizeReqs ) ;
103
141
res . status ( 200 ) . send ( html ) ;
104
142
} ) ;
105
143
0 commit comments