实现一个数据驱动的Web前端框架(for指令尚未完成)
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>View</title>
</head>
<body>
<div id="app" y-if="is">
<h1 :style="color">Hello MVVM</h1>
<p>请欣赏此句:{{title}}</p>
<div :innerhtml="html"></div>
<img :src="img" alt="" style="width: 60px;height: 60px;">
<br>
<button onclick="changeImg()">改变图片</button>
<br><br>
<button onclick="changeHTML()">改变html</button>
<br><br>
<button onclick="changeColor()">改变Hello MVVM的字体颜色</button>
<br><br>
<button onclick="changeText()">改变'橘皮安知橘瓣之酸甜'文字</button>
<br><br>
<button onclick="removeApp()">移除所有元素 三秒之后重新加入dom</button>
</div>
<script src="view.js"></script>
<script>
let data = { // 这里存放所有的响应式数据
is: true, // 控制id为app的div的隐藏显示
img: '1.jpg', // 控制图片
html: '<h6>h6标签</h6>', // 控制html
title: '橘皮安知橘瓣之酸甜', // 控制p标签显示的内容
color: 'color: red;' // 控制h1标签的style属性
};
/**
* 调用框架提供的MVVM方法 传入响应式数据 和 框架要接管控制的区域元素
* 该方法返回一个函数 在你对响应式数据进行修改之后 要调用此函数通知框架进行数据更改
* 原理和angular一样都用的脏值检查 只是我没有使用zone.js对所有异步进行包裹 所以需要手动调用change方法
* angular如果关闭zonejs 也需要手动调用tick方法
*/
let change = MVVM(data, document.getElementById('app'));
function changeColor() {
data.color = 'color: green;';
change();
}
function changeHTML() {
data.html = '<h1>h1标签</h1>';
change();
}
function changeText() {
data.title = '橘瓣难解橘皮之苦涩';
change();
}
function removeApp() {
data.is = false;
change();
setTimeout(() => {
data.is = true;
change();
}, 3000);
}
function changeImg() {
data.img = '2.jpg';
change();
}
</script>
</body>
</html>
JS
/** * 这是一个js的模版语法库,力图使用模版语法进行web开发 * 支持的语法如下: * 数据绑定:属性绑定 前面加冒号: (已完成,特别对class和innerhtml做了增强,使用时直接:class :innerhtml即可) * 数据绑定:双花括号语法 {{}} (已完成) * if指令:y-if 判断值为true时显示元素,为false移除元素 (已完成) * for指令:y-for 迭代生成元素 item in list (未完成) */ function MVVM(bindData, e) { ;(function(data) { let keys = Object.keys(data); for (let i = 0, len = keys.length; i < len; ++i) { data['_y_' + keys[i]] = !data[keys[i]]; } })(bindData); function getElementList(e) { let result = []; let eList = [e]; while (eList.length) { let _eList = []; for (let i = 0, len = eList.length; i < len; ++i) { result.push(eList[i]); let temp = eList[i].childNodes; for (let j = 0, lens = temp.length; j < lens; ++j) { let item = temp[j]; let type = item.nodeType; if (type == 1) { // 元素节点 _eList.push(item); } else if (type == 3) { // 文本节点 if (item.nodeValue.trim() != '') { // 如果不是空白节点 result.push(item); } } else if (type == 8) { // 注释节点 // 暂时不做处理 } else { // 其他节点不做处理 } } } eList = _eList; } return result; } function parseText (e) { let result = []; let value = e.nodeValue; let mustache = new RegExp(/{{.*?}}/g); let arr1 = value.match(mustache); if (arr1 == null) { return null; } for (let i = 0, len = arr1.length; i < len; ++i) { arr1[i] = arr1[i].substr(2, arr1[i].length - 4); } value = value.replace(mustache, '$'); let arr2 = value.split('$'); if (arr2[0] == '') { arr2.length = arr2.length - 1; for (let i = 0, len = arr1.length; i < len; ++i) { result.push({ isStr: false, value: arr1[i] }); result.push({ isStr: true, value: arr2[i] }); } } else { if (arr2[arr2.length - 1].trim() == '') { arr2.length = arr2.length - 1; } [arr1, arr2] = [arr2, arr1]; for (let i = 0, len = arr1.length; i < len; ++i) { result.push({ isStr: true, value: arr1[i] }); result.push({ isStr: false, value: arr2[i] }); } } return { type: 'text', e: e, value: result, old: '' }; } function parseElement (e) { let attributes = e.attributes; let result = []; for (let i = 0, len = attributes.length; i < len; ++i) { let temp = attributes[i]; if (temp.nodeName[0] == ':') { // 属性绑定 let attrName = temp.nodeName.substr(1); attrName == 'class' && (attrName = 'className'); attrName == 'innerhtml' && (attrName = 'innerHTML'); result.push({ type: 'attr', e: e, attrName: attrName, value: temp.nodeValue }); continue; } if (temp.nodeName == 'y-if') { // if指令 let div = document.createElement('div'); e.insertAdjacentElement('beforebegin', div); e.parentNode.removeChild(e); result.push({ type: 'y-if', e: e, location: div, value: temp.nodeValue }); continue; } if (temp.nodeName == 'y-for') { // for指令 continue; } } return result; } function parseTemplate(list) { let result = []; for (let i = 0, len = list.length; i < len; ++i) { if (list[i].nodeType == 1) { // 若为元素节点 let temp = parseElement(list[i]); for (let i = 0, len = temp.length; i < len; ++i) { result.push(temp[i]); } } else { // 若为文本节点 let temp = parseText(list[i]); if (temp != null) { result.push(temp); } } } return result; } let mapping = parseTemplate(getElementList(e)); // 保存所有的模版映射关系 function change() { // 脏检查 for (let i = 0, len = mapping.length; i < len; ++i) { let tempE = mapping[i]; if (tempE.type == 'text') { // 如果是文本节点(处理mustache语法) let value = ''; let temp = mapping[i].value; for (let j = 0, lens = temp.length; j < lens; ++j) { if (temp[j].isStr) { value += temp[j].value; } else { value += bindData[temp[j].value]; } } if (mapping[i].old != value) { mapping[i].old = mapping[i].e.nodeValue = value; } } else if (tempE.type == 'attr') { // 如果是元素节点(处理属性绑定) let _new = bindData[tempE.value]; if (bindData['_y_' + tempE.value] != _new) { bindData['_y_' + tempE.value] = tempE.e[tempE.attrName] = _new; } } else if (tempE.type == 'y-if') { // 如果是元素节点(处理y-if指令) let _new = bindData[tempE.value]; if (bindData['_y_' + tempE.value] != _new) { bindData['_y_' + tempE.value] = _new; if (_new) { tempE.location.insertAdjacentElement('afterend', tempE.e); } else { tempE.e.parentNode.removeChild(tempE.e); } } } else if (tempE.type == 'y-for') { // 如果是元素节点(处理y-for指令) } else {} } } change(); return change; }

浙公网安备 33010602011771号