非常简单的js双向绑定框架(一)

本文介绍了作者在尝试创建一个简单JavaScript双向绑定框架的过程。灵感来源于AngularJS的特性,如控制器继承和数据双向绑定,但目标是通过借鉴knockoutjs,避免使用脏值检测和DOM树编译。文章主要探讨了第一个关键特性——双向绑定的实现,通过建立view.change和model.change两个pubsub信道,实现了模型和视图的同步更新。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

初衷

搞了近5个月的angularjs项目,用起来非常顺手。最爽的是两个功能:
1. 控制器的继承特性
2. 数据的双向绑定
3. 表达式控制显示与否
前者减少了很多model的重复声明,赋值。后者大大简化了动态编辑,动态显示。比如我的表格需要根据某一列排序,我只用改动数据模型的顺序,视图会自动更新。
但是,作为“Get your hands dirty”的小项目,用脏值检测和dom树编译太难了,目标1周的话搞不掂。所以借鉴knockoutjs的方法:

var myViewModel = {
    personName: ko.observable('Bob'),
    personAge: ko.observable(123)
};
ko.applyBindings(myViewModel, document.getElementById('my-view'));

手动的把一个js object绑到某个DOM元素上去。

目标

  1. 实现数据的双向绑定(input, textarea, select)
  2. 实现控制器继承,控制器的作用范围在html里声明,而不是像knockout一样用js声明
  3. 基本事件的绑定,如click
  4. 类似ng-show这样的表达式控制显隐

第一个特性:双向绑定

参考这篇blog: pubsub双向绑定
我们用原生js先实现pubsub本身,为了代码的独立性,不像原文一样做dom操作:

<body isi-controller="MainController">
    <script type="text/javascript">
      var myModel;
      window.onload = function() {
          myModel = new Model;
          myModel.set( "name", "isildur" );
      }
    </script>
    <input type="text" data-bind="name"/>
</body>   
var pubsub = (function() {
    var _callbacks = {};
    var pubsub = {
        sub: function(name, callback) {
            if( !_callbacks.hasOwnProperty(name) ) {
                _callbacks[name] = [];
            }
            _callbacks[name].push(callback);
        },
        pub: function(name) {
            var args = Array.prototype.slice.call(arguments,1)
            if( _callbacks.hasOwnProperty(name) ) {
                _callbacks[name].forEach( function(callback) {
                    callback.apply(this, args);
                });
            }
        }
    }
    return pubsub;
})();

// listener capture view change event --> publish view.change event
var changeHandler = function(event) {
    var target = event.target,
        propName = target.getAttribute('data-bind');
    if( propName && propName !== '' ) {
        pubsub.pub('model.change', propName, target.value);
    }
}
document.addEventListener('change', changeHandler, false);

// view.change event --> change view
pubsub.sub('view.change', function(propName, newVal) {
    var elements = document.querySelectorAll('[data-bind=' + propName +']'),
        tagName;
    for(var i=0,l=elements.length; i<l; i++) {
        tagName = elements[i].tagName.toLowerCase();
        if(tagName==='input' || tagName==='textarea' || tagName==='select') {
            elements[i].value = newVal;
        } else {
            elements[i].innerHTML = newVal;
        }
    }
});

var Model = function() {
    var model = {
        props: {},
        set: function(propName, value) {
            this.props[propName] = value;
            // model change --> publish view.change event
            pubsub.pub('view.change', propName, value);
        },
        get: function(propName) {
            return this.props[propName];
        }
    }
    // model.change event --> change model data
    pubsub.sub('model.change',function(propName, newVal) {
        model.set(propName,newVal);
    });
    return model;
}

可以看到,我们搞了2个pubsub的”信道”: view.change和model.change
模型的set方法会给view.change信道发消息,改变view
view的监听器会给model.change信道发消息,改变model
这样就完成了双向绑定

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值