数据视图双向绑定(jQuery 实现)

最近在做一个购物车相关的项目,前端开发的需求无非就是点货物数量两边的『加号』和『减号』,或者直接修改购物车数量里的数字(文本框),可以实时改变购物车总金额数量的显示,以及加减号可点状态的变化(到数量 1 就不能再点减号,到库存最大数就不能再点加号……)。当然,这里面还涉及到很多复杂的逻辑,比如购物车里删除单品,跨境商品和普通商品必须分开结账等……

这个任务交给小朋友来实施,结果小朋友很快就把自己绕进去了。细看他们写的代码,可以用下面的伪代码来描述:

这还并不涉及商品从购物车删除,以及刚才说的跨境商品的计算,只要是正常人,是妥妥会晕的。

如果是老鸟来接手这个项目,一定会说:这种需求显然用 Angular.js 或者 Vue.js 提供的数据双向绑定功能来做要轻松得多啊!

的确,但问题是小朋友刚接触 Vue.js 这样的框架,可能对数据双向绑定的意义,以及双向绑定的实现原理会有疑惑,所以我这里将阐述这些疑惑,让大家能够理解,并且在理解的基础上用好数据双向绑定的技术。

哪首先什么是数据双向绑定呢?

我们在给页面写 JS 代码的时候,其实大部分时间都是在跟 DOM 和 DOM 上呈现的数据打交道,DOM 代表了视图,数据就是 Model。一些简单的需求,直接操作 DOM 来显示数据其实并没有太大问题,但如果需求变得很复杂,比如刚才所说的购物车的例子,直接在 DOM 里操作数字,因为显示逻辑和数据逻辑缠绕在一起,代码变得极其冗长而难以维护。如果,视图上显示的数是跟某个 JS 代码里的 Model 是绑定的关系,比如说如果 JS 代码里有一个叫 cart 的购物车对象,cart 上有个属性叫 price 表示购物车的结算价,当 cart 的 price 发生变化时,视图会自动跟着变化,不用在做数据逻辑的时候考虑视图逻辑的事情,那该多清爽。

实现视图自动跟随数据变化而变化,叫做『数据驱动视图』;相反,当视图发生事件(比如用户的点击)改变了数据,叫做『视图操作数据』。这两者结合起来就是『数据双向绑定』了。

『视图操作数据』的实现没什么好解释的,当用户点击了加号按钮,让购物车对象里与之对应的商品对象的购买数加 1,这应该大家都会。而『数据驱动视图』的实现,就不是那么直白了。

这里我提供的数据驱动视图的思路是:使用事件监听机制。

事件监听本质上是使用的『观察者模式』来实现的,关于『观察者模式』的具体信息可以查看多年前我在 segmentfault.com 网站上的一个回答。大家都知道在 JS 里可以多次绑定一个按钮的 onclick 事件,当这个按钮被用户点击后,所有被绑定的事件处理代码都会依次执行。其实『数据驱动视图』也一样,当数据模型里的某个变量被更新,如果能抛出事件,所有要跟随变化的视图的处理,都可以通过监听此事件而得到解决。

一个好消息是,jQuery 这样的民工库,是支持任意对象抛出事件的:

这样,我就可以将工作分成两部分,一部分是关注 cart 数据逻辑本身,以及抛出合适的事件,视图的事情一概不管,而另一部分工作,则只关心一件事:当 cart 的结算方法完成时,视图应该如何更新,从而实现分拆关注点,方便理清思路,以及实现团队分工的目的:

为了体现『数据双向绑定』后代码的简洁易懂性,下面我把给小朋友们写的示例代码全部贴出来(使用 ES6 语法实现,需要在现代浏览器里执行):

上面示例代码并没有实现所有购物车功能,可能还有 bug,但双向绑定的含义应该是阐述清楚了。

可能 Vue.js 和 Angular.js 并不一定是通过这种方式来实现的(我猜测很有可能是通过 Object.defineProperty 来实现),但原理都一样,这些框架的作用——也是他们方便的地方——就是省略了给每个数据和视图做绑定的过程。

2017-03-30 补充:关于使用 Object.defineProperty 方法来实现双向绑定,我也写了一个示例,请点此查看

通过『双向绑定』的思想作为工具,可以简化我们的代码,但要用好这个工具,必须保持一个原则,否则还是容易『把自己绕进去』,这个原则就是:数据可以改变别的数据(数据内部逻辑),数据可以驱动视图,视图可以改变数据,但视图一定不要直接改变另外一个视图(除非视图对数据没有改变)(比如点加号后直接改变单品总价格和购物车总价格的显示)。

希望能给大家带来一些帮助。

wx pay

CC BY-NC-ND 4.0 数据视图双向绑定(jQuery 实现) by Chrisyue's Blog is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.

数据视图双向绑定(jQuery 实现)》上有2条评论

发表评论

电子邮件地址不会被公开。