深入理解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 相关文章推荐
鼠标经过的文本框textbox变色
May 21 Javascript
javascript算法学习(直接插入排序)
Apr 12 Javascript
jQuery中end()方法用法实例
Jan 08 Javascript
js+cookies实现悬浮购物车的方法
May 25 Javascript
JS实现仿新浪微博发布内容为空时提示功能代码
Aug 19 Javascript
深入理解JavaScript单体内置对象
Jun 06 Javascript
浅谈js中同名函数和同名变量的执行问题
Feb 12 Javascript
求js数组的最大值和最小值的四种方法
Mar 03 Javascript
JS闭包的几种常见形式实例详解
Sep 16 Javascript
js+html5生成自动排列对话框实例
Oct 09 Javascript
详解Webpack实战之构建 Electron 应用
Dec 25 Javascript
微信小程序 如何引入外部字体库iconfont的图标
Jan 31 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
PHP5.4中json_encode中文转码的变化小结
2013/01/30 PHP
使用php实现下载生成某链接快捷方式的解决方法
2013/05/07 PHP
getimagesize获取图片尺寸实例
2014/11/15 PHP
纯PHP代码实现支付宝批量付款
2015/12/24 PHP
php 下 html5 XHR2 + FormData + File API 上传文件操作实例分析
2020/02/28 PHP
javascript 动态生成私有变量访问器
2009/12/06 Javascript
修改jquery里的dialog对话框插件为框架页(iframe) 的方法
2010/09/14 Javascript
Moment.js 不容错过的超棒Javascript日期处理类库
2012/04/15 Javascript
一个CSS+jQuery实现的放大缩小动画效果
2014/02/19 Javascript
使用js dom和jquery分别实现简单增删改
2014/09/11 Javascript
javascript实现的简单计时器
2015/07/19 Javascript
JS判断是否长按某一键的方法
2016/03/02 Javascript
node.js cookie-parser 中间件介绍
2016/06/06 Javascript
一步一步封装自己的HtmlHelper组件BootstrapHelper(三)
2016/09/14 Javascript
在vue.js中抽出公共代码的方法示例
2017/06/08 Javascript
如何在 JavaScript 中更好地利用数组
2018/09/27 Javascript
js实现前面自动补全位数的方法
2018/10/10 Javascript
nodejs读取本地中文json文件出现乱码解决方法
2018/10/10 NodeJs
vue实现侧边栏导航效果
2019/10/21 Javascript
修改Python的pyxmpp2中的主循环使其提高性能
2015/04/24 Python
在Python中关于使用os模块遍历目录的实现方法
2019/01/03 Python
Python3实现统计单词表中每个字母出现频率的方法示例
2019/01/28 Python
Python搭建代理IP池实现存储IP的方法
2019/10/27 Python
pygame实现成语填空游戏
2019/10/29 Python
python 错误处理 assert详解
2020/04/20 Python
使用pygame实现垃圾分类小游戏功能(已获校级二等奖)
2020/07/23 Python
HTML5之SVG 2D入门1—SVG(可缩放矢量图形)概述
2013/01/30 HTML / CSS
日本高端护肤品牌:Tatcha
2016/08/29 全球购物
如何利用cmp命令比较文件
2013/09/23 面试题
小学生考试获奖感言
2014/01/30 职场文书
中考冲刺决心书
2014/03/11 职场文书
餐厅服务员岗位职责
2015/02/09 职场文书
农村结婚典礼主持词
2015/06/29 职场文书
React forwardRef的使用方法及注意点
2021/06/13 Javascript
win10蓝屏0xc0000001安全模式进不了怎么办?win10出现0xc0000001的解决方法
2022/08/05 数码科技
SQLServer常见数学函数梳理总结
2022/08/05 MySQL