Vuejs 响应式系统

yugasun

2018.02.06 10:38*字数 2082阅读 364评论 0喜欢 1

you-may-not-know-vuejs.png

byyugasunfromhttps://yugasun.com/post/you-may-not-know-vuejs-2.html

本文可全文转载,但需要保留原作者和出处。

虽然说是Vuejs实践,但是有些重要的理论还是必不可少的,本文将简单的带你了解Vuejs的响应式原理。

Vue 最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的 Javascript 对象。而当你修改它们时,视图会进行更新。这使得状态管理非常简单直接,不过理解其工作原理同样非常重要,这样你可以回避一些常见的问题。

原理图剖析

vue-reactive-data

仔细阅读这张官方原理图,大概可以剖析为以下几个步骤:

编译组件:对特殊标记的部分(比如双大括号部分)进行替换为相应的数据值。

收集依赖:对于编译阶段依赖的数据进行监听(这个都是通过watcher对象实现的)

通知更新:当步骤2中监听的数据发生变化时,会通知watcher进行重新计算,触发关联视图更新。

可以简单理解为一个发布订阅系统,当然这里的过程介绍比较通俗,单纯是为了理解而解释的,实际流程其实还是很复杂的。如果想结合源码深入了解的,建议去阅读这篇文章:

Vue 源码解析:深入响应式原理

关于Vuejs模板

上一篇中提到,Vue实例在初始化的时候会将目标节点div#app内容进行替换,是在定义了template属性或者render函数的前提下。否则会对把div#app内容当做模板进行编译输出。一般情况下,很少使用template属性来定义模板,因为实际开发过程中,我们的模板还是很复杂的,单纯通过template属性定义,我们的代码会显得非常臃肿,不便于阅读。所以,我们通常使用的是将模板内容写在过载目标元素的内部,稍后将采用这种方法做代码演示。

当然对于复杂项目,更多的是使用单文件组件(这将在以后专题文章中介绍)。

百闻不如一见

还记得上一篇中讲述的如何实现一个简单的 Vue 应用,并通过不同的 API

实现了目标元素的渲染吧。不记得也不要紧,这里还是从最基本的代码开始。下面我们会动态的实现一个列表的渲染,并通过修改数据,来触发视图的更新,以此来感受下响应式系统的快感。先来看一段代码:

Demo1:data

添加

{{ item.name }}

varapp =newVue({el:"#app",  data () {return{count:1,list: [        {name:'Vuejs官网',url:'https://cn.vuejs.org'},        {name:'Github',url:'https://github.com'},        {name:'Yuga博客1',url:'https://yugasun.com'}      ]    }  },methods: {    addItem () {this.count++this.list.push({name:'Yuga博客'+this.count,url:'https://yugasun.com'})    }  }})

上面 Vue 实例在初始化的时候,属性data中定义了一个站点列表,然后模板中通过v-for指令进行列表渲染,同时也使用了v-bind指令来进行属性绑定,并且通过v-on指令来监听按钮的click事件来执行addItem方法。当点击添加按钮,就会向listpush一条数据,视图会再次更新。(关于指令相关文章将在下一篇中讲到,感兴趣的同学可以先到官网学习了解。)

Demo2:computed

在实际开发过程中,我们的接口请求到的数据往往会差强人意,这时就需要我们进行一些转化,来生成我们想要的数据结构,当然最直接的方式就是每次请求完数据就通过固定函数处理一遍,但是这个得手动执行。此时computed计算属性就可以用上了。

我们知道除了data中定义的数据可以再模板中使用外,computed属性也可以。只不过computed中的属性是需要先进行计算,然后再返回想要的数据的。当我们输出某个属性,必须依赖另外一个data中的属性来动态计算获得的,此时使用computed就非常简单了。代码如下:

varapp =newVue({el:"#app",  data () {return{count:1,// 实际开发中往往是通过接口请求获取requestList: ['Vuejs官网-https://cn.vuejs.org','Github-https://github.com','Yuga博客1-https://yugasun.com']    }  },computed: {list:function(){varlist = [];this.requestList.map(function(item, index){vartempArr = item.split('-');        list.push({name: tempArr[0],url: tempArr[1]        });      })returnlist;    }  },methods: {    addItem () {this.count++this.requestList.push('Yuga博客'+this.count +'-https://yugasun.com')    }  }})

关于计算属性 - computed

关于计算属性,其实有个细节是很多同学没有注意到的,计算属性实际上是可以修改的!那么如何才能修改呢?

其实计算属性不仅可以定义为一个函数,也可以定义为一个含有get/set属性的对象。当我们定义为一个函数是,Vue 内部会默认将这个函数赋值给get属性,一般set是未定义的。当我们定义set属性后,就可以对它进行修改了。来看下面一段代码:

改变姓名

{{ username }}

varapp =newVue({el:"#app",  data () {return{firstName:'Yuga',lastName:'Sun'}  },computed: {username: {get:function(){returnthis.firstName +' '+this.lastName;      },set:function(newVal){varnames = newVal.split(' ');this.firstName = names[0];this.lastName = names[1];      }    }  },methods: {    changeName () {if(this.username ==='Yuga Sun') {this.username ='Summer Wu';      }else{this.username ='Yuga Sun';      }    }  }})

当进行赋值操作this.username = 'Summer Wu'时,计算属性username的set函数就会被调用,同时也对firstName和lastName进行了相应的更新。这里看似是直接进行赋值操作,其实也是通过间接修改firstName和lastName来实现username的更新的。因为计算属性是基于它依赖的值进行缓存的,如果它依赖的值没有发生改变,它自己就没法发生改变。

关于侦听器 - watch

创建 Vue 应用时,我们还提到过watch这个属性,它其实是个对象,键是需要观察的表达式,值是对应的回调函数。值也可以是方法名,或者包含选项的对象。和上面的计算属性类似,他可以监听值/表达式的变化来执行回调函数。我们先实现上面的功能:

varapp =newVue({el:"#app",  data () {return{firstName:'Yuga',lastName:'Sun',username:'Yuga Sun'}  },watch: {firstName:function(val, oldVal){this.username = val +' '+this.lastName;    },lastName:function(val, oldVal){this.username =this.firstName +' '+ val;    }  },methods: {    changeName () {if(this.username ==='Yuga Sun') {this.firstName ='Summer';this.lastName ='Wu';      }else{this.firstName ='Yuga';this.lastName ='Sun';      }    }  }})

以上就是最基础用法,往往有些时候我们的监听属性并没有那么简单。往往是一个对象,这时当我们修改该对象的属性时,如何实现监听呢?先看下面代码:

改变姓名

{{ username }}

varapp =newVue({el:"#app",  data () {return{userinfo: {firstName:'Yuga',lastName:'Sun',      },username:'Yuga Sun'}  },watch: {userinfo:function(val, oldVal){this.username = val.firstName +' '+ val.lastName;    }  },methods: {    changeName () {if(this.username ==='Yuga Sun') {this.userinfo.firstName ='Summer'this.userinfo.lastName ='Wu'}else{this.userinfo.firstName ='Yuga'this.userinfo.lastName ='Sun'}    }  }})

此时无论我们如何点击按钮,都无法改变username的值,因为watch侦听器默认只是侦听该对象本身的赋值操作,也就是直接对this.userinfo进行赋值操作时的变化,并未对其内部属性进行侦听。实际上对于侦听的值是可以为一个对象的,它还有个deep属性,用来设置是否侦听内部属性的变化,而回调函数是通过handler来设置的。我们再次修改代码如下:

varapp =newVue({el:"#app",  data () {return{userinfo: {firstName:'Yuga',lastName:'Sun',      },username:'Yuga Sun'}  },watch: {userinfo: {deep:true,handler:function(val, oldVal){this.username = val.firstName +' '+ val.lastName;      }    }  },methods: {    changeName () {if(this.username ==='Yuga Sun') {this.userinfo.firstName ='Summer'this.userinfo.lastName ='Wu'}else{this.userinfo.firstName ='Yuga'this.userinfo.lastName ='Sun'}    }  }})

点击按钮,你会发现username可以根据按钮点击更新了,这个属性在实际项目中非常实用,因为往往我们修改数据时,并不是整体赋值,大部分时候都是局部修改属性的,所以这个时候就需要通过设置deep属性为true,来达到我们的侦听目的。

问题来了,当侦听对象包含很多属性,而我们只是需要监听其中的一个或某几个属性,这时如果我们通过这种方式侦听所有内部属性的变化,自然就会造成内存的浪费。那么能不能只侦听单一内部属性的变化呢?答案是肯定的。

其实在watch定义的时候,键是可以为需要观察的表达式的,表达式就是说明,我们是可以写成对象属性访问表达式的。比如我们只需要侦听姓氏的修改,我们可以写成userinfo.lastName。再来看下面代码:

varapp =newVue({el:"#app",  data () {return{userinfo: {firstName:'Yuga',lastName:'Sun',      },username:'Yuga Sun'}  },watch: {'userinfo.lastName':function(val, oldVal){this.username =this.userinfo.firstName +' '+ val;    }  },methods: {    changeName () {if(this.username ==='Yuga Sun') {this.userinfo.lastName ='Wu'}else{this.userinfo.lastName ='Sun'}    }  }})

这样我们就可以根据项目实际情况,灵活的使用侦听器来侦听我们所关注的属性了,赶紧动手试一试吧~

源码在此

专题目录

You-May-Not-Know-Vuejs

真诚赞赏,手留余香~

你也许不知道的Vuejs

          © 著作权归作者所有


举报文章

yugasun

写了 24514 字,被 12 人关注,获得了 26 个喜欢

热爱前端技术,喜欢钻研各种前端新技术,欢迎各位前端朋友来撩~




        更多分享

被以下专题收入,发现更多相似内容

收入我的专题

你也许不知道的...

MVVM框架的搭建(二)——项目搭建

介绍完背景以及初衷之后,我们开始搭建MVVM的框架,这一部分我们进行简单的搭建,了解MVVM架构的基本结构。

MVVM框架的搭建(一)——背景MVVM框架的搭建(二)——项目搭建MVVM框架的搭建(三)——网络请求MVVM的数据持久化(一)——ROOM的集成MVVM的数据持...

YangZC

预计2019年发布的Vue3.0到底有什么不一样的地方?

摘要: Vue 3.0预览。

原文:预计今年发布的Vue3.0到底有什么不一样的地方? 作者:小肆 微信公众号:技术放肆聊 Fundebug经授权转载,版权归原作者所有。

还有几个月距离 vue2 的首次发布就满 3 年了,而 vue 的作者尤雨溪也在去年年末发布了关于 v...

Fundebug

vue 中递归组件的用法

概念:

组件是可以在自己的模板中调用自身的,不过他们只能通过name选项来做这件事。 使用vue框架,我们会发现export default

导出的对象中有一个name属性,这个name属性是一个比较重要的属性,而且属性好处不止这一处。 用法: 1.首先我们要知道,既然是递...

廊桥梦醉

10个JavaScript难点

译者按:能够读懂这篇博客的JavaScript开发者,运气不会太差...

原文: 10 JavaScript concepts every Node.js programmer must master 译者:

Fundebug 为了保证可读性,本文采用意译而非直译,并且对示...

Fundebug

vue项目总结(2)vue-router(为做登录拦截准备)

我们开始就采用了前后端分离的开发模式;话说vue不兼容IE8;不扯太多;总结一下前端在control层面做的控制;也就是路由的跳转部分;这样才能再深一步的开发

了解一下前端路由 在说到登录拦截的时候必须要结合路由使用的 前端路由 1. hash 模式 随着 ajax 的流行...

吴高亮

《乌鸦》(中部)31

“大家谈一下那天那个凶杀案件吧,以及对前天晚上那个袭击南辉的案情做一个分析。”重新就坐的市公安局局长郑智对着大家首先说话了。紧接着又说了一句,“刑警队,之前的这个案件主要是由你们牵头负责处理,这段时间你们对案情掌握的情况都描述一下。”

刑警队赵队长说话了:“已经一个月了,没...

此城此警

大学生创业的注意事项

1.技术

用智力换资本,这是大学生创业的特色之路。一些风险投资家往往就因为看中大学生所掌握的先进技术,而愿意对其创业计划进行资助。因此,打算在高科技领域创业的大学生,一定要注意技术创新,开发具有自己独立知识产权的产品,吸引投资商。

2.能力 大学生由于长期接受应试教育,不熟...

无限纠结Cc

别让我笑我害怕

吓大的田大侠

一直特别喜欢笑,直到那一年去欧洲卢浮宫玩,在人群之中看到的蒙娜丽莎的那一双明眸,往下看去,她的微笑,真的,我再也没敢笑过,怎么形容,在公共汽车上有人对你这么笑,就是有人在扒你腰包,公共场合这样微笑看起来以为我的裤子拉链没拉好。火车站遇到陌生的女孩对我笑,很甜,...

田大侠

《巴菲特》扫读后感

今天在路上迅速扫读了前几天从得到APP收到的《巴菲特历年股东信精选》音频稿——嗯,每天需要读的东西其实很多,所以要分类精读泛读扫读之类的。

所以大概总结一下就是: 1长期很重要 2唯一确定的就是不确定性 3仍然看好未来 1长期很重要 APP的另外一个专栏《通往财富自由之路》...

艾萱_刘

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342