vue 源码自问自答-响应式原理

最近看了 Vue 源码和源码分析类的文章,感觉明白了很多,但是仔细想想却说不出个所以然。

所以打算把自己掌握的知识,试着组织成自己的语言表达出来

不打算平铺直叙的写清楚 vue 源码的前因后果和全部细节,而是以自问自答的形式,回答我自己之前的疑惑,

如果有错误的地方,欢迎指正哈~

Vue 的双向数据绑定原理

Vue 实现响应式的核心 API 是 ES5 的 Object.defineProperty(obj,key,descriptor),Vue 的「响应式」和「依赖收集」都依靠这个 API

它接受 3 个参数,分别是 obj / key / 描述符,返回的是一个包装后的对象

它的作用就是,用这个 API 包装过后的对象可以拥有 getter 和 setter 函数。

getter 会在对象的这个 key 被获取时触发,setter 会在这个对象的 key 被修改时触发。

一个 Vue 项目的开始, 通常是从 Vue 构造函数的实例化开始的。

new Vue()的时候会执行一个_init()方法,会初始化属性,比如 props/event/生命周期钩子,也包括 data 对象的初始化。

Vue 在初始化时,将 data 对象上的所有 key,都包装成拥有 getter 和 setter 的属性。

  • 渲染页面时,会执行 render function(无论是用 template 还是 render 最终都会生成 render 函数)
  • 执行 render function 会获取 data 对象上的属性,所以会触发对应属性的 getter 函数
  • getter 触发时,主要就做 2 个事情。 1.把值返回 2.依赖收集,也就是讲 watcher 存放到 Dep 实例的一个队列里。
  • 当修改对象的值触发 setter,setter 同样是做 2 个事情。1.把 newVal 设置好 2.用一个循环通知之前存放在 dep 实例中 watcher 对象们,watcher 对象调用各自的 update 方法来更新视图

实现双向数据绑定的demo1 - 忽略「收集依赖」的版本


function cb() {console.log("更新视图");
}function defineReactve(obj, key, val) {Object.defineProperty(obj, key, {enumerable: true,configurable: true,get: () => {console.log("触发了getter");return val;},set: newVal => {console.log("触发了setter");if (newVal === val) return;val = newVal;cb()}});
}function observe(data) {function walk(data) {Object.keys(data).forEach(key => {if (typeof data[key] === "object") {walk(data[key]);} else {defineReactve(data, key, data[key]);}});}walk(data);
}class Vue {constructor(options) {this._data = options.data;observe(this._data);}
}var vm = new Vue({data: {msg: "test",person: {name: "ziwei",age: 18}}
});vm._data.person.name = 'hello'// 触发setter和cb函数,从而视图更新

实现双向数据绑定的demo2 - 「收集依赖」的版本

function defineReactve( obj, key, val ) {const dep = new Dep()Object.defineProperty( obj, key, {enumerable: true,configurable: true,get: () => {console.log( "触发了getter" );dep.addSub(Dep.target)return val;},set: newVal => {console.log( "触发了setter" );if ( newVal === val ) return;val = newVal;dep.notify()  // 通知队列的wather去update视图}} );}function observe( data ) {function walk( data ) {Object.keys( data ).forEach( key => {if ( typeof data[ key ] === "object" ) {walk( data[ key ] );} else {defineReactve( data, key, data[ key ] );}} );}walk( data );}class Dep{constructor(){this.subs = []}addSub(){this.subs.push(Dep.target)}notify(){this.subs.forEach(sub => {sub.update()})}}Dep.target = nullclass Watcher{constructor(){Dep.target = this}update(){console.log('update更新视图啦~')}}class Vue {constructor( options ) {this._data = options.data;observe( this._data );new Watcher()           // 模拟页面渲染,触发getter,依赖收集的效果this._data.person.name}}var vm = new Vue( {data: {msg: "test",person: {name: "ziwei",age: 18}}} );vm._data.person.name = 'hello'

一些省略掉的环节

这样就是 Vue 响应式的一个基本原理,不过我描述的过程中,也省略了很多环节,比如

  • Vue 是如何实现给 data 对象上的属性都拥有 getter 和 setter 的
  • 为什么要进行「依赖收集」,
  • 如何避免重复「收集依赖」
  • watcher 调用 update,也并不是直接更新视图。实现上中间还有 patch 的过程以及使用队列来异步更新的策略。

Vue 是如何实现给 data 对象上的属性都拥有 getter 和 setter 的


通过循环data对象,给对象的每一个key,用Object.defineProperty包装遍历时,如果发现data[key]也是对象的话,需要用递归

为什么要进行「依赖收集」?

举2个场景的栗子?

  • 1.如果我们不进行依赖收集,那页面里有2处使用了data上的数据,当这个数据发生变化时,会触发setter,然后setter当中进行视图更新。

但问题在于,你并不知道谁依赖我?我应该更新哪几个地方

  • 2.如果没有依赖收集,我们修改了data中的msg,按说我们要触发setter更新视图,但是如果视图里并没有用到data.msg的话,实际上不应该更新的。

如何避免重复「收集依赖」

在往Dep中放wather对象是,实际上wather的update方法时,会把其放入queue队列中,会通过watch.id判断是否重复了,重复的wather就不会被推入队列

watcher 调用 update,也并不是直接更新视图。实现上中间还有 patch 的过程以及使用队列来异步更新的策略。

异步更新的策略,类似于setTimeout(fn,0) ,目的就是为了避免频繁的更新dom,让页面的渲染的性能更好。

原文地址:https://segmentfault.com/a/1190000016880073

更多专业前端知识,请上 【猿2048】www.mk2048.com

vue 源码自问自答-响应式原理相关推荐

  1. 【Vue.js源码解析 一】-- 响应式原理

    前言 笔记来源:拉勾教育 大前端高薪训练营 阅读建议:建议通过左侧导航栏进行阅读 课程目标 Vue.js 的静态成员和实例成员初始化过程 首次渲染的过程 数据响应式原理 – 最核心的特性之一 准备工作 ...

  2. matlabeig函数根据什么原理_vue3.0 源码解析二 :响应式原理(下)

    一 回顾上文 上节我们讲了数据绑定proxy原理,vue3.0用到的基本的拦截器,以及reactive入口等等.调用reactive建立响应式,首先通过判断数据类型来确定使用的hander,然后创建p ...

  3. Vue设计模式,发布订阅,响应式原理(简略版)

    Vue mvvm框架是什么? mvvm框架(model-view-viewMode),本质是mvc框架的改进版,mvc框架一旦项目复杂度越来越高,代码量大,维护起来很难,尤其管理层,controlle ...

  4. Vue源码学习之Computed与Watcher原理

    前言  computed与watch是我们在vue中常用的操作,computed是一个惰性求值观察者,具有缓存性,只有当依赖发生变化,第一次访问computed属性,才会计算新的值.而watch则是当 ...

  5. 手机壁纸网站源码 带全自动采集 响应式手机电脑端模板

    介绍: 可以看很多手机壁纸图片哦! 源码采用bootstrap和ajax异步获取数据,响应式的页面 在电脑和手机端的体验都不错. 网盘下载地址: http://kekewl.net/tyRzITFdH ...

  6. java官网门户源码 SSM框架 自适应-响应式 freemarker 静态模版引擎

    来源:http://www.fhadmin.org/webnewsdetail3.html 前台:支持(5+1[时尚单页风格])六套模版,可以在后台切换 官网:www.fhadmin.org 系统介绍 ...

  7. wap商城源码php,ecshop商城源码html5手机wap响应式自适应php网站模板带后端dossy

    购买须知: (1)因送吗安装费用的调整,故需要安装的用户请先联络我们!无联络硬拍的慎重! (2)联络好了安装的用户请自行备好服务器域名等... (3)素质低下,贪小便宜,追求完美者请绕道! (4)手动 ...

  8. 【Vue源码初探】五.diff算法原理

    五.diff算法原理 文章目录 五.diff算法原理 一.基本Diff算法 不是同一个节点 同一个节点 递归比较儿子节点 二.完整的Diff算法 在开头和结尾新增元素 头移尾,尾移头 图示 乱序比对 ...

  9. Vue源码之渲染watcher

    1. 前文回顾 在上一篇文章<Vue源码之计算属性watcher>中,我们学习了计算属性watcher是如何与计算属性的computedGetter协作,在计算属性所依赖的数据发生变化时, ...

最新文章

  1. 操作系统学习:Linux0.12文件异步IO
  2. Windows GPT磁盘GUID结构详解
  3. yii2史上最简单式安装教程,没有之一
  4. 计算机网络实验报告嗅探器,计算机网络实验(Wireshark)
  5. 8-2:C++继承之父类和子类对象赋值转换(公有继承)也即切片
  6. 高频交易的思路模型简介
  7. 牛腩新闻发布系统--总结
  8. 金蝶记账王和易记账哪个好_金蝶易记账和记账王的区别是什么?金蝶易记账的具体操作如下...
  9. Paper Reading:BigGAN
  10. 建筑工程PPP项目财务风险控制探析
  11. Matlab的对角阵、三角阵,矩阵变换:矩阵的转置、旋转、翻转、求逆、方阵的行列式、矩阵的秩求解
  12. 熊出没电锯机器人哪一集_熊出没伐木机器人第几集 熊出没光头强造伐木机器人是哪一集?...
  13. 转载: 找不到MSVCR90.dll、Debug vs Release及cppLapack相关
  14. 沃尔沃旗下豪华纯电动轿跑Polestar 2全球首发
  15. PostMan测试接口,出现415报错,Unsupported Media Type
  16. 编程初学者如何缓解迷茫和焦虑?墙裂推荐此文,助你赢在起跑线
  17. android listview 的下拉刷新
  18. (JAVA练习)输入一个四位数,各个位的数字相加
  19. 10种基于MATLAB的方程组求解方法
  20. dparsf是什么_用rs-fmri数据画脑功能连接图

热门文章

  1. 手把手教你部署Docker(手撸官网)
  2. 蓝桥杯 填数字游戏 20分。
  3. android webview goback 刷新,解决webview调用goBack()返回上一页自动刷新闪白的情况
  4. 爬山法求解八皇后问题的全部解法
  5. 5.8日 ksjsb 小黄鸟抓快手ck教程
  6. [10.21][转贴][中国][成龙作品集34部][DVD-RMVB/16.2G][中文字幕/影片截图]
  7. OneNote中英文格式不同,OneNote无法修改英文字体,OneNote默认英文字体为Calibri无法修改的问题。
  8. 28岁程序员从字节退休:IT是改命的唯一出路吗?
  9. 小米手机深陷“返修门” 售后成软肋
  10. flex布局下文字超出省略号代替不起作用解决方法