zoukankan      html  css  js  c++  java
  • CanJS 简单入门

    1. can.Construct

    首先需要了解的是can.Construct。
    一个完全的例子如下:

    var Student = can.Construct({
        role : "Student",
        getRole : function() {
            return this.role;
        }
    }, {
        _studentId : null,
        _name : null,
        _age : null,
        _gender : null,
        init : function(stuInfo) {
            this._studentId = stuInfo.id;
            this._name = stuInfo.name;
            this._age = stuInfo.age;
            this._gender = stuInfo.gender;
        },
        getName : function() {
            return this._name;
        }
    });

    此时我们就有了一个新的类Student,

    我们可以看到我们传入了两个JSON格式的对象。这两个JSON对象中个有一些值对象和方法对象。这两个JSON对象的区别就是
    第一个JSON中传入的全是类属性和类方法,第二个JSON中传入的是实例属性和实例方法。一下的例子可以看到他们的区别:

    //类属性和类方法可以用类直接调用
    console.log(Student.role);
    console.log(Student.getRole());
    
    var stu = new Student({
        id : 0,
        name : "Xinxing Jiang",
        age : "27",
        gender : "male"
    });
    // 实例属性和实例方法需要首先创建一个实例出来才能调用
    console.log(stu.getName());

    这里需要注意的是类属性,方法和实例属性,方法不能相互调用,即实例对象只能调用实例属性方法,不能调用类方法属性。
    需要注意的是第一个JSON可以省略不写,即当传入参数只有一个JSON对象时,默认为实例属性和方法。


    实例方法init是这个类的构造函数,我们用以下代码新建一个实例时,我们可以传入参数[argsments],

    var stu = new Student([argsments]);

    参数可以在init方法中获取到。如 上面的例子中新建stu对象时,传入了参数:

    {
        id : 0,
        name : "Xinxing Jiang",
        age : "27",
        gender : "male"
    }

    init方法中就用参数stuInfo获取了这个JSON对象。


    我们还能很简单的实现继承:

    var UniversityStudent = Student({
        role : "University Student",
        level : "University",
        getLevel : function() {
            return this.level;
        }
    }, {
        getName : function() {
            return "Name: " + this._name;
        },
        getAge : function() {
            return this._age;
        }
    
    });

    子类能够重写父类的所有的属性,方法,还能扩展更多的属性和方法。

    后面我们将提到的can.Control 和 can.Model就会用到can.Construct,
    我们可以看作can.Control 和 can.Model是can.Construct的子类。

    2. can.Observe

    can.Observe 可以为JS对象提供监听模式。

    1. 设置或者删除对象的属性的值
    2. 监听这些值的改变
    3. 监听嵌套的对象

    新建一个can.Observe实例如下:

    var student = new can.Observe({
        name : 'Xinxing Jiang',
        age : 27,
        gender : 'male'
    });

    我们可以看下常见的方法:

    //获取student的name属性
    console.log(student.attr('name')); //---> Xinxing Jiang
    
    //设置student的age属性
    student.attr('age',30);
    
    //student.attr()能得到student对象的所有属性,返回值是一个JSON对象。
    console.log(JSON.stringify(student.attr())); //---> {name: 'Xinxing Jiang', age: 30, gender: 'male' }
    
    //同时设置student的多条属性
    student.attr( { age: 35, gender: "male...." } );
    console.log(JSON.stringify(student.attr())); //---> {name: 'Xinxing Jiang', age: 35, gender: 'male....' }
    
    //用removeAttr方法可以删除这条属性,并将当前值返回
    console.log(student.removeAttr('age')); //---> 35

    以上都是对对象的属性进行操作,我们还可以用以下方法来监听这些改变:

    bind
    我们来看个例子就能很简单的知道其用法

    // 监听student对象的改变,监听所有属性的所有改变
    // 其中attr是改变属性的属性名,如:age
    // how是改变的方式,有三个值,分别为 remove, add, set
    // newVal为改变后的值,如果为 remove事件, 这个值为undefined
    // oldVal为改变前的值,如果为 add事件, 这个值为undefined
    student.bind('change', function(ev, attr, how, newVal, oldVal) {
        console.log(ev, attr, how, newVal, oldVal); //#1
    
    // 只监听age属性的改变
    }).bind('age',function(ev, newVal, oldVal){
        console.log(ev, newVal, oldVal);//#2
    });
    
    student.removeAttr('age');
    //#1--->f.Event "age" "remove" undefined 35 
    //#2--->f.Event undefined 35 
    student.attr('age',18);
    //#1--->f.Event "age" "add" 18 undefined 
    //#2--->f.Event 18 undefined 
    student.attr('age',24);
    //#1--->f.Event "age" "set" 24 18 
    //#2--->f.Event 24 18 
    student.attr('age');//--->无改变,无输出

    我们还可以对一个属性进行多次绑定

    var ageListener1 = function(ev, newVal, oldVal) {
        console.log('listen age event 1');// #1
    };
    var ageListener2 = function(ev, newVal, oldVal) {
        console.log('listen age event 2');// #2
    };
    student.bind('age', ageListener1);
    student.bind('age', ageListener2);

    此时如果我们再改变age的值:

    student.attr('age',24);

    输出会变为

    //--->listen age event 1
    //--->listen age event 2

    由此可见我们对同一个属性绑定多个监听的时候会按绑定时的顺序,
    依次执行我们绑定的方法。


    unbind
    与bind相对应,解除对属性改变的监听。
    有两种写法:
    一种是 

    student.unbind('age',ageListener1);

    此时仅仅将我们绑定到age属性上的两个监听方法ageListener1,ageListener2中的ageListener1解除绑定,
    ageListener2会继续监听。
    如果我们想一次解绑所有的监听,我们可以用另一种写法:

    student.unbind('age');

    each
    用each遍历所有的属性和对应的值
    需要注意的是值在属性前

    student.each(function(value, name) {
        console.log(name, value);
    });
    // --->name Xinxing Jiang
    // --->gender male....
    // --->age 24

    can.Observe.List
    can.Observe.List继承于can.Observe,所以我们仍能用bind,unbind,each方法
    但是bind监听的事件只能是add,remove,change三种。

    var students = new can.Observe.List([ 'Jiang', 'Wang', 'Li' ]);
    
    students.bind('add', function(ev, newVals, index) {
        console.log('added', newVals, 'at', index);
    }).bind('remove', function(ev, oldVals, index) {
        console.log('removed', oldVals, 'at', index);
    }).bind('change', function(ev, index, how, newVal, oldVal) {
        console.log('changed', ev, index, how, newVal, oldVal);
    });
    
    students.unbind('change');
    
    students.each(function(item) {
        console.log(item);
    });

    并扩展了一些List的方法。

    • indexOf list.indexOf( item ) - 返回item在list中的索引号
    • pop list.pop() - 删除数组最后一个元素,并作为返回值返回
    • push list.push( items... ) - 在数组最后增加一个元素,返回数组长度
    • shift list.shift() - 删除数组第一个元素,并作为返回值返回
    • splice list.splice( index, howMany, [ items... ] ) - 删除从序列号为index开始的howMany个元素,并将后面参数依次加到list的index处,返回值为删除掉的元素的数组
    • unshift list.unshift( items... ) - 在数组开头增加一个元素,返回数组长度
    console.log(students.indexOf('Jiang')); //---> 0
    console.log(students.pop());//---> Li
    console.log(students.push('Zhu'));//---> 3
    console.log(students.shift());//---> Jiang
    console.log(students.unshift('Jiang'));//---> 3
    console.log(students.splice( 1, 2,'Qu','Gao','Su' ));//---> ['Wang', 'Zhu']
    console.log(students);//---> ["Jiang", "Qu", "Gao", "Su", "Zhu"]
    
    
    //不要用如下方式进行改变值,因为这样监听事件监听不到改变
    students[0] = 'Xinxing';
    //要以如下方式
    students.splice( 0, 1,'Xinxing');

    3. can.Model
    一个最简洁的Model如下:

    var Student = can.Model({
        findAll : 'GET /students',
        findOne : 'GET /students/{id}',
        create : 'POST /students',
        update : 'PUT /students/{id}',
        destroy : 'DELETE /students/{id}'
    }, {})

    findAll, findOne, create, update, destroy这五个方法是由canjs特殊处理的,是Model的类方法,我们调用的时候可以使用

    Student.findOne({id:1,name:"Xinxing Jiang"});

    来调用,这是canjs会发送一个ajax请求到Server端并返回一个Student的实例。

    其作用与以下代码相似:

    var Student = can.Model({
        findAll : 'GET /students',
        findOne : function(data, success) {
            $.ajax({
                url : '/students/{id}',
                type : "GET",
                data : data,
                success : function(d) {
                    success(new Student(d)); // 将返回的对象封装成Student Model的一个实例。
                }
            });
        },
        create : 'POST /students',
        update : 'PUT /students/{id}',
        destroy : 'DELETE /students/{id}'
    }, {});

    所以我就可以来增加更多的类方法:

    var Student = can.Model({
        findAll : 'GET /students',
        findOne : 'GET /students/{id}',
        findByName : function(data, success) {
            $.ajax({
                url : '/students/name',
                type : "GET",
                data : data,
                success : function(d) {
                    success(new Student(d));
                }
            });
        },
        create : 'POST /student',
        update : 'PUT /students/{id}',
        destroy : 'DELETE /students/{id}'
    }, {});

    调用时如下:

    Student.findByName({
        name : "Jiang"
    }, function(student) {
        console.log(student);
    });
    Student.findOne({
        id : 3
    }, function(student) {
        console.log(student);
    });

    以上只是说明Model的类方法基本都是一些增删改查,对应Java语言,其实Model的类方法就相当于Model的DAO类,用来对数据进行操作并返回一个Model的实例。


    can.Model是一种can.Observe,可以看作是can.Observe的子类,扩展了一些符合REST的接口。Model的实例属性,实例方法才是Java中的Model.

    var stu = new Student({
        id : 3,
        name : "Xinxing Jiang",
        age : "27",
        gender : "male"
    });

    以上就是一个Student的实例。就像can.Observe中一样,我们可以用 attr来对其中的值来进行读写:

    stu.attr(); //---->{id:3,name:"Xinxing Jiang",age:"27",gender:"male"}
    stu.attr("name"); //---->Xinxing Jiang
    stu.attr("name","Susan Wang"); //---->stu object
    stu.attr({id:4,name:"Susan Wang",age:"27",gender:"female"}); //---->stu object

    除此之外,还有一个实例方法save。使用方法如下:

    stu.save(function(stu) {
        console.log("client1", stu);
        stu.attr("name", "Sheldon Jiang");
        stu.save(function(stu) {
            console.log("client2", stu);
        });
    });

    save方法会自动调用类方法中的create,update方法。

    当实例对象是新建出来 并且是第一次save的话 就会调用create方法,其他调用update方法。

    同样的实例方法destroy会调用类方法destroy

    与can.Observe一样我们可以给每个属性绑定监听,

    stu.bind('name', function(ev, newVal, oldVal) {
        console.log('name changed to', newVal);
    });

    或者给整个对象绑定监听:只有三个event: created,updated,destroyed

    stu.bind('created', function(ev, stu) {
        console.log('created', stu);
    });
    stu.bind('updated', function(ev, stu) {
        console.log('created', stu);
    });
    stu.bind('destroyed', function(ev, stu) {
        console.log('created', stu);
    });

    can.Model.List是can.Model的集合,用法与can.Observe.List一样。Student.findAll的返回值就是一个can.Model.List

    4. can.View

    can.View可以说是最简单的模块了,只是用来加载模板,它接收两个参数:can.View(idOrUrl, data);
    第一个参数是模板对应的id或者模板的文件名(含路径), 第二个参数是传入模板的数据,这个数据通常是一个JSON对象或者JSON数组。

    $("#content").html(can.View("view/test.ejs", {name: "Jiang", age: "27"}));

    can.View也支持延迟加载:

    can.view('view/test.ejs', {
        students : Student.findAll()
    }).then(function(testViewHtml) {
        $("#content").html(testViewHtml);
    })

    Student.findAll()是一个从服务端下载数据的ajax请求,会花费一段时间,can.View能够等待这些数据返回再将这些数据运用到模板中,然后再加到页面上显示出来。其功用与以下写法一致。

    Student.findAll({}, function(students) {
        $("#content").html(can.view('view/test.ejs', {
            students : students
        }));
    });

    render功能其实就是将返回的Document对象变成String返回。 通常是在一个模板引用另一个模板时使用:

    <% for( var i = 0; i < students.length; i++ ) { %>
        <li><%== can.view.render( 'view/student.ejs', {
                student: students[ i ]
        } ) %></li>
    <% } %>

    5. can.EJS

    上一节can.View中提到can.View是来加载模板的,can.EJS就是canjs默认使用的模板,并且也基本上是canjs使用者唯一使用的模板,因为虽然canjs号称支持多种模板,但是很多功能都支持的没有can.EJS好,比如Model的自动更新机制。

    can.EJS有两种定义方式,一种是写成一个script标签中如下

    <script type="text/ejs" id="testEJS">
        <!--CODES-->
    </script>

    还有一种创建的方式是创建一个test.ejs文件,并且将<!--CODES-->写在这个文件中。

    这与html引用javascript是差不多的。

    EJS中与html相似,但是又额外含有几种特殊标签:

    • <% CODES %>: 这个标签用来运行javascript语句。
    • <%= CODES %>: 这个标签用来运行javascript语句,并且将返回值输出到当前位置。
    • <%== CODES %>: 这个标签用来运行javascript语句,并且将返回值输出到当前位置,并且会将返回值解析一次。

    <%= CODES %>与<%== CODES %>有比较容易混淆的地方可以用一个例子来表示:
    <%= ‘<b>黑体</b>’ %> 显示出来的是‘<b>黑体</b>’
    而<%== ‘<b>黑体</b>’ %> 显示出来的是已经加粗的‘黑体’二字
    当然在实际应用中我们很少会用到<%= ‘<b>黑体</b>’ %>这种写法,通常
    <%= CODES %> 只是用来输出一个对象的值 比如 <%= student.name %>等,基本不会包含html的标签。
    <%== CODES %>通常用来引用一个子模板,上面can.View里面我们提到过其应用。


    can.EJS的重头戏,也是我认为canjs存在的最大意义的部门就是下面要说的动态绑定。
    拿上文Model的例子来说。一个Student的Model如下:

    var Student = can.Model({
        findAll : 'GET /students',
        findOne : 'GET /students/{id}',
        findByName : function(data, success) {
            $.ajax({
                url : '/students/name',
                type : "GET",
                data : data,
                success : function(d) {
                    success(new Student(d));
                }
            });
        },
        create : 'POST /student',
        update : 'PUT /students/{id}',
        destroy : 'DELETE /students/{id}'
    }, {});

    那么我们可以得到一个Student的实例stu

    var stu = new Student({
        id : 0,
        name : "Xinxing Jiang",
        age : "27",
        gender : "male"
    });

    然后我们有一个简单的模板叫student.ejs

    <table>
    <tr><td>Name:</td><td><%=student.attr("name")%></td></tr>
    <tr><td>Age:</td><td><%=student.attr("age")%></td></tr>
    <tr><td>Gender:</td><td><%=student.attr("gender")%></td></tr>
    <table>

    然后我们可以调用

    $("#content").html(can.View("student.ejs",{student:stu}));

    这时我们的页面会显示

    Name:Xinxing Jiang
    Age:27
    Gender:male

    如果我们使用
    stu.attr("name","Sheldon Jiang");
    我们不需要做任何其他的事情,页面会自动更新成
    Name:Sheldon Jiang
    Age:27
    Gender:male
    这就是我认为canjs中最值得使用的地方,他将一个页面上显示的element对象与后台的Model对应了起来,我们不需要因为值的改变来手动的更新html代码。

    上面只是一个简单的例子,现在我们来写一个复杂一点的例子。
    假设现在我们有许多学生:

    var STUDENTS = [ {
        id : 0,
        name : "Xinxing Jiang",
        age : "27",
        gender : "male"
    }, {
        id : 1,
        name : "Zhongjiao Wang",
        age : "27",
        gender : "female"
    }, {
        id : 2,
        name : "Meimei Han",
        age : "26",
        gender : "female"
    }, {
        id : 3,
        name : "Lei Li",
        age : "28",
        gender : "male"
    } ];

    现在我们需要用一个列表来显示所有的学生,首先我们需要一个模板用来显示:

    <ul>
    <% students.each( function( student ) { %>
    <li><%==can.view.render( 'student.ejs', {student:student} )%></li>
    <% }); %>
    </ul>

    我们还需要一个Model对象

    var students = new Student.List(STUDENTS);

    和一个调用的地方:

    $("#content").html(can.View("students.ejs",{students:students}));

    这里有几个需要注意的地方:

    第一是创建Student.List对象的参数必须是Student对象的一个数组。
    第二是 引用子模板:<%==can.view.render( 'student.ejs', {student:student} )%>
    第三是对Student.List的遍历最好使用students.each的方式。
    for (var i =0; i < students.length; i++) {...students[i]...} 的方式会导致Live Binding的功能失效.因为当这种方式循环结束是i的值永远都是students.length-1。canjs再也不能找到相对应的对象来进行监听。

    还有用jquery的方式 $(students).each(function(index, student){ ...student...}),这种方式虽然能监听单个student的变化,但是当students增加或减少时将得不到更新。除非在这个代码块的上一行调用students.attr('length')。这显然不是一个值得提倡的代码方式,因为我们并不需要知道其lenght。


    我们这种方式能够监听这个List的变化:

    students.push(new Student({
        id : 4,
        name : "Tom",
        age : "31",
        gender : "male"
    }));

    或者 

    students[0].destroy();

    can.EJS还有一个重要的功能就是 Element Callbacks。

    写法如下:

    <div <%= function( element ) { element.style.display = 'none' } %> >
    Test
    </div>

    并且当方法内语句就是一个单行的时候可以简写成

    <div <%= ( element ) -> element.style.display = 'none' %> >
    Test
    </div>

    通常来说这个功能是让你能够将对象缓存在这个element上:

    <div <%= ( el ) -> el.data('student', student) %> >
    Test
    </div>

    然后如果用户点击这个div的时候我们就能很容易的得到其对应的Model实例

    $('div').click(function() {
        var student = this.data('student');
        // TODO:some other logic.
    }) 

    6. can.Control
    can.Control就是MVC中的控制器,控制器有以下特点:有组织的,内存回收,执行迅速,有状态的。
    我们所有的逻辑代码,UI的控制代码都需要写在can.Control中。

    var Todos = can.Control({
        defaults : {
            age : '32',
            mail : "sheldon.jiang@airbiquity.com"
        }
    }, {
        'init' : function(element, options) {
            var self = this;
            Todo.findAll({}, function(todos) {
                self.element.html(can.view('todosEJS', todos));
            });
        }
    });

    我们来分析这个Control,相当于在掉can.Control的时候传入了两个参数,这两个参数都是json对象,并且第一个参数可以省略。

    第一个JSON参数中的值,我们可以看作Todos的类静态方法和静态属性,并且其中有一个Key叫做defaults,这个key对应的值会与实例化Todos的时候与传入的第二个参数进行合并。
    第二个JSON参数中的值,我们看作是Todos的实例方法和实例属性,其中我们可以把init当作构造函数,这个构造函数能接收两个参数,一个是element,一个是options。

    初始化一个Todos的实例如下:

    var todos = new Todos('#test', {name:'sheldon',age:'27'});

    element和options相对应的接收new Todos实例时传入的两个参数'#test'和{name:'sheldon',age:'27'}。

    当然element就已经不是字符串'#test‘而是$('#test')对象。
    options就是传入的json对象和上面提到的defaults里的值进行合并,可以用options.name来取得相对应的值。
    并且options的整个值如下:

    { 
        name:'sheldon',
        age:'27',
        mail: "sheldon.jiang@airbiquity.com"
    }

    在除去init方法以外的方法中,我们也可以用this.element 和this.options来调用这两个参数。

    既然我们Control不仅要组织逻辑还需要对页面,也就是can.View进行操作,就必不可少的需要监听页面上的用户交互事件。

    var Todos = can.Control({
        defaults : {
            age : '32',
            mail : "sheldon.jiang@airbiquity.com"
        }
    }, {
        'init' : function(element, options) {
            var self = this;
            Todo.findAll({}, function(todos) {
                self.element.html(can.view('todosEJS', todos))
            })
        },
        '.testButton click' : function(el, event) {
            // TODO:
        }
    });

    我们可以看到

    '.testButton click' : function(el, event) {
        //TODO:
    }

    这就是监听class为testButton的页面元素的click事件,当用户点击testButton元素时,后面的时间就会被触发,并且接收两个参数,

    el就是当前被点击的dom对象,即$('.testButton')。event就是事件对象,我们可以从中取出点击的坐标轴之类。

    这是一个坑,等待填满。

  • 相关阅读:
    uuid模块
    使用pip管理第三方包
    hashlib和hmac模块
    hashlib和hmac模块
    JAVA热部署,通过agent进行代码增量热替换!!!
    史上最全java pdf精品书籍整理
    JAVA RPC (十) nio服务端解析
    java代理,手把手交你写java代理
    JAVA RPC 生产级高可用RPC框架使用分享
    DB缓存一致性
  • 原文地址:https://www.cnblogs.com/biglaojiang/p/3069426.html
Copyright © 2011-2022 走看看