从renderToStaticMarkup入手优化React同构性能

先附上github地址 react-dom-faster

react-dom提供的几个同构接口:

1.renderToString:将React Component转化为HTML字符串,生成的HTML的DOM会带有额外属性:各个DOM会有data-react-id属性,第一个DOM会有data-checksum属性。

2.renderToStaticMarkup:同样是将React Component转化为HTML字符串,但是生成HTML的DOM不会有额外属性,从而节省HTML字符串的大小。

3.renderToNodeStream: 以的形式输出html, 不用像renderToString生成整个html才发送给客户端。相对于renderToString能更快的响应客户端,提升页面渲染速度。

4.renderToStaticNodeStream:和renderToNodeStream一样,也是输出流,但是html中不带data-reactroot等属性。

在最近hybrid业务开发中,采用ssr方案。由于我们不是页面直出,是ajax数据直出,renderToNodeStream/renderToStaticNodeStream不适合我们的业务形态。所以选择了更快的 renderToStaticMarkup。本文的主题也是从renderToStaticMarkup入手来提升ssr渲染速度。

SSR基本原理

1.jsx通过babel转化后调用的React.createElement(这里可以修改@babel/plugin-transform-react-jsx的pragma来自定义调用函数名)

2.React.createElement生成就是vdom对象

{
  type: 'ul',
  props: {
     class: 'container'
  },
  children: [
    {type: 'li', props: {}, children: ['1']},
    {type: 'li', props: {}, children: ['2']},
    {type: 'li', props: {}, children: ['3']}
  ]
}
复制代码

3.renderToStaticMarkup调用component方法得到vdom,遍历这棵vdom树递归生成html进行拼接。

对于renderToStaticMarkup而言,不需要生成data-checksum、data-react-id等属性,所以vdom是多余且消耗性能的一步。

优化方向

可以拦截React.createElement提供自定义的render函数,让React.createElement生成的就是html,我们只需要对html进行拼接即可

export default function fastRenderToStaticMarkup (renderComponent) {
    React.createElement = h;
    const html = renderComponent();
    React.createElement = oldH;
    if (!html) {
        return '';
    } else {
        return html.hasOwnProperty('html') ? html.html : html;
    }
}
复制代码

h函数实现如下:

const h = function (type, attrs, ...children) {
    attrs = attrs || {};
    // dom element
    if (typeof type === 'string') {
        let html = '<' + type + getStaticMarkupAttrStr(attrs);
        if (!emptyTags[type]) {
            html += '>';
            if (attrs.hasOwnProperty('dangerouslySetInnerHTML')) {
                html += attrs.dangerouslySetInnerHTML.__html;
            }
            html += hChildren(children);
            html += '</' + type + '>';
        } else {
            html += '/>';
        }
        return { html };
    }

    // support React.Fragment
    if (type === React.Fragment) {
        return { html: hChildren(children) };
    }

    // support class component
    if (isReactComponent(type)) {
        const props = {
            ...(type.defaultProps || {}),
            ...attrs,
            children: children
        };
        const instance = new type(props);
        instance.props = props;
        instance.componentWillMount && instance.componentWillMount();
        instance.UNSAFE_componentWillMount && instance.UNSAFE_componentWillMount();
        return instance.render();
    }

    // support function component
    if (typeof type === 'function') {
        const props = {
            ...(type.defaultProps || {}),
            ...attrs,
            children: children
        };
        return type(props);
    }
};
复制代码

通过拦截,使用自定的h函数保证React.createElement生成是string, 省去了vdom的中间产物。这个模块已经在业务中落地,相比react-dom renderToStaticMarkup渲染时间提升了6倍,极大的提升了node的吞吐量。

具体实现可以参考react-dom-faster

转载于:https://juejin.im/post/5cc7c12b6fb9a031f525d20c

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值