服务端渲染性能浅析
服务端渲染性能浅析
前几日笔者在服务端渲染性能大乱斗:Vue, React, Preact, Rax, Marko
笔者看完这个数据对比之后不由好奇,缘何renderToString
的函数实现,其紧耦合于
/** The default export is an alias of `render()`. */
export default function renderToString(vnode, context, opts, inner, isSvgMode) {
// 获取节点属性
let { nodeName, attributes, children } = vnode || EMPTY,
isComponent = false;
context = context || {};
opts = opts || {};
let pretty = opts.pretty,
indentChar = typeof pretty === "string" ? pretty : "\t";
if (vnode == null) {
return "";
} // 字符串类型则直接返回
if (!nodeName) {
return encodeEntities(vnode);
} // 处理组件
if (typeof nodeName === "function") {
isComponent = true;
if (opts.shallow && (inner || opts.renderRootComponent === false)) {
nodeName = getComponentName(nodeName);
} else {
// ...
if (
!nodeName.prototype ||
typeof nodeName.prototype.render !== "function"
) {
// 处理无状态函数式组件
// ...
} else {
// 处理类组件
// ...
} //递归处理下一层元素
return renderToString(
rendered,
context,
opts,
opts.shallowHighOrder !== false
);
}
} // 将 JSX 渲染到 HTML
let s = "",
html;
if (attributes) {
let attrs = objectKeys(attributes); //处理所有元素属性
// ...
} // 处理多行属性 // ...
if (html) {
// 处理多行缩进
// ...
} else {
// 递归处理子元素
// ...
}
// ...
return s;
}
if (process.env.NODE_ENV !== 'production') {
...
}
显而易见如果我们没有将环境变量设置为production
,势必会在运行时调用更多的调试代码,拖慢整体性能。另一个有可能拖慢服务端渲染性能的因素是
<div data-reactroot="" data-reactid="1" data-react-checksum="-492408024">
...
</div>
上述代码中的data-react-checksum
就是计算而来的校验和,该计算过程是会占用部分时间,不过影响甚微。笔者对于renderToStringImpl
函数进行了断点性能分析,主要是利用console.time
记录函数执行时间并且进行对比:
...
return transaction.perform(function () {
var componentInstance = instantiateReactComponent(element, true);
var reactDOMContainerInfo = ReactDOMContainerInfo();
console.time('transaction');
console.log('transaction 开始:' + Date.now());
var markup = ReactReconciler.mountComponent(componentInstance, transaction, null, reactDOMContainerInfo, emptyObject, 0 /* parentDebugID */
);
console.log('transaction 结束:' + Date.now());
console.timeEnd('transaction');
...
if (!makeStaticMarkup) {
console.time('markup');
markup = ReactMarkupChecksum.addChecksumToMarkup(markup);
console.timeEnd('markup');
}
return markup;
...
// 运行结果为:
// transaction: 12.643ms
// markup: 0.249ms
从运行结果上可以看出,计算校验和并未占用过多的时间比重,因此这也不会是拖慢服务端渲染性能的主因。实际上当我们调用ReactDOMServer.renderToString
时,其会调用ReactServerRendering.renderToStringImpl
这个内部实现,该函数的第二个参数makeStaticMarkup
用来标识是否需要计算校验和。换言之,如果我们使用的是ReactDOMServer.renderToStaticMarkup
,其会将makeStaticMarkup
设置为true
并且不计算校验和。完整的一次服务端渲染的对象与函数调用流程如下:
整个流程同样是递归解析组件树到mountComponent
函数之间间隔