双向绑定学习 Object.defineProperty

基于熟悉angularJs情况下学习Vue,自己动手造了个类似的双向绑定,带简单的依赖。

代码所在:https://github.com/huanrum/ehuanrum

例子所在:https://files.cnblogs.com/files/ehuanrum/demo-build.rar

可以引用 https://files.cnblogs.com/files/ehuanrum/framework.css

    https://files.cnblogs.com/files/ehuanrum/framework.min.css

    https://files.cnblogs.com/files/ehuanrum/framework.js

    https://files.cnblogs.com/files/ehuanrum/framework.min.js

 


 

该框架的调用接口是window.$ehr,它是一个方法,有如下的参数

1.window.$ehr(true)

  表示所有的关于双向绑定的标记都会呈现在dom里面,否则就不呈现

2.window.$ehr(method,method)

  只要第一个参数是函数就表示这个在Dom加载完执行的方法,第二个存在就回在dom注销的时候执行

3.window.$ehr('name',function)

  第一个参数是字符串就表示我要注册(第二个参数存在)或者获取(第二个参数不存在)一个这个名称的功能

  如果第一个参数以.结束就表示我们注册的function,否则就表示我们注册的是function的返回值,这个function有两种写法(类似于angularJs)

  a.function(name1,name2...){...} 这里的name1,name2都是我们注入的功能的名称,但是这种写法会导致代码不能混淆

  b.['name1','name2',function(name1,name2){...}]  这里的name1,name2都是我们注入的功能的名称

 


 

框架自己带上了如下的几个功能,框架里面使用只需要注入到参数里面就行,外面使用的时候是window.$ehr('name')

1.value 用于获取某个对象的值或设置值

  var xx = {x:1,y:4,foo:{m:12,n:45}},filed = 'foo.m',action = 'x + y + foo.m';

  这时我们需要求得filed和action的运行值就可以用value(xx ,field);value(xx ,action);

 

2.binding 用于把dom与对应的data绑定起来

  var template = '<input [value]="name">',data = {name:'demo'};

  binding(template,data[,父Dom][,Controller]);

  第一个参数可以是一个字符串或者一个Dom元素

  需要注意的是第三个参数,Dom元素是父节点,mathod是controller,字符串是会调路由的

    返回的是Dom元素组的scope方法返回绑定的数据,update用于更新父Dom元素

3.filter 用于显示数据格式化

  添加 filter window.$ehr('filter.language',function(){

    return function(value,...){

      //自己的处理逻辑

    };

  });

  使用添加的filter 

  var template = '<input [value]="name | language">',data = {name:'demo'};

  binding(template,data[,父Dom][,Controller]);

 

4.额外提供了functions,里面包含color和event

  color 根据传入参数获取颜色,如果无参数就随机

  event 作为一个事件处理,可以注册,注销,或发起


关于注册各种功能

1.注册多个页面

  所有已router开头的都是会产生菜单的

  window.$ehr('router.about',...)

  window.$ehr('router.page',...)

  window.$ehr('router.page.child',...)

  这时我们就注册了三个页面about,page和page.child,第三个是第二个的子菜单

2.注册自己的控件和指令

  所有以control开头的都是自定义指令

  window.$ehr('control.my.number',...) 使用请看下面

2.main和filter.menu

  如果注册了main,它会在Dom加载完成后执行

  如果注册了filter.menu,它会去格式化我们现实出来的菜单
 

关于双向绑定
  所有的双向绑定都是元素的属性以[]关闭,或者属性值里面带{{...}}的,但两者不能混合使用
    新加入了类似ng2的[]/(),以及类似vue的:/@来绑定属性和事件
    类似 [value]="name" 或 value="{{name}}" 或 <div>{{name}}</div> 这就表示某个Dom元素的属性跟数据的name关联起来了
    等号后面的可以是一个表达式,所有已on开头有的属性都是事件。
  这里没有类似于if的操作,但循环还是有的 [item:items]这种中间带:的是循环
  自定义指令使用 [my.number]="filed" 这里的field是定义时的第三个参数

 

<!doctype html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link href="https://files.cnblogs.com/files/ehuanrum/framework.css" rel="stylesheet">
    <script src="https://files.cnblogs.com/files/ehuanrum/framework.js"></script>
</head>

<body>
    <script>
        $ehr(true);
        $ehr('filter.demo', ['binding', 'functions.color', function (binding, functions_color) {
            return function (value, type) {
                return ('<type style="color:' + functions_color() + '">value</type>').replace('type', type || 'span').replace('value', value);
            };
        }]);

        $ehr('control.demo', ['binding', function (binding) {
            return function (element, data, field) {
                binding('<a [innerHTML]="entity.name|demo(entity.type)" [title]="entity.info" [onclick]="open(entity)"></a><del [innerHTML]="entity.name"></del>'.replace(/entity/g, field), data.$extend({}, [field]), element);
            };
        }]);

        $ehr('control.if', ['value', function (value) {
            return function (element, data, field) {
                var parentNode = element.parentNode, nextSibling;
                this.defineProperty(data, field, function () {
                    if (value(data, field)) {
                        if (!element.parentNode) {
                            element.update(parentNode, nextSibling);
                        }
                    } else if (element.parentNode) {
                        parentNode = element.parentNode;
                        nextSibling = element.nextSibling
                        element.update();
                    }
                });
            };
        }]);
    </script>
    <script>
        $ehr('router.vue', ['binding', function (binding) {
            var template = [
                '<ul>',
                                '<li :innerHTML="item.name"></li>',
                                '<li @click="open(item)">{{item.name}}</li>',
                           '</ul>'
            ].join('');
            return function (name) {
                binding(template, {
                    open: function (item) {
                        alert(JSON.stringify(item));
                    },
                    item: { name: name + '-Name-1', info: 'Name-1' }
                });
            };
        }]);
        $ehr('router.angular2', ['binding', function (binding) {
            var template = [
                '<ul>',
                                '<li [innerHTML]="item.name"></li>',
                                '<li (click)="open(item)">{{item.name}}</li>',
                           '</ul>'
             ].join('');
            return function (name) {
                binding(template, {
                    open: function (item) {
                        alert(JSON.stringify(item));
                    },
                    item: { name: name + '-Name-1', info: 'Name-1' }
                });
            };
        }]);
        $ehr('router.page', ['binding', function (binding) {
            var template = '<ul><li [item:items] [demo]="item"></li></ul>';
            return function (name) {
                binding(template, {
                    open: function (item) {
                        alert(JSON.stringify(item));
                    },
                    items: [
                        { name: name + '-Name-1', info: 'Name-1' },
                        { name: name + '-Name-2', info: 'Name-2', type: 'button' }
                    ]
                });
            };
        }]);
        $ehr('router.work', ['binding', function (binding) {
            var template = '<ul><li [item:items] [demo]="item"></li></ul>';
            return function (name) {
                binding(template, {
                    open: function (item) {
                        alert(JSON.stringify(item));
                    },
                    items: [
                        { name: name + '-Name-1', info: 'Name-1' },
                        { name: name + '-Name-2', info: 'Name-2', type: 'strong' }
                    ]
                });
            };
        }]);
        $ehr('router.test', ['binding', function (binding) {
            var template = '<ul><li [item:items] [demo]="item" [hidden]="item.show" [style.fontSize]="item.size + \'px\'"></li></ul>';
            return function (name) {
                binding(template, {
                    open: function (item) {
                        alert(JSON.stringify(item));
                    },
                    items: [
                        { name: name + '-Name-1', info: 'Name-1' },
                        { name: name + '-Name-2', info: 'Name-2', type: 'strong' }
                    ]
                }, function (scope) {
                    var handel = setInterval(function () {
                        scope.items.forEach(function (item) {
                            item.show = Math.random() > 0.1;
                            item.size = Math.floor(Math.random()*20) + 12;
                        });
                    },1000);
                    scope.$destroy(function () {
                        clearInterval(handel);
                    });
                });
            };
        }]);
        $ehr('router.input', ['binding', 'random',function (binding,random) {
            var template = '<ul><li [item:items]><input [type]="item.type" [value]="item.value" [title]="item.formatter"></li></ul>';
            return function (name) {
                binding(template, {
                    open: function (item) {
                        alert(JSON.stringify(item));
                    },
                    items: 'button,text|[(){4-20}],number|[(0-9)3-10],email|[(){3-7}]@[(a-z){1-4}].[(a-z){2-4}],range,color|#[(0-f)6],date|[1960-2020]-[1-12]-[1-29],reset,!file'.split(',').map(function(type,index){
                        return  { name: name + '-Name-' + index, auto:!/!/.test(type),formatter:type.split('|')[1],type:type.split('|')[0].replace('!','')};
                    })
                }, function (scope) {
                    var handel = setInterval(function () {
                        scope.items.forEach(function (item) {
                            if(!item.auto){return;}
                            var value = Math.floor(Math.random()*100);
                            if(item.formatter){
                                value = random(item.formatter);
                            }
                            item.value = value;
                        });
                    },2000);
                    scope.$destroy(function () {
                        clearInterval(handel);
                    });

                    function complement(num,length){
                        return (Array(length).join(0) + num).slice(-length);
                    }
                });
            };
        }]);
    </script>
</body>

</html>
View Code

 

posted @ 2017-04-13 15:45  EhuanRum  阅读(204)  评论(0)    收藏  举报