How We Reduced Server CPU Usage by 20% with Vue Render Function Optimizations

This article details how the MPM page rendering service at JD.com was profiled with v8‑profiler, identified heavy replace and compile calls, and applied string‑splitting, Vue transformCode hooks, and a custom lightweight compiler to cut CPU consumption by roughly 20%, improving stability during traffic spikes.

WecTeam
WecTeam
WecTeam
How We Reduced Server CPU Usage by 20% with Vue Render Function Optimizations

Problem Background

MPM (Market Page Maker) is JD.com’s component‑based visual page builder launched in September 2016. It uses Vue.js for components and a Node.js direct‑output service. Sudden traffic spikes caused CPU usage to exceed 80%, prompting a scaling request and raising the question of whether the service could be optimized to handle such loads without scaling.

Analysis Method and Issues

The team used the v8‑profiler NPM package to collect CPU profiles for five minutes, exported the data to Speedscope , and examined the flame graph. The most time‑consuming functions were replace , compile , and Vue rendering.

1) replace function call analysis

Code iterates over MPM_COM_STYLE_MAP, builds a code string, and performs two replace operations: one for UID placeholders and one for component template placeholders. The first replace runs many times on large strings, becoming a performance bottleneck.

Object.keys(MPM_COM_STYLE_MAP).forEach(function(comId){
  var styleKey = MPM_COM_STYLE_MAP[comId];
  var code = '';
  if(hideComIds.indexOf(comId)!=-1){
    code = HIDE_TPL;
  }else if(loadingComs.indexOf(comId)!=-1){
    code = LOADING_TPL;
  }else if(MPM_STYLE_TPL_MAP[styleKey]){
    // first replace
    code = MPM_STYLE_TPL_MAP[styleKey].replace(/__uid__replace__str__/g, comId);
  }else{
    console.error('最终替换,发现无模板组件', comId);
  }
  if(code){
    // second replace
    compileTpl = compileTpl.replace(`_v("__vue__${comId}__replace__")`, code);
  }
});

2) UID replacement optimization

Replacing a placeholder in a large string repeatedly is slower than splitting the string once and joining it. A benchmark showed the "array‑join" method was roughly half the time of the regex replace.

const exeCount = 10000000;
const str = `... long template string ...`;
// regex replace
for(let i=0;i<exeCount;i++){
  str.replace(/__uid__replace__str__/g, 'com_1001');
}
// array join
const segs = str.split('__uid__replace__str__');
for(let i=0;i<exeCount;i++){
  segs.join('com_1001');
}

Result: array join consistently faster (≈0.5‑0.6× the time of regex).

3) Component template replacement optimization

Instead of a second regex replace for each component, the team leveraged Vue’s hidden transformCode hook. By compiling a dummy <div> template with a custom transformCode that swaps the placeholder id with the pre‑compiled component template, the second replace was eliminated.

var compileTpl = compiler.compile(`<div>${html}</div>`, {
  modules: {
    transformCode: function(el, code){
      if(el.attrsMap && el.attrsMap['id'] && el.attrsMap['id'].match(/__vue__com_\d{4,5}__replace__/)){
        var comId = el.attrsMap['id'].match(/com_\d{4,5}/)[0];
        var styleTemplate = compiledComTplMap[comId];
        return styleTemplate;
      }
      return code;
    }
  }
}).staticRenderFns.toString();

This removed the heavy second replace, leaving only the UID replacement.

4) UID replacement using array‑join

The first replace was rewritten to use the split‑join technique, cutting its cost by about half.

5) compile function call optimization

The remaining heavy operation was Vue’s compile on simple container templates. Observing that these templates only generate static div elements with fixed attributes, a lightweight custom compiler was written to produce the same render function code without invoking Vue’s full compiler.

function simpleCompile(comList){
  function genTree(tree){
    var html = '';
    for(var i=0, len=tree.length; i<len; i++){
      var node = tree[i];
      var comId = node.id;
      html += `_c('div',{attrs:{"id":"${comId}_con"}},[`;
      html += compiledComTplMap[comId] + `])`;
      if(node.child && node.child.length){
        html += `_c('div',{staticClass:"childBox",attrs:{"mpm_edit_child_box":"","tabpid":"${comId}"}},[` + genTree(node.child) + `])`;
      }
      html += (i===len-1)?'' : ',';
    }
    return html;
  }
  return genTree(comList);
}

Replacing Vue’s compile with this function eliminated the compile‑time hotspot.

Other Optimizations Explored

The team also investigated faster JSON parsing (large PageData objects from Redis) and string‑based template rendering as alternatives to Vue’s virtual DOM, but found native JSON.parse still fastest and the component model made pure string rendering impractical.

Conclusion

By applying the array‑join method for UID replacement, using Vue’s transformCode hook to skip the second replace, and implementing a custom lightweight compiler to avoid Vue’s heavy compile call, CPU consumption was reduced by roughly 20%. This not only saved resources but also lowered the need for emergency scaling during traffic spikes.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Performance OptimizationNode.jsVue.jsCPU profilingServer Rendering
WecTeam
Written by

WecTeam

WecTeam (维C团) is the front‑end technology team of JD.com’s Jingxi business unit, focusing on front‑end engineering, web performance optimization, mini‑program and app development, serverless, multi‑platform reuse, and visual building.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.