【分享】WeX5的正确打开方式(3)——绑定机制

今天整理一下WeX5的绑定机制。

原生的问题
  假设我们做一个订单系统,需要显示商品单价,然后可以根据输入数量计算出总价并显示出来。使用原生代码也很容易实现,效果:
         
代码如下:

<!--HTML code-->
Price: <span id="price"></span><br />
Account: <input type="text" id="account" value="" placeholder="请输入数量" /><br />
sum: <span id="sum"></span>
//js codevar priceNode = document.getElementById(‘price‘),
accountNode = document.getElementById(‘account‘),
sumNode = document.getElementById(‘sum‘),
price = 100,
account = 11,
sum = price * account;//初始化。
priceNode.innerText = price;
accountNode.value = account;
sumNode.textContent = sum;
//监视 View层的用户输入
accountNode.addEventListener(‘keydown‘, function (e) {
window.setTimeout(function () {
account = accountNode.value;
sum = price * account;
sumNode.textContent = sum;
},10);
});

嗯,蛮简单的!哦,对了,我们一次展示50件商品,同时又有10类这样的展示,还有买5盒冈本送一根油条这样的各种促销呢……

所以,你知道原生实现的问题了吧:

  • 随着 UI 和数据交互的增多,代码量迅速增长,难以维护
  • 基于 Dom 查询,id 或 class 的命名难以管理
  • 代码耦合度高,难以复用

WeX5的解决之道
    为了解决上述问题,WeX5中引入了knockoutjs(下文简称ko)这个MVVM库。
为何选用ko而不是Angular一类比较全面的框架?Angular是好,但这么大而全的框架,没有经过足够的实战测试的话,很多坑都不会被暴露出来。而ko是一个轻量级的MVVM库,专注于实现数据与视图的绑定,本身并不提供 UI 类和路由等功能,所以非常简单稳定。同时,由于他出来也已经有些年头了,现在是比较成熟的框架了。所以在做一些移动页面开发时,ko无疑是一个比较好的选择。另外,关于MVVM小茄就不多说了,一图以蔽之:
   
ko建立在3大核心特征之上(官网介绍):
    1. 可观察对象与依赖跟踪 (Observables and dependency tracking):使用可观察对象在模型数据之间设立隐性关系链,用于数据转换和绑定。
    2. 声明式绑定 (Declarative bindings):使用简单易读的语法方便地将模型数据与DOM元素绑定在一起。
    3. 模板 (Templating):内置模板引擎、为你的模型数据快速编写复杂的 UI 展现。
下面简单说说ko的几大概念:
可观察对象
    使用ko重写上面的例子(自定价格,这也是我小时候的愿望之一):
    
代码是这样的:

<!--HTML Code--><div id="one">
Price: <input type="text" data-bind="value: price" placeholder="请输入单价" /><br />
Account: <input type="text" data-bind="value: account" placeholder="请输入个数" /><br />
sum: <span data-bind="text: sum"></span></div>
// js Codevar ViewModel = function(p, a) {
//设置为可观察对象并以参数p、a初始化
this.price = ko.observable(p);
this.account = ko.observable(a);
//调用ko函数的时候将this传入,否则执行ko.pureComputed内部代码时,this为ko,ko.price()报错。
this.sum = ko.pureComputed(function() {
//因为可观察对象是一个函数对象,所以要用 price()来读取当前值。
//设置值使用price(NewValue),支持链式写法:this.price(12).account(3)
return this.price() * this.account();
}, this);
};var vm = new ViewModel(135, 10);//应用该绑定,绑定开始生效
ko.applyBindings(vm);

1)先看HTML代码:
可以看到在每个标签中都加入了一个 data-bind = "XX:OO" 这样的键-值对。这个就是 ko 的绑定语法,XXOO代表什么东西呢?(XXOO?小茄还是个孩子啊…)从例子可以看到XX为标签的属性,可以是text、value、class、checked等标签属性,其实也可以是click、focus、load等DOM事件。OO看起来像是一个变量,实际上并不是变量,而是一个函数对象,执行这个函数(带个())就能得到相应的绑定值。通过XXOO就可以将元素的属性或事件跟js中的函数对象绑定在一起(XXOO过了就要相互负责?),这就是ko的声明式绑定。绑定的定义其实就是一个观察者模式,只不过这是双向的绑定,发布者和订阅者相互订阅了对方的消息而已,这就是MVVM的双向绑定。ko双向绑定的结果就是一方变化就可以自动更新另一方,也就是通过ViewModel将数据和表现层紧紧绑定在一起了。绑定的效果类似于:

2)再看看js代码:
可以看到js中定义了一个ViewModel对象,在对象中对HTML中绑定的OO进行了操作。这里主要有两个操作: ko.observable()和ko.pureComputed()。

  • ko.observable(p):见名知义、这个就是设置可观察对象的方法,传入的参数p就是初始化的值,这里的参数可以是基本数据类型,也可以是一个json对象。被设置为可观察对象后就意味着系统会一直观察这个值。无论是ViewModel中的p还是被绑定对象的p发生变化都会引起刷新事件,将所有用到这个值的地方都更新到最新状态。显然,可观察对象是比较消耗性能的,所以对于不需要动态变更的值(如价格)则不要设置为可观察对象,当然还是需要放入ViewModel中进行集中初始化。
  • 注意:ko.observable(p)返回的可观察对象是一个函数对象,所以读取可观察对象需要使用price()这种方式;同样的,设置可观察对象需要使用price(newValue)这种方式。比较贴心的是,设置的时候支持链式写法:ViewModel.price(100).account(10)。
  • ko.pureComputed()就是所谓的依赖跟踪了,这里是单价*数量等于总价,注意这里不能直接用this.sum = this.price() * this.account();来指定sum,这种写法不能动态刷新被绑定的对象,只是动态改变了sum变量,但要去刷新绑定对象还需要其他操作。所以,与计算相关的绑定值都要用ko的计算函数来设置。当然,返回的也是一个函数对象。另外,ko还有一个computed函数,也可以用其来进行设置,不过推荐使用pure的方式,以提高性能。
  • 注意这里的写法:ko.pureComputed(fn, this),也就是将fn绑定到ViewModel执行环境中,其实就是js中的call/apply。因为在执行ko内部函数的时候,this为ko对象,所以为了得到ViewModel对象,需要通过上面的写法传入this。当然也可以在ko函数外部用that保存ViewModel对象,然后在ko函数内部使用that来调用ViewModel对象。像这样:
var that = this;this.sum = ko.pureComputed(function() {
return that.price() * that.account();
});

定义好ViewModel构造函数后便实例化了一个ViewModel对象,然后使用了ko.applyBindings()的方式来使得绑定生效,这一步不要漏掉了。

使用ko的页面简单模式:

<!--HTML Code--><span data-bind="text: bindtext"></span>
// js Codevar viewModel = {
bindtext: ko.observable(‘initValue‘)
};
ko.applyBindings(viewModel);

总结起来就是:HTML中使用data-bind="XX: OO"声明绑定,js中建立ViewModel并设置可观察对象,最后应用绑定。

可观察对象数组
    再看看可观察对象数组的使用方法,在ko中可不能像js一样数组和变量混用,对于数组对象就要用ko.observableArray([…,…])这种形式,同样的,数组元素也可以是基本类型也可以是json对象。ko中的可观察对象数组有一系列的数组操作方法,如slice()、sort()、push()这种,效果跟原生的js数组操作方法一样,只是通过ko方法所做的改动会通知到订阅者从而刷新界面,但js方法则不会刷新界面。下面是一个简单例子:

<!--HTML Code--><select data-bind="options: list"></select>
// js Codevar vm = {
// list: ko.observableArray()
list: ko.observableArray([‘Luffy‘,‘Zoro‘,‘Sanji‘])
};
ko.applyBindings(vm);

关键点:ko监控的是数组的状态,而不是元素本身的状态。也就是说当数组状态变化(增减元素)的时候会触发ko事件引起绑定对象的刷新,但数组内部元素的变化(如:值变化)则不被监控不能触发ko事件。例如:


    在控制台中使用原生方法将Luffy动态改成Lucy是不会刷新UI页面的,而使用ko的数组操作改动数组则会立即刷新页面,值得注意的是在刷新的时候,也会将之前的改动刷新出来(Luffy > Lucy)。也就是说其实js内存中的变量是已经改变了,但是还缺少一个刷新DOM的动作。这里大家可以看到,读取数组的方法是vm.list()[0],因为list也是一个函数对象,执行返回值才是我们想要的list内容。同理,也可以通过 vm.list(["妹子","妹子","妹子"]) 这样的方式重置可观察对象数组,也能立即刷新UI。
如果需要将数组元素的改动也动态反应到UI上,需要将数组元素也设置为可观察对象,然后使用ko的方法改变数组元素值。注意,是使用ko的方法 list()[0]("Lucy")!

    操作可观察对象数组的方法有两类,一类是与原生js数组方法同名的:pop, push, shift, unshift, reverse, sort, splice,这一部分与js原生方法的用法和效果都一样,就不再赘述了。
另外一些方法是js中没有的,主要有以下几个:

  • remove(someItem) -- 删除所有值与someItem相等的元素项并将它们以数组形式返回,这里的意思就是说你可不能直接list.remove(0)来删除第一项,而是要用list.remove(list()[0]) 这种形式来删除。总而言之,传入的参数必须是元素项的值,可以用list()[0] 的形式,也可以直接输入值的字符串(比如“Luffy”这种)。
  • remove(function(item) { return item.age < 18;}) -- 删除所有age属性小于18的元素项并将它们以数组形式返回,这种用法跟平常的数组高阶函数没什么区别。Item作为高阶函数的参数传入,遍历数组时,当高阶函数返回值为真值时就删除该项,否则转到下一项。
  • removeAll([‘Chad‘, 132, undefined]) -- 删除所有值与 ‘Chad‘ 或 123 或 undefined 相等的元素项并将它们以数组形式返回。
  • removeAll() -- 删除所有项并以数组形式返回。

小窍门:在处理可观察对象时,若对象数量众多而且交互频繁的情况下,每次变更都立即刷新的话会非常消耗性能,这个时候可以使用扩展 myObservableArray.extend({ rateLimit: 1000 }) 来设置延迟刷新。比如在不断往可观察对象数组中插入元素时,可以设置一个周期时间1000ms,让1000ms内的所有操作集中到一次刷新中去,避免频繁操作 DOM 带来的性能恶化。

WeX5中如何使用ko?
    WeX5作为Html5 开发工具界的翘楚,少不了集成优秀的ko框架,使用的方法非常简单:在可视化编辑器中指定组件的bind属性,然后在js代码中操作相应绑定值。
    先在可视化编辑器中指定:
    
    这种方法在hello world篇也有简单介绍,不熟悉的同学可以先去看看哈。通过可视化编辑器我们就可以绑定相应的属性或者事件了,这里我们为 bind-ref 绑定了一个字符串“hello world”,至于其他的属性和事件将在下一篇中介绍。绑定后我们打开代码编辑器,发现里面并没有出现1)那样的绑定代码。那绑定代码写到哪里去了呢?请打开HTML源码:

可以看见代码中出现了“bind-ref=‘Hello World’”这个跟上文说的data-bind是不是很相似呢?这里WeX5将每个组件可以绑定的属性都添加到可视化编辑器中,这样就不用再去记某个组件可以绑什么属性了,鼠标指哪就绑哪!当然绑定字符串意义不大, 我们一般会绑定一个变量(实际上是返回值为所需变量的函数对象)。例如:

这里绑定了text为myText,这种形式的绑定为直接绑定在model对象下的,所以可以在js源码中的Model下操作这个myText对象。
1 define(function(require){ 2 var $ = require("jquery"); 3 var justep = require("$UI/system/lib/justep"); 4 5 var Model = function(){ 6 this.callParent(); 7 this.myText = justep.Bind.observable("bind!"); 8 }; 9 Model.prototype.button2Click = function(event){ 10 this.myText.set("changed"); 11 }; 12 return Model; 13 }); 14
    可以看到ko组件已经被封装到justep的Bind对象里面去了,另外对可观察对象的操作也跟ko中有点不同,这里采用的是set/get分别来设置和获取可观察对象的值。其他诸如compute等大部分方法的用法跟ko中一致。
总结
本篇主要简单介绍了WeX5中数据绑定的由来和背后的优秀框架(knockoutjs),着重介绍了ko中最重要的概念:可观察对象(数组),然后简单示范了如何在WeX5中使用绑定机制以及 WeX5中的绑定与ko中的差异点。
关于可观察对象的简单介绍就到这里了,下一篇将具体介绍各种绑定的用法!码字不易,随手点赞哈~
参考资料:
1. ko官方教程:http://knockoutjs.com/documentation/introduction.html
2. WeX5绑定教程:http://docs.wex5.com/data-bind-instro/

时间: 2024-11-02 23:38:41

【分享】WeX5的正确打开方式(3)——绑定机制的相关文章

【分享】WeX5的正确打开方式(7)——数据组件详解

本文是[WeX5的正确打开方式]系列的第7篇文章,详细介绍WeX5中数据组件的增删改查以及数据定位方法. 前言 上一篇 数据组件初探 我们简单介绍了数据组件的引入和底层结构以及特性,并实现了简单的数据展示和数据改动.上一篇中简单提到了,数据组件就是后台数据库表在前端的一个映射,前后端开发人员只要预定好数据组件的内容,就可以简单实现前后端开发的分离.既然数据组件是一个数据库表的映射,那它的基本结构就是一张 2 维表格,举个栗子,定义一个 html5开发工具 的数据组件,xid 为 devtool

【分享】WeX5的正确打开方式(1)

最近在研究WeX5,想在这里记录下使用过程中的点滴,今天先把之前已经掌握的分享一下. WeX5官方的开发指南难度系数较大,面向的受众可能是已经敲过上万行代码的html5 app开发者.但是作为一个刚进入代码世界的html5 app开发宝宝,本人表示拿着这份指南也找不到"南", 先抛开官方版的,下面就来看看进击版的Hello World吧. 基础部分 1.准备工作:打开hml5 app 开发工具--WeX5(路径:WeX5\studio\studio.exe),在左侧模型资源管理器中建立

【分享】WeX5的正确打开方式(2)

我的上篇文章介绍了WeX5中基本的页面布局和交互事件写法,有人私信我为什么源码中的js要那样写,HTML源码的结构是怎样的之类.那今天就总结一下Hello World的源码结构,才识有限,有疑问欢迎大家继续私信我,一起钻研 ,一起进步. HTML部分 在上篇Hello World的案例中,我们是通过新建一个w文件来进行页面样式布局的,这里可能有同学会疑惑:怎么没看见HTML文件呢?最终的HTML文件在哪?而且w文件的结构也跟我们常见的网页结构不同啊,常规的<hear>.<body>

关于格子之表白的正确打开方式

飞碟说69期:<表白的正确打开方式>优酷视频在线观看.你可能想不通, 我身高177.体重117.长相不错.家庭不错.成绩好.体育好.器大活还好,为啥她不喜欢我?飞碟君教你三招: 知己知彼才好下套.自恋和自卑是表白大忌.再牛的技术也比不上真情流露 .自古表白多白表,要经历多少次“十动然拒”,才能学会表白的正确打开方式? 飞碟说谈恋爱系列: 表白的正确打开方式 文字版台词 闽江学院男生赖国森向他心仪的蕾蕾,送出了一封212天写的16万字的情书,内容是回忆两人一起吃过的沙县,他将其装订成册,并取名&

Xcode 的正确打开方式——Debugging(转载)

Xcode 的正确打开方式——Debugging 程序员日常开发中有大量时间都会花费在 debug 上,从事 iOS 开发不可避免地需要使用 Xcode.这篇博客就主要介绍了 Xcode 中几种能够大幅提升代码调试效率的方式. “If debugging is the process of removing bugs, then programming must be the process of putting them in.”——Edsger W. Dijkstra 添加条件 有时候我们可

swf文件的正确打开方式

软件下载链接在此,可不必看下面的解释 最近在看新东方的英语口语视频教程(<新东方4+1英语口语教程>,也在此向大家推荐一下),可是在播放过程中,老是出现自己跳转到视频的起始位置并暂停的状况,很让人抓狂,我试了好几款视频播放器,结果都是一样.想着可能是视频文件的问题吧,也就没管它了,迁就着看了几集. 可是,第二天我贼心不死,百度之.在筛掉几个错误答案之后,发现了这款ZzFlash播放器,下载试了下,视频果然不跳了. 原来视频跳转的原因是:课程设定有跟读的内容环节,一般的视频播放器没有识别出这个东

转载:淘宝前端团队的干货《论版本号的正确打开方式》

引用原文评论的一句话:条理清晰, 如此甚叼! 论版本号的正确打开方式 作者: 法海 发表于: 2016-08-04 版本号广泛运用于开发的各种场景:NPM 包的版本定义.对 NPM 包的特定版本的依赖指定.git 的 daily 版本号分支…… 面对如此多的场景,版本号的命名却存在很大问题.举些例子: 开始写一个新项目 / 模块时,不管三七二十一,都从 0.0.1 起版本,直到项目不再维护时,版本还停留在 0.0.48,前两位永远都是 0. API 变化巨大,而版本号雷打不动一步一个脚印.一个二

任务队列和异步接口的正确打开方式(.NET Core版本)

任务队列和异步接口的正确打开方式 什么是异步接口? Asynchronous Operations Certain types of operations might require processing of the request in an asynchronous manner (e.g. validating a bank account, processing an image, etc.) in order to avoid long delays on the client si

区块链技术是版权保护的“正确打开方式”

21世纪是互联网的时代,也是知识经济时代.在互联网发展成熟的现阶段,网络成为了众人获取信息.知识.消遣的主要渠道.越来越多的人开始进入新媒体,网络的创作者人数也日益增加,同时网络上也带来了大量的网络版权问题.今天墨者安全就给大家说一下版权保护方面的事情. 为什么说区块链技术是版权保护的正确打开方式呢? 首先是因为区块链独有的特性,有着去中心化.高安全性.不可篡改.公开透明的特性.通过区块链技术可以完整的记录作品和创作者的详细信息,并全部公开:所有的节点都可以共同记录,共同维护:并且盖上不可篡改的