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.
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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
