## 好处
React16推出了流式服务端渲染,它允许你并行地分发HTML片段。这样可以让渲染 出的首字节有意义的内容给用户速度更快。 (例子1,上面部分是一次性转换,下面是流渲染,两种方式) 而且相对renderToString ,流是异步的。这个可以让你的Node.js服务一次性渲染多个请求,并且保持在高请求压力环境下的及时响应。在一个持续的时间内,如果网络阻塞了,它可以停止React的渲染,并且不会因为重的网络请求影响其他轻请求。
实现: 用renderToNodeStream
import { renderToNodeStream } from 'react-dom/server';
import Frontend from '../client';
app.use('*',(request,response) => {
response.write('<html><head><title>Page</title></head><body><div id="root">');
const stream = renderToNodeStream(<Frontend />); stream.pipe(response,{ end: 'false' }); // 当React渲染结束后,发送剩余的HTML部分给浏览器 stream.on('end',() => { response.end('</div></body></html>'); }); });
(以上是例子2)
流是怎么工作的
renderToNodeStream 返回了一个可读流,可以比作水龙头:它有源源不断的水,只是被堵住了,等待别人把它拧开。如果用这个可读流(如果把水龙头打开),只需要监听“data”时间就可以了(如果有新数据到了会自动触发) 代码类似这样:
import { renderToNodeStream } from 'react-dom/server';
const stream = renderToNodeStream(<Frontend />);
stream.on('data',(data) => { console.log(JSON.stringify(data)); });
但是我们看例子2并没有监听”data“事件,而是直接把流pipe给了response对象。其实啊, response对象在Node.js中是一个可写流,可以比做下水道。可读流通过pipe给可写流时, 可读流的数据会被分块发给可写流(就像是水龙头直接和下水道连接了一样,水源源不断流通)。 可写流可以对数据(水)进行任意的处理。
在我们的例子中,我们把组件渲染出来的流(html片段)给了response,response会把片段发给请求的客户端(比如浏览器)。
(可以看到First Byte明显前移动了,说明流渲染更快;因为并行的原因,片段渲染也更快了)
在Node.js中缓存HTML
const originalSend = response.send;
response.send = (html,...other) => {
if (response.statusCode > 100 && response.statusCode < 300) cache.put(request.path,html)
originalSend(html,...other);
}
采用Express的中间件,提供缓存文件服务:
app.use('*',response,next) => {
if (cache.has(request.path)) {
cache.get(request.path).then(html => response.send(html));
} else {
next();
}
})
接下来的问题是:如何渲染HTML,当你直接把htm的可写流pipe给了response流。因为response.send 不会再被调用了,你也无法访问整个HTML文档。
transform streams是什么?它就像是一个水龙头的过滤器。可读流经过它转换后才会给可写流。
在我们这里,我们只是想要缓存HTML并不会转化数据,具体怎么做,看代码:
import { Transform } from 'stream';
const createCacheStream = (key) => {
const bufferedChunks = [];
return new Transform({
transform(data,enc,cb) {
bufferedChunks.push(data);
cb(null,data);
},
flush(cb) {
cache.set(key,Buffer.concat(bufferedChunks))
cb();
}
});
}
然后我们可以在我们的服务端渲染代码中使用它:
app.use('*',response) => {
let cacheStream = createCacheStream(request.path);
cacheStream.pipe(response);
cacheStream.write('<html><head><title>Page</title></head><body><div id="root">');
const renderStream = renderToNodeStream(<Frontend />); // 渲染流接上cacheStream renderStream.pipe(cacheStream,{ end: false }); renderStream.on('end',() => { // 一旦结束渲染则做剩余部分的输出 cacheStream.end('</div></body></html>'); }); })
以上就是主要内容
原文链接: https://zeit.co/blog/streaming-server-rendering-at-spectrum?nsukey=uW6M5M2uTd6F3Nc1u6tAVuAMGhbsBnpEuVplgUQLBlKevYZGGYN%2Bi1m6RocmDu%2F9367Zl71zSNn%2BkcWnaXcdX7bmeFLEyjbMMVsl5VtaBTfAvX8iNXgXMdbYWxVuuAealdKz8wkNfwEQtp4yLxknnredPgKISZUp%2BuR420YfPbpg8dW1lJMnpBMwNAJYMFZ0 作者知乎/公众号:前端疯 (编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|