数据绑定简介

数据绑定是一个通用的技术,用来绑定来自提供者和消费者的数据源并同步它们。

这是一个通用定义,概括了数据绑定技术的通用构建模块

  • 一种定义数据提供者和消费者的语法
  • 一种定义哪些变化会触发同步的语法
  • 一种在提供者中监听这些变化的方法
  • 当这些变化发生时运行的一个同步函数。从现在开始,我将把这个函数称为 handler()

以上的步骤在不同的数据绑定技术中会以不同的方式实现。接下来将会介绍两种技术,即脏检查和存取器方法。他们都有优缺点,我将在介绍后简要讨论。

脏检查

脏检查是最为人熟知的数据绑定方法。它的概念很简单,不需要复杂的语言特性,这使得它可以作为一个很好的候选缺省选择。

语法

定义提供者和消费者不要求任何特别的语法,只需要一个简单的 JavaScript 对象。

  1. const provider = {
  2. message: 'Hello World'
  3. }
  4. const consumer = document.createElement('p')

同步通常是提供者上的属性变化触发。需要监听变化的属性,必须明确映射到各自的 handler() 函数。

  1. observe(provider, 'message', message => {
  2. consumer.innerHTML = message
  3. })

observe() 函数只保存 (provider, property) -> handler 映射供以后使用。

  1. function observe(provider, prop, handler) {
  2. provider._handlers[prop] = handler
  3. }

这样,我们就有一个定义提供者和消费者的语法,以及一种为属性改变而注册 handler() 函数的方法。我们库的公共 API 已经准备好了,现在介绍其内部实现。

监听变化

脏检查因一个原因而被称为脏。它定时检查而不是直接监听属性变化。从现在起,我们把这个检查称为摘要周期。一个摘要周期遍历每一个由 observe() 函数添加的 (provider, property) -> handler 入口,并且检查自上次遍历以来属性值是否发生变化。如果变化则运行 handler() 函数。一个简单的实现类似如下:

  1. function digest() {
  2. providers.forEach(digestProvider)
  3. }
  4. function digestProvider (provider) {
  5. for (let prop in provider._handlers) {
  6. if (provider._prevValues[prop] !== provider[prop]) {
  7. provider._prevValues[prop] = provider[prop]
  8. handler(provider[prop])
  9. }
  10. }
  11. }

digest() 函数需要一直不停地运行以确保状态同步。