双向数据绑定是现在前端框架中很流行的一种技术,当前最流行的三大前端框架AngularJS、React、Vue均对其做了实现。通过 View <-> ViewModal <=> Modal
的mvvm模型,使View的变化能触发Model的修改,Model的变化也能触发View的修改。本文不讨论双向数据绑定的功能实现,仅仅从最简单的层面分析其优势。
业务逻辑
假设有如下业务场景:
事件驱动
在未实现双向数据绑定时,我们的所有前端操作都属于事件驱动型,也就是为不同的DOM附加各种EventLIstener,当事件触发时,对页面和数据进行操作。
下面以jquery为例对上述逻辑进行实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <input id="A"/> <input id="B"/> <input id="C"/>
<script> $("#A").on("keydown", function() { $("#B").val($(this).val()) $("#C").val($(this).val()) }) $("#B").on("keydown", function() { $("#A").val($(this).val()) $("#C").val($(this).val()) }) $("#C").on("keydown", function() { $("#A").val($(this).val()) $("#B").val($(this).val()) }) </script>
|
可见上述逻辑看起来非常简单,但实际业务场景复杂度远远超过这个,当一个DOM发生变化时,也许要操作多个DOM,所以,双向数据绑定诞生了。
数据驱动
双向数据绑定后,所有的变化都是由数据的变化来驱动的,所以称为数据驱动型。同样来解决上面提到的业务需求,这里以vue为例来写代码,angular和react的代码逻辑类似。如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| <input id="A" v-bind:value="inputVal"/> <input id="B" v-bind:value="inputVal"/> <input id="C" v-bind:value="inputVal"/>
<script> export default { data() { return { inputVal: '' } } } </script>
|
可以看到上面的代码非常简单,没有任何事件的监听,其中v-bind:value='inputVal'
里的v-bind:
表示将input
的属性value
与下面数据集中的inputVal
属性绑定,甚至可以更简单的写成:value="inputVal"
。与事件驱动型的代码比较起来,仅仅看代码的行数就少了50%。
更复杂的例子
我们下面看一个更复杂的例子,来比较事件驱动和数据驱动的开发工作量。
例如:一个任务卡片有4个状态(未提交、运行、完成、错误),显示在任务卡的状态栏中,还有5个功能按钮(运行、删除、编辑、详情、日志),需要根据任务卡的状态来修改状态栏文本,以及判断5个功能按钮是否应该显示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| <div class="card"> <div id="status">未提交</div> <button id="run">运行</button> <button id="delete">删除</button> <button id="edit">编辑</button> <button id="reset">重置</button> <button id="log">日志</button> </div>
<script> var changeWithStatus = function (status) { $("#status").html(status) switch(status) { case "未提交": $("#run").show(); $("#delete").show(); $("#edit").show(); $("#reset").show(); $("#log").hide(); break; case "运行": $("#run").hide(); $("#delete").hide(); $("#edit").hide(); $("#reset").hide(); $("#log").hide(); break; case "完成": $("#run").hide(); $("#delete").hide(); $("#edit").hide(); $("#reset").show(); $("#log").show(); break; case "错误": $("#run").show(); $("#delete").show(); $("#edit").show(); $("#reset").hide(); $("#log").show(); break; } } </script>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <div class="card"> <div>{{ status }}</div> <button v-show="canOperater">运行</button> <button v-show="canOperater">删除</button> <button v-show="canOperater">编辑</button> <button v-show="status == '完成'">重置</button> <button v-show="status == '完成' || status == '错误'">日志</button> </div> <script> export default { data() { return { status: '' } }, methods: { canOperate: function() { return this.status == '未提交' || status =='错误'" } } } </script>
|
综合上面的例子来看,实现了双向数据绑定的框架,代码逻辑更加清晰,代码量少了一半左右,开发效率更高。