jwon.js
// simple-reactive.js
(function() {
// 存储依赖关系
const deps = new WeakMap();
let currentComputation = null;
// 创建响应式对象
window.$ = new Proxy({}, {
get(target, key) {
// 依赖收集
if (currentComputation) {
if (!deps.has(target)) deps.set(target, {});
if (!deps.get(target)[key]) deps.get(target)[key] = new Set();
deps.get(target)[key].add(currentComputation);
}
return target[key];
},
set(target, key, value) {
target[key] = value;
// 触发更新
if (deps.has(target) && deps.get(target)[key]) {
deps.get(target)[key].forEach(fn => fn());
}
updateDOM();
return true;
}
});
// 更新DOM
function updateDOM() {
document.querySelectorAll('*').forEach(el => {
// 处理元素内容
if (el.innerHTML.includes('{{')) {
const original = el.getAttribute('data-original') || el.innerHTML;
el.setAttribute('data-original', original);
el.innerHTML = original.replace(/\{\{(.+?)\}\}/g, (_, expr) => {
try {
currentComputation = () => el.innerHTML = original.replace(/\{\{(.+?)\}\}/g, (_, e) => safeEval(e));
const result = safeEval(expr);
currentComputation = null;
return result ?? '';
} catch(e) {
return '';
}
});
}
// 处理属性
Array.from(el.attributes).forEach(attr => {
if (attr.value.includes('{{')) {
const original = el.getAttribute('data-original-' + attr.name) || attr.value;
el.setAttribute('data-original-' + attr.name, original);
const newValue = original.replace(/\{\{(.+?)\}\}/g, (_, expr) => safeEval(expr) ?? '');
if (el.getAttribute(attr.name) !== newValue) {
el.setAttribute(attr.name, newValue);
}
}
});
});
}
// 安全求值
function safeEval(expr) {
const keys = Object.keys(window.$);
const values = keys.map(k => window.$[k]);
return new Function(...keys, `return ${expr}`)(...values);
}
// 初始更新
updateDOM();
document.addEventListener('DOMContentLoaded', updateDOM);
new MutationObserver(updateDOM).observe(document, {
childList: true,
subtree: true
});
})();
index.html
<!DOCTYPE html>
<html>
<head>
<title>LightRouter Demo</title>
<meta charset="utf-8" name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" type="text/css" href="jwon.css">
<script src="jwon.js"></script>
</head>
<body>
<div>
<p>{{ $.name }}</p>
<p>{{ $.count * 2 }}</p>
<input value="{{ $.inputValue }}">
</div>
<script>
// 设置值会自动更新DOM
$.name = "John";
$.count = 5;
$.inputValue = "test";
</script>
</body>
</html>
为什么Proxy没有被触发