marko/compiler/util/vdom/VDOMOptimizer.js

140 lines
3.8 KiB
JavaScript

'use strict';
/*
Algorithm:
Walk the DOM tree to find all HtmlElementVDOM and TextVDOM nodes
a) If a node is static then move to a static variable. Depending on whether or not the node is a root or nested,
we will need to replace it with one of the following:
- out.n(staticVar)
- .n(staticVar)
b) If a node is HTML-only then generate code depending on if it is root or not:
- out.e('div', ...) | out.t('foo')
- .e('div', ...) || .t('foo')
c) Else, generate one of the following:
- out.beginElement()
*/
const Node = require('../../ast/Node');
const nextConstIdFuncSymbol = Symbol();
const OPTIONS_DEFAULT = { optimizeTextNodes: true, optimizeStaticNodes: true };
const OPTIONS_OPTIMIZE_TEXT_NODES = { optimizeTextNodes: true, optimizeStaticNodes: false };
class NodeVDOM extends Node {
constructor(variableIdentifier) {
super('NodeVDOM');
this.variableIdentifier = variableIdentifier;
}
writeCode(writer) {
var builder = writer.builder;
let funcCall = builder.functionCall(
builder.identifier('n'),
[
this.variableIdentifier
]);
if (this.isChild) {
writer.write('.');
} else {
writer.write('out.');
}
writer.write(funcCall);
}
}
function generateNodesForArray(nodes, context, options) {
let builder = context.builder;
let nextNodeId = 0;
let nextAttrsId = 0;
var optimizeStaticNodes = options.optimizeStaticNodes !== false;
function generateStaticNode(node) {
if (node.type === 'HtmlElementVDOM') {
node.createElementId = context.helper('createElement');
}/* else {
node.createTextId = context.importModule('marko_createText', 'marko/vdom/createText');
}*/
let nextConstIdFunc = context.data[nextConstIdFuncSymbol];
if (!nextConstIdFunc) {
let constId = context.helper('const');
let fingerprintLiteral = builder.literal(context.getFingerprint(6));
nextConstIdFunc = context.data[nextConstIdFuncSymbol] = context.addStaticVar('marko_const_nextId', builder.functionCall(constId, [ fingerprintLiteral ]));
}
node.nextConstId = builder.functionCall(nextConstIdFunc, []);
node.isStaticRoot = true;
let staticNodeId = context.addStaticVar('marko_node' + (nextNodeId++), node);
return new NodeVDOM(staticNodeId);
}
function handleStaticAttributes(node) {
var attributesArg = node.attributesArg;
if (attributesArg) {
node.isStaticRoot = true;
let staticAttrsId = context.addStaticVar('marko_attrs' + (nextAttrsId++), attributesArg);
node.attributesArg = staticAttrsId;
}
}
let finalNodes = [];
let i = 0;
while (i<nodes.length) {
let node = nodes[i];
if (node.type === 'HtmlElementVDOM') {
if (optimizeStaticNodes) {
if (node.isStatic) {
finalNodes.push(generateStaticNode(node));
doOptimizeNode(node, context, OPTIONS_OPTIMIZE_TEXT_NODES);
} else {
if (node.isAttrsStatic) {
handleStaticAttributes(node);
}
finalNodes.push(node);
}
} else {
finalNodes.push(node);
}
} else {
finalNodes.push(node);
}
i++;
}
return finalNodes;
}
function doOptimizeNode(node, context, options) {
let walker = context.createWalker({
enterArray(nodes) {
return generateNodesForArray(nodes, context, options);
}
});
return walker.walk(node);
}
class VDOMOptimizer {
optimize(node, context) {
doOptimizeNode(node, context, OPTIONS_DEFAULT);
}
}
module.exports = VDOMOptimizer;