magix
Version:
view manager framewrok
436 lines (427 loc) • 19.6 kB
JavaScript
let V_SPECIAL_PROPS = {
input: [G_VALUE, 'checked'],
textarea: [G_VALUE],
option: ['selected']
};
let V_TEXT_NODE = G_COUNTER;
if (DEBUG) {
V_TEXT_NODE = '#text';
}
let V_UnmountVframs = (vf, n) => {
if (n.nodeType == 1) {
let id = IdIt(n);
if (vf['@{vframe#children}'][id]) {
vf.unmountVframe(id, 1);
} else {
vf.unmountZone(id, 1);
}
}
};
let V_NSMap = {
svg: 'http://www.w3.org/2000/svg',
math: 'http://www.w3.org/1998/Math/MathML'
};
let V_IgnoreKeys = {
mxv: 1,
mxa: 1,
mxs: 1
};
let V_SetAttributes = (oldNode, lastVDOM, newVDOM, ref) => {
let key, value,
nMap = newVDOM['@{~v#node.attrs.map}'],
oMap = lastVDOM['@{~v#node.attrs.map}'];
if (lastVDOM) {
for (key in oMap) {
if (!G_Has(nMap, key)) {//如果旧有新木有
if (key == 'id') {
ref.d.push([oldNode, G_EMPTY]);
} else {
ref.c = 1;
oldNode.removeAttribute(key);
}
}
}
}
for (key in nMap) {
if (!G_Has(V_IgnoreKeys, key)) {
value = nMap[key];
//旧值与新值不相等
if (!lastVDOM || oMap[key] !== value) {
if (key == 'id') {
ref.d.push([oldNode, value]);
} else {
ref.c = 1;
oldNode.setAttribute(key, value);
}
}
}
}
};
let V_SpecialDiff = (oldNode, lastVDOM, newVDOM) => {
let tag = lastVDOM['@{~v#node.tag}'], c, now;
let specials = V_SPECIAL_PROPS[tag];
let nMap = newVDOM['@{~v#node.attrs.map}'];
let result = 0;
if (specials) {
for (c of specials) {
now = G_Has(nMap, c) ? c != G_VALUE || nMap[c] : c == G_VALUE && G_EMPTY;
if (oldNode[c] != now) {
result = 1;
oldNode[c] = now;
}
}
}
return result;
};
let V_CreateNode = (vnode, owner, ref) => {
let tag = vnode['@{~v#node.tag}'], c;
if (tag == V_TEXT_NODE) {
c = G_DOCUMENT.createTextNode(vnode['@{~v#node.outer.html}']);
} else {
c = G_DOCUMENT.createElementNS(V_NSMap[tag] || owner.namespaceURI, tag);
/*#if(modules.viewChildren){#*/
if (vnode['@{~v#node.attrs.map}'][G_MX_VIEW]) {
c['@{node#vnode}'] = vnode;
}
/*#}#*/
V_SetAttributes(c, 0, vnode, ref);
for (tag of vnode['@{~v#node.children}']) {
c.appendChild(V_CreateNode(tag, owner, ref));
}
}
return c;
};
/*let V_GenKeyedNodes = (vnodes, nodes, start, end) => {
let keyed = {}, i = end, v, key;
for (; i >= start; i--) {
v = vnodes[i];
key = v['@{~v#node.compare.key}'];
if (key) {
key = keyed[key] || (keyed[key] = []);
key.push({
'@{~v#old.list.node}': nodes[i],
'@{~v#old.vlist.node}': v
});
}
}
return keyed;
};*/
let V_SetChildNodes = (realNode, lastVDOM, newVDOM, ref, vframe, keys) => {
if (lastVDOM) {//view首次初始化,通过innerHTML快速更新
if (lastVDOM['@{~v#node.has.mxv}'] ||
lastVDOM['@{~v#node.html}'] != newVDOM['@{~v#node.html}']) {
let i, oi = 0,
oldChildren = lastVDOM['@{~v#node.children}'],
newChildren = newVDOM['@{~v#node.children}'], oc, nc,
oldCount = oldChildren.length, newCount = newChildren.length,
reused = newVDOM['@{~v#node.reused}'],
nodes = realNode.childNodes, compareKey,
keyedNodes = {};
for (i = oldCount; i--;) {
oc = oldChildren[i];
compareKey = oc['@{~v#node.compare.key}'];
if (compareKey) {
compareKey = keyedNodes[compareKey] || (keyedNodes[compareKey] = []);
compareKey.push(nodes[i]);
}
}
/* let oldStartIdx = 0,
oldEndIdx = oldCount - 1,
newStartIdx = 0,
newEndIdx = newCount - 1,
oldStartVNode = oldChildren[oldStartIdx],
oldEndVNode = oldChildren[oldEndIdx],
newStartVNode = newChildren[newStartIdx],
newEndVNode = newChildren[newEndIdx];
while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
if (newStartVNode['@{~v#node.compare.key}'] == oldStartVNode['@{~v#node.compare.key}']) {
V_SetNode(nodes[newStartIdx], realNode, oldStartVNode, newStartVNode, ref, vframe, data);
newStartVNode = newChildren[++newStartIdx];
oldStartVNode = oldChildren[++oldStartIdx];
} else if (newEndVNode['@{~v#node.compare.key}'] == oldEndVNode['@{~v#node.compare.key}']) {
V_SetNode(nodes[newEndIdx], realNode, oldEndVNode, newEndVNode, ref, vframe, data);
newEndVNode = newChildren[--newEndIdx];
oldEndVNode = oldChildren[--oldEndIdx];
} else {
if (!keyedNodes) keyedNodes = V_GenKeyedNodes(oldChildren, nodes, oldStartIdx, oldEndIdx);
}
}
if (newStartIdx > newEndIdx) {
for (i = oldStartIdx; i <= oldEndIdx; i++) {
oi = nodes[oldStartIdx];//删除多余的旧节点
V_UnmountVframs(vframe, oi);
realNode.removeChild(oi);
ref.c = 1;
}
}*/
/*#if(modules.updaterAsync){#*/
for (i = 0; i < newCount; i++) {
oc = oldChildren[i];
nc = newChildren[i];
compareKey = keyedNodes[nc['@{~v#node.compare.key}']];
if (compareKey && (compareKey = compareKey.pop())) {
//orn = compareKey;//['@{~v#old.list.node}'];
//ovn = compareKey['@{~v#old.vlist.node}'];
while (compareKey != nodes[i]) {//如果找到的节点和当前不同,则移动
//oldChildren.splice(i, 0, oc = ovn);//移动虚拟dom
// for (oi = oldChildren.length; oi--;) {//从后向前清理虚拟dom
// if (oldChildren[oi] == ovn) {
// oldChildren.splice(oi, 1);
// break;
// }
// }
realNode.appendChild(nodes[i]);
oldChildren.push(oldChildren[i]);
oldChildren.splice(i, 1);
oc = oldChildren[i];
}
if (reused[oc['@{~v#node.compare.key}']]) {
reused[oc['@{~v#node.compare.key}']]--;
}
Async_AddTask(vframe, V_SetNode, nodes[i], realNode, oc, nc, ref, vframe, keys);
} else if (oc) {//有旧节点,则更新
if (keyedNodes[oc['@{~v#node.compare.key}']] &&
reused[oc['@{~v#node.compare.key}']]) {
oldChildren.splice(i, 0, nc);//插入一个占位符,在接下来的比较中才能一一对应
oldCount++;
ref.c = 1;
//ref.n.push([8, realNode, V_CreateNode(nc, realNode, ref), nodes[i]]);
realNode.insertBefore(V_CreateNode(nc, realNode, ref), nodes[i]);
} else {
Async_AddTask(vframe, V_SetNode, nodes[i], realNode, oc, nc, ref, vframe, keys);
}
} else {//添加新的节点
oldChildren.push(nc);
ref.n.push([1, realNode, V_CreateNode(nc, realNode, ref)]);
//realNode.appendChild(V_CreateNode(nc, realNode, ref));
ref.c = 1;
}
}
oi = oldCount - newCount;
if (oi > 0) {
oldChildren.splice(-oi);
ref.c = 1;
}
/*#}else{#*/
for (i = 0; i < newCount; i++) {
nc = newChildren[i];
oc = oldChildren[i];
compareKey = keyedNodes[nc['@{~v#node.compare.key}']];
if (compareKey && (compareKey = compareKey.pop())) {
while (compareKey != nodes[i]) {//如果找到的节点和当前不同,则移动
//oldChildren.splice(i, 0, oc = ovn);//移动虚拟dom
// for (oi = oldChildren.length; oi--;) {//从后向前清理虚拟dom
// if (oldChildren[oi] == ovn) {
// oldChildren.splice(oi, 1);
// break;
// }
// }
realNode.appendChild(nodes[i]);
oldChildren.push(oldChildren[i]);
oldChildren.splice(i, 1);
oc = oldChildren[i];
}
if (reused[oc['@{~v#node.compare.key}']]) {
reused[oc['@{~v#node.compare.key}']]--;
}
V_SetNode(nodes[i], realNode, oc, nc, ref, vframe, keys);
} else if (oc) {//有旧节点,则更新
if (keyedNodes[oc['@{~v#node.compare.key}']] &&
reused[oc['@{~v#node.compare.key}']]) {
oldChildren.splice(i, 0, nc);//插入一个占位符,在接下来的比较中才能一一对应
oldCount++;
ref.c = 1;
ref.n.push([8, realNode, V_CreateNode(nc, realNode, ref), nodes[i]]);
// realNode.insertBefore(V_CreateNode(nc, realNode, ref), nodes[i]);
} else {
V_SetNode(nodes[i], realNode, oc, nc, ref, vframe, keys);
//ref.c = 1;
}
} else {//添加新的节点
ref.n.push([1, realNode, V_CreateNode(nc, realNode, ref)]);
//realNode.appendChild(V_CreateNode(nc, realNode, ref));
ref.c = 1;
}
}
/*#}#*/
for (i = newCount; i < oldCount; i++) {
oi = nodes[i];//删除多余的旧节点
V_UnmountVframs(vframe, oi);
if (DEBUG) {
if (!oi.parentNode) {
console.error('Avoid remove node on view.destroy in digesting');
}
}
ref.n.push([2, realNode, oi]);
//realNode.removeChild(oi);
}
}
/*#if(modules.updaterAsync){#*/
if (lastVDOM['@{~v#node.outer.html}'] != newVDOM['@{~v#node.outer.html}']) {
V_CopyVNode(lastVDOM, newVDOM);
}
/*#}#*/
} else {
ref.c = 1;
lastVDOM = V_CreateNode(newVDOM, realNode, ref);
realNode.innerHTML = '';
while (lastVDOM.firstChild) {
realNode.appendChild(lastVDOM.firstChild);
}
}
};
/*#if(modules.updaterAsync){#*/
let V_CopyVNode = (lastVDOM, newVDOM, withChildren, p) => {
for (p in lastVDOM) {
if (withChildren || p != '@{~v#node.children}') {
delete lastVDOM[p];
}
}
for (p in newVDOM) {
if (withChildren || p != '@{~v#node.children}') {
lastVDOM[p] = newVDOM[p];
}
}
};
/*#}#*/
let V_SetNode = (realNode, oldParent, lastVDOM, newVDOM, ref, vframe, keys) => {
if (DEBUG) {
if (oldParent.nodeName == 'TEMPLATE') {
console.error('unsupport template tag');
}
if ((realNode.nodeName == '#text' && lastVDOM['@{~v#node.tag}'] != '#text') || (
realNode.nodeName != '#text' && realNode.nodeName.toLowerCase() != lastVDOM['@{~v#node.tag}'])) {
console.error('Your code is not match the DOM tree generated by the browser. near:' + lastVDOM['@{~v#node.html}'] + '. Is that you lost some tags or modified the DOM tree?');
}
}
let lastAMap = lastVDOM['@{~v#node.attrs.map}'],
newAMap = newVDOM['@{~v#node.attrs.map}'];
if (V_SpecialDiff(realNode, lastVDOM, newVDOM) ||
lastVDOM['@{~v#node.has.mxv}'] ||
lastVDOM['@{~v#node.outer.html}'] != newVDOM['@{~v#node.outer.html}']) {
if (lastVDOM['@{~v#node.tag}'] == newVDOM['@{~v#node.tag}']) {
if (lastVDOM['@{~v#node.tag}'] == V_TEXT_NODE) {
ref.c = 1;
/*#if(modules.updaterAsync){#*/
lastVDOM['@{~v#node.outer.html}'] = newVDOM['@{~v#node.outer.html}'];
/*#}#*/
realNode.nodeValue = newVDOM['@{~v#node.outer.html}'];
} else if (!lastAMap[G_Tag_Key] ||
lastAMap[G_Tag_Key] != newAMap[G_Tag_Key]) {
let newMxView = newAMap[G_MX_VIEW],
newHTML = newVDOM['@{~v#node.html}'];
let updateAttribute = lastVDOM['@{~v#node.attrs}'] != newVDOM['@{~v#node.attrs}'],
updateChildren, unmountOld,
oldVf = Vframe_Vframes[realNode.id],
assign,
view,
uri = newMxView && G_ParseUri(newMxView),
params,
htmlChanged,
paramsChanged;
/*
如果存在新旧view,则考虑路径一致,避免渲染的问题
*/
/*
只检测是否有参数控制view而不检测数据是否变化的原因:
例:view内有一input接收传递的参数,且该input也能被用户输入
var d1='xl';
var d2='xl';
当传递第一份数据时,input显示值xl,这时候用户修改了input的值且使用第二份数据重新渲染这个view,问input该如何显示?
*/
//旧节点有view,新节点有view,且是同类型的view
if (newMxView && oldVf &&
oldVf['@{vframe#view.path}'] == uri[G_PATH] &&
lastAMap.id == newAMap.id &&//id如果不一样也要销毁,只有id同时存在且相同或同时不存在id才可以
(view = oldVf['@{vframe#view.entity}'])) {
htmlChanged = newHTML != lastVDOM['@{~v#node.html}'];
paramsChanged = newMxView != oldVf[G_PATH];
assign = lastAMap[G_Tag_View_Key];
if (!htmlChanged && !paramsChanged && assign) {
params = assign.split(G_COMMA);
/*#if(modules.vframeHost){#*/
lastAMap = Updater_ChangedKeys[oldVf.hId || oldVf.pId];
/*#}#*/
for (assign of params) {
if (assign == G_HashKey || G_Has(keys, assign)/*#if(modules.vframeHost){#*/ || G_Has(lastAMap, assign)/*#}#*/) {
paramsChanged = 1;
break;
}
}
}
if (paramsChanged || htmlChanged /*#if(modules.updaterTouchAttr){#*/ || updateAttribute/*#}#*/) {
assign = view['@{view#rendered}'] && view['@{view#assign.fn}'];
//如果有assign方法,且有参数或html变化
if (assign) {
params = uri[G_PARAMS];
//处理引用赋值
/*#if(modules.viewChildren){#*/lastAMap = /*#}#*/Vframe_TranslateQuery(/*#if(modules.vframeHost){#*/oldVf.hId ||/*#}#*/oldVf.pId, newMxView, params);
//oldVf['@{vframe#template}'] = newHTML;
//oldVf['@{vframe#data.stringify}'] = newDataStringify;
oldVf[G_PATH] = newMxView;//update ref
//如果需要更新,则进行更新的操作
uri = {
//node: newVDOM,//['@{~v#node.children}'],
//html: newHTML,
//mxv: hasMXV,
node: realNode,
attr: updateAttribute,
/*#if(modules.viewChildren){#*/
vnode: newVDOM,
map: Children_Wrap(newVDOM, lastAMap),
/*#}#*/
//html: newHTML,
deep: !view.tmpl,
inner: htmlChanged,
query: paramsChanged,
keys
};
//updateAttribute = 1;
if (G_ToTry(assign, [params, uri], view)) {
ref.v.push(view);
}
//默认当一个组件有assign方法时,由该方法及该view上的render方法完成当前区域内的节点更新
//而对于不渲染界面的控制类型的组件来讲,它本身更新后,有可能需要继续由magix更新内部的子节点,此时通过deep参数控制
updateChildren = uri.deep;
} else {
unmountOld = 1;
updateChildren = 1;
}
}// else {
// updateAttribute = 1;
//}
} else {
updateChildren = 1;
unmountOld = oldVf;
}
if (unmountOld) {
ref.c = 1;
oldVf.unmountVframe(0, 1);
}
if (updateAttribute) {
V_SetAttributes(realNode, lastVDOM, newVDOM, ref);
}
// Update all children (and subchildren).
//自闭合标签不再检测子节点
if (updateChildren &&
!(newVDOM['@{~v#node.self.close}'] &&
lastVDOM['@{~v#node.self.close}'])) {
//ref.c = 1;
V_SetChildNodes(realNode, lastVDOM, newVDOM, ref, vframe, keys);
} else {
/*#if(modules.updaterAsync){#*/
V_CopyVNode(lastVDOM, newVDOM);
/*#}#*/
}
}
} else {
/*#if(modules.updaterAsync){#*/
//V_CopyVNode(lastVDOM, newVDOM, 1);
/*#}#*/
V_UnmountVframs(vframe, realNode);
ref.n.push([4, oldParent, V_CreateNode(newVDOM, oldParent, ref), realNode/*#if(modules.updaterAsync){#*/, lastVDOM, newVDOM/*#}#*/]);
//oldParent.replaceChild(V_CreateNode(newVDOM, oldParent, ref), realNode);
ref.c = 1;
}
}
};