Skip to content

Reactive Bootstrap 从 0 到 1

Talk is Cheap, Show Me the Code.

index.hml

<div id="app"></div>
<script src="app.js"><script>

reactive.js

/* reactive module */
let watchingFn = null;

function observe(data) {
  // 收集 data 各个字段的监听函数
  const depends = {};

  return new Proxy(data, {
    get(obj, key) {
      if (watchingFn) {
        if (!depends[key]) {
          depends[key] = [];
        }
        depends[key].push(watchingFn); // 注册
      }
      return obj[key];
    },
    set(obj, key, value) {
      obj[key] = value;
      if (depends[key]) {
        depends[key].forEach(fn => fn()); // 执行
      }
    }
  });  
}

// 注册函数 fn 到 depends[key] 中,并执行函数
function watcher(fn) {
  watchingFn = fn;
  fn();
  watchingFn = null;
}

App.js

// --- App ---
// 1. 拦截 data, 并返回 Proxy
const data = observe({
  count: 10,
  doubleCount: 10 * 2
});

// 2. 注册依赖
// 调用 count 属性的 getter 方法,
// 将匿名函数注册到 depends[key] 中
watcher(() => {
  data.doubleCount = data.count * 2;
});

// 3. 声明 render,并注册 render 到所引用的字段的 depends 中
function render() {
  document.getElementById('app').innerHTML = `
    <div>Count: ${data.count}</div>
    <div>DoubleCount: ${data.doubleCount}</div>
    <div>
      <button @click="increment">+ increment</button>
      <button @click="decrement">- decrement</button>
    </div>
  `;
}

// 调用 count 和 doubleCount 属性的 getter 方法,
// 将 render 函数分别注册到 对应depends[key] 中
watcher(render); 

// 4. 测试一下
data.count = 100;

// 5. 实现事件监听
const methods = {
  increment() {
    data.count++
  },
  decrement() {
    data.count--
  }
};

document.getElementById('app')
  .addEventListener('click', ev => {
    const clickAttr = ev.target.attributes['@click'];
    const methodName = clickAttr && clickAttr.value;
    if (methodName) {
      methods[methodName]();
    }
  });