深入理解Vue 单向数据流的原理


Posted in Javascript onNovember 09, 2017

单向数据流是什么

单向数据流指只能从一个方向来修改状态。下图是单向数据流的极简示意:

深入理解Vue 单向数据流的原理

单向数据流的极简示意

与单向数据流对对应的是双向数据流(也叫双向绑定)。在双向数据流中,Model(可以理解为状态的集合) 中可以修改自己或其他Model的状态, 用户的操作(如在输入框中输入内容)也可以修改状态。这使改变一个状态有可能会触发一连串的状态的变化,最后很难预测最终的状态是什么样的。使得代码变得很难调试。如下图所示:

深入理解Vue 单向数据流的原理

与双向数据流比,在单向数据流中,当你需要修改状态,完全重新开始走一个修改的流程。这限制了状态修改的方式,让状态变得可预测,容易调试。

单向数据流的使用场景

多个组件会共享状态时,共享状态和组件间(兄弟组件)通信变的不容易。我们把共享状态抽取出来,用单向数据流的方式会变得容易。

从v-model开始讲起

1、v-model 用在 input 元素上

v-model在使用的时候很像双向绑定的(实际上也是。。。),但是 Vue 是单项数据流,v-model 只是语法糖而已:

<input v-model="something" />
<input v-bind:value="something" v-on:input="something = $event.target.value" />

第一行的代码其实只是第二行的语法糖。然后第二行代码还能简写成这样:

<input :value="something" @input="something = $event.target.value" />

要理解这行代码,首先你要知道 input 元素本身有个 oninput 事件,这是 HTML5 新增加的,类似 onchange ,每当输入框内容发生变化,就会触发 oninput ,通过 $event 把最新的 value 传递给 something。

我们仔细观察语法糖和原始语法那两行代码,可以得出一个结论: 在给 input 元素添加 v-model 属性时,默认会把 value 作为元素的属性,然后把 'input' 事件作为实时传递 value 的触发事件

2、v-model 用在组件上

v-model 不仅仅能在 input 上用,在组件上也能使用,拿官网上的demo看。

<currency-input v-model="price"></currency-input>
Vue.component('currency-input', {
 template: '\
 <span>\
  $\
  <input\
  ref="input"\
  v-bind:value="value"\
  v-on:input="updateValue($event.target.value)"\
  >\
 </span>\
 ',
 props: ['value'], // 为什么这里要用 value 属性,value在哪里定义的?
 methods: {
 // 不是直接更新值,而是使用此方法来对输入值进行格式化和位数限制
 updateValue: function (value) {
  var formattedValue = value
  // 删除两侧的空格符
  .trim()
  // 保留 2 位小数
  .slice(
   0,
   value.indexOf('.') === -1
   ? value.length
   : value.indexOf('.') + 3
  )
  // 如果值尚不合规,则手动覆盖为合规的值
  if (formattedValue !== value) {
  this.$refs.input.value = formattedValue
  }
  // 通过 input 事件带出数值
  // <!--为什么这里把 'input' 作为触发事件的事件名?`input` 在哪定义的?-->
  this.$emit('input', Number(formattedValue))
 }
 }
})

如果你知道这两个问题的答案,那么恭喜你真正掌握了 v-model,如果你没明白,那么可以看下这段代码:

<currency-input v-model="price"></currency-input>
所以在组件中使用时,它相当于下面的简写:
//上行代码是下行的语法糖
<currency-input :value="price" @input="price = arguments[0]"></currency-input>

所以,给组件添加 v-model 属性时,默认会把 value 作为组件的属性,然后把 'input' 值作为给组件绑定事件时的事件名。这在写组件时特别有用。

3、v-model 的缺点和解决办法

在创建类似复选框或者单选框的常见组件时,v-model就不好用了。

<input type="checkbox" v-model="something" />

v-model 给我们提供好了 value 属性和 oninput 事件,但是,我们需要的不是 value 属性,而是 checked 属性,并且当你点击这个单选框的时候不会触发 oninput 事件,它只会触发 onchange 事件。

因为 v-model 只是用到了 input 元素上,所以这种情况好解决:

<input type="checkbox" :checked="value" @change="change(value, $event)"

当 v-model 用到组件上时:

<checkbox v-model="value"></checkbox>

Vue.component('checkbox', {
 tempalte: '<input type="checkbox" @change="change" :checked="currentValue"/>'
 props: ['value'],
 data: function () {
  return {
   //这里为什么要定义一个局部变量,并用 prop 的值初始化它。
   currentValue: this.value
  };
 },
 methods: {
 change: function ($event) {
  this.currentValue = $event.target.checked;
  this.$emit('input', this.currentValue); 
 }
})

在 Vue 2.2 版本,你可以在定义组件时通过 model 选项的方式来定制 prop/event 。

4、vue 组件数据流

从上面 v-model 的分析我们可以这么理解,双向数据绑定就是在单向绑定的基础上给可输入元素(input、textare等)添加了 change(input) 事件,来动态修改 model 和 view ,即通过触发($emit)父组件的事件来修改mv来达到 mvvm 的效果。而 vue 组件间传递数据是单向的,即数据总是由父组件传递到子组件,子组件在其内部可以有自己维护的数据,但它无权修改父组件传递给它的数据,当开发者尝试这样做的时候,vue 将会报错。这样做是为了组件间更好的解耦,在开发中可能有多个子组件依赖于父组件的某个数据,假如子组件可以修改父组件数据的话,一个子组件变化会引发所有依赖这个数据的子组件发生变化,所以 vue 不推荐子组件修改父组件的数据,直接修改 props 会抛出警告。流程图如下:

深入理解Vue 单向数据流的原理

所以,当你想要在子组件去修改 props 时,把这个子组件当成父组件那样用,所以就有了

1、定义一个局部变量,并用 prop 的值初始化它。
2、定义一个计算属性,处理 prop 的值并返回。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
通过下拉框的值来确定输入框是否可以为空的代码
Oct 18 Javascript
jquery获取div距离窗口和父级dv的距离示例
Oct 10 Javascript
jquery ajax post提交数据乱码
Nov 05 Javascript
自己实现ajax封装示例分享
Apr 01 Javascript
jQuery实现防止提交按钮被双击的方法
Mar 24 Javascript
jquery实现的树形目录实例
Jun 26 Javascript
jQuery插件扩展extend的简单实现原理
Jun 24 Javascript
利用JS提交表单的几种方法和验证(必看篇)
Sep 17 Javascript
js前端日历控件(悬浮、拖拽、自由变形)
Mar 02 Javascript
jQuery实现的粘性滚动导航栏效果实例【附源码下载】
Oct 19 jQuery
详解vuex结合localstorage动态监听storage的变化
May 03 Javascript
通过JS深度判断两个对象字段相同
Jun 14 Javascript
node.js基于express使用websocket的方法
Nov 09 #Javascript
angular2系列之路由转场动画的示例代码
Nov 09 #Javascript
使用ef6创建oracle数据库的实体模型遇到的问题及解决方案
Nov 09 #Javascript
基于vue配置axios的方法步骤
Nov 09 #Javascript
微信小程序倒计时功能实现代码
Nov 09 #Javascript
js与jQuery实现的用户注册协议倒计时功能实例【三种方法】
Nov 09 #jQuery
微信小程序获取手机号授权用户登录功能
Nov 09 #Javascript
You might like
DedeCMS 核心类TypeLink.class.php摘要笔记
2010/04/07 PHP
php删除页面记录 同时刷新页面 删除条件用GET方式获得
2012/01/10 PHP
php解压文件代码实现php在线解压
2014/02/13 PHP
php表单请求获得数据求和示例
2014/05/15 PHP
PHP使用redis实现统计缓存mysql压力的方法
2015/11/14 PHP
php版微信开发之接收消息,自动判断及回复相应消息的方法
2016/09/23 PHP
thinkPHP框架实现的无限回复评论功能示例
2018/06/09 PHP
插件:检测javascript的内存泄漏
2007/03/04 Javascript
javascript限制用户只能输汉字中文的方法
2014/11/20 Javascript
jQuery实现高亮显示网页关键词的方法
2015/08/07 Javascript
谈谈对offsetleft兼容性的理解
2015/11/11 Javascript
JavaScript实现iframe自动高度调整和不同主域名跨域
2016/02/27 Javascript
AngularJS教程之环境设置
2016/08/16 Javascript
JavaScript 网页中实现一个计算当年还剩多少时间的倒数计时程序
2017/01/25 Javascript
基于Vue的文字跑马灯组件(npm 组件包)
2017/05/24 Javascript
微信小程序基于本地缓存实现点赞功能的方法
2017/12/18 Javascript
使用sessionStorage解决vuex在页面刷新后数据被清除的问题
2018/04/13 Javascript
2种在vue项目中使用百度地图的简单方法
2018/09/28 Javascript
vue 父组件通过v-model接收子组件的值的代码
2019/10/27 Javascript
微信小程序自定义模态弹窗组件详解
2019/12/24 Javascript
使用vue实现一个电子签名组件的示例代码
2020/01/06 Javascript
JavaScript将数组转换为链表的方法
2020/02/16 Javascript
javascript实现前端成语点击验证优化
2020/06/24 Javascript
vue组件入门知识全梳理
2020/09/21 Javascript
[04:26]2014DOTA2西雅图国际邀请赛 总决赛TOPPLAY
2014/07/22 DOTA
python实现汉诺塔递归算法经典案例
2021/03/01 Python
网站渗透常用Python小脚本查询同ip网站
2017/05/08 Python
Python使用functools实现注解同步方法
2018/02/06 Python
在Python中实现shuffle给列表洗牌
2018/11/08 Python
Python数据抓取爬虫代理防封IP方法
2018/12/23 Python
Python 输出详细的异常信息(traceback)方式
2020/04/08 Python
全球性的奢侈品梦工厂:Forzieri(福喜利)
2019/02/20 全球购物
三八妇女节趣味活动方案
2014/08/23 职场文书
2014幼儿教师个人工作总结
2014/12/03 职场文书
2015年公务员个人工作总结
2015/04/24 职场文书
如何用JS实现网页瀑布流布局
2021/04/24 Javascript