作者:心叶
时间:2019-07-18 10:34

本文章构思的实现方法在github上有对应的项目已经实现,项目依旧在不断改进中:watcher.js

核心方法说明

Object.defineProperty(obj,key,{
    get(){
        // todo1
    },
    set(newValue){
        // todo2
    }
});

比如obj是一个json,其有一个值key,也就是obj.key。我们可以通过上面的方法对设置和获取obj.key的时候进行拦截处理,而这就是js部分双向绑定的核心。

设计思路

页面 → JS

监听输入框的输入时间,如果有输入,就主动改变js中的值,这样,页面到js这一条路就通了。

JS → 页面

js部分通过前面说的Object.defineProperty方法,对需要双向绑定的数据进行拦截,发现数据改变的时候,就主动去修改页面显示,这样,js到页面这一条路就通了。

难点

上面的说明看起来很简单,然而,事实并非如此,主要有下面几个问题(其实主要是设计的问题):

  • js数据改变了,你怎么知道应该修改页面中哪些地方?
  • 页面的表达式可能很复杂,如何解析
  • 数组或对象如何监听

实现

主体对象

控制整个流程,余下的都是由这里协调资源。

let iCrush = function (options) {

    // 管理的数据(双向绑定)
  this._data = options.data || {};
    
   // 对维护的数据进行监听
  this.__observe();

  // 获取挂载点
  this.$el = $$(options.el)[0];
  
  // 编译结点
  this.__compile();

}

数据拦截者observe.js

对象options的data中定义的key的编辑会在这里拦截,通知专门处理变化情况的(同步视图和数据)。

export default function(){

    for (let key in this._data) {
    
    // 定义拦截钩子
    Object.defineProperty(this._data, key, {
         get() {
            // todo
          },
          set(newValue) {
             // todo
          }
    });
    
};

编译程序compile.js

简单点说,就是分析结点中的内容,有没有定义的指令等,根据结点内容追加需要管理的东西。

export default function(){

    let nodes = this.$el.childNodes;
    
    for (let i = 0; i < nodes.length; i++) {
        
        // 如果不是文本结点,继续编译
      if (node.nodeType !== 3) {
        // todo
      }
      
      // 开始正式解析一个结点(分为文本结点和非文本结点)
      // 解析的意思就是标记需要管理的属性,文本等
      if (node.nodeType === 3) {
        // 如果是文本

      } else {
        // 如果不是文本
        
        }
        
    }
    
};

后记

上面力求简单,具体的代码没有显示出来,如果需要看具体代码,请去github/iCrush上查看。


心叶
304 声望114 粉丝

我还惊讶地意识到, 在我生命中有很多时刻, 每当我遇到一个遥不可及、令人害怕的情境,并感到惊慌失措时, 我都能够应付——因为我回想起了很久以前自己上过的那一课。