Upload
others
View
15
Download
0
Embed Size (px)
Citation preview
Джеймс Аквух, Software Engineer
SSR: DIY
3
spinner.com
SPA
4
5
SSR
6
+
7
〉SEO〉Никакого SPA-синдрома〉Плавная деградация〉Улучшение FMP, TTI〉Кэширование HTML
8
{ "express": "^4.15.2", "handlebars": "^4.0.7", "webpack": "^2.4.1", "babel-loader": "^7.0.0", }
* 3G соединение 9
FMPTTI
10
FMP
TTI
12
{{!–– Document.hbs ––}}
...
<link rel="stylesheet" href="dist/styles.css">
...
<div class="bicycle"> {{content}} </div>
...
<script src="dist/bundle.js"></script>
13
// entries/server.js
...
app.use(async (req, res, next) => { const content = new SafeString( `<div class="loader"></div>` );
res.end(template({content})); next() });
...
14
// entries/client.js
...
function start() { const $container = document.querySelector('div.bicycle'); const app = new Bicycle({fetch});
app.render().then(html => { // XSS ! don't do that ;) $container.innerHTML = html; }); }
...
15
// fetch.js
...
const delay = IS_SERVER ? 100 : 500;
export default function({offset = 0, count = 20} = {}) { return new Promise(resolve => { setTimeout(() => resolve(data.slice(offset, offset + count)), delay ) }); }
...
16
17
FMP: ~6300 мсTTI: ~6300 мс
18
20
// entries/server.js
...
app.use(async (req, res, next) => { const app = new Bicycle({fetch}); const content = await app.render(); res.end(template({content})); next() });
...
21
22
23
FMP: ~1700 мсTTI: ~6900 мс
〉Прямой доступ к DOM〉Унифицированный API〉Глобальные переменные〉Синглтоны〉Реактивность данных
*
24
26
<div data-reactid=".157rq30hudc" data-react-checksum=“556954499" >
<div data-reactid=".157rq30hudc" data-react-checksum="556954499" >
27
<div data-reactid=".157rq30hudc" data-react-checksum=“556954499" >
{{!–– Bicycle.hbs ––}}
<div {{SSR_HASH_ATTR}}="{{uid}}" class="bicycle"> {{content}} </div>
28
<div data-reactid=".157rq30hudc" data-react-checksum=“556954499" >
// Bicycle.js
export class Bicycle {
...
getUID() { return `${this.count}-${this.chunks}`; }
... }
29
<div data-reactid=".157rq30hudc" data-react-checksum=“556954499" >
<div class="bicycle" data-ssr-hash="100-3" >
30
<div data-reactid=".157rq30hudc" data-react-checksum=“556954499" >
// entries/client.js
function start() { ... const app = new Bicycle({fetch});
const expectedUID = app.getUID(); const actualUID = $container.children[0] .getAttribute(SSR_HASH_ATTR);
if (expectedUID !== actualUID) { app.render().then(html => { ... }); } }
31
—//—
32
FMP: ~1700 мсTTI: ~5100 мс
uid — плохоhash(VDOM) — хорошо
33
*
35
<div data-reactid=".157rq30hudc" data-react-checksum=“556954499" >
// Bicycle.js import Cache from 'lru-cache'; const cache = new Cache(100); ... async render() { const uid = this.getUID();
if (cache.has(uid)) { console.info('Cache hit.'); return cache.get(uid); }
cache.set(uid, html); return html; } ...
36
37
−300 мс
38
FMP: ~1400 мсTTI: ~4800 мс
Кэшируйте только для неавторизованных пользователей
39
*
41
<div data-reactid=".157rq30hudc" data-react-checksum=“556954499" >
{{!–– Document.hbs ––}}
<link rel="prefetch" as="script" href="dist/bundle.js">
42
MDN: Link prefetching — это браузерный механизм, использующий время простоя браузера для предзагрузки ресурсов, которые могут скоро понадобиться пользователю
Ха. Ха. Ха.
43
44
45
FMP: ~1400 мсTTI: ~7800 мс
47
<div data-reactid=".157rq30hudc" data-react-checksum=“556954499" >
{{!–– Document.hbs ––}}
<link rel="preload" as="script" href="dist/bundle.js">
48
49
FMP: ~1600 мсTTI: ~5000 мс
51
<div data-reactid=".157rq30hudc" data-react-checksum=“556954499" >
export class GeneratorStream extends Readable {
constructor(generator) { super(); this.iterator = generator(); }
async readAndEmit() { ... }
}
52
<div data-reactid=".157rq30hudc" data-react-checksum=“556954499" >
let stop = false; do { let {value, done} = this.iterator.next(); // если текущее значение - Promise - ожидаем его if (value && value.then) { value = await value; } // прекращаем отдачу контента, если его больше нет // либо .push() вернул false // (что означает, что клиент не успевает // выкачать данные - backpressure) stop = !this.push(done ? null : value) || done; } while (!stop)
53
<div data-reactid=".157rq30hudc" data-react-checksum=“556954499" >
// DocumentGenerator.js
export default function *() { yield headerTemplate();
const app = new Bicycle({fetch}); // Делегируем рендеринг корневому компоненту yield * app.render();
yield footerTemplate(); }
54
<div data-reactid=".157rq30hudc" data-react-checksum=“556954499" >
{{!–– Header.hbs ––}}
<html> <head> <title>SSR demo app</title> <link rel="stylesheet" href="dist/styles.css"> <link rel="preload" as="script" href="dist/bundle.js"> </head> <body> <div class="title"> <h1>Bitcoin rate</h1> </div> <div id="container">
55
<div data-reactid=".157rq30hudc" data-react-checksum=“556954499" >
// Bicycle.js
*render() { const uid = this.getUID(); const {count, chunks} = this;
yield headerTemplate({SSR_HASH_ATTR, uid});
for (let i = 0; i < chunks; ++i) { yield this.renderPartial({ offset: count * i, count }); }
yield footerTemplate(); }
56
<div data-reactid=".157rq30hudc" data-react-checksum=“556954499" >
{{!–– Footer.hbs ––}}
</div> <div class="copyright"> Data taken from <a target="_blank" href="/price/"> coindesk.com </a> </div> <script src="dist/bundle.js"></script> </body> </html>
57
58
59
FMP: ~1600 мсTTI: ~4600 мс
〉HTTP-статус всегда 200 :)〉Меньший размер gzip_buffers〉Поисковики (не ждут)
60
*
61
TL;DRSPA → SSRFMP: 6300 → 1600 мсTTI: 6300 → 4600 мс
62
Демо: akwuh.me/ssr-demoКод: github.com/jakwuh/ssr-demo
Джеймс АквухSoftware Engineer
jakwuh
jakwuh
Спасибо за внимание
t.me/dailytip