vue 源码自问自答-响应式原理
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 源码自问自答-响应式原理相关推荐
- 【Vue.js源码解析 一】-- 响应式原理
前言 笔记来源:拉勾教育 大前端高薪训练营 阅读建议:建议通过左侧导航栏进行阅读 课程目标 Vue.js 的静态成员和实例成员初始化过程 首次渲染的过程 数据响应式原理 – 最核心的特性之一 准备工作 ...
- matlabeig函数根据什么原理_vue3.0 源码解析二 :响应式原理(下)
一 回顾上文 上节我们讲了数据绑定proxy原理,vue3.0用到的基本的拦截器,以及reactive入口等等.调用reactive建立响应式,首先通过判断数据类型来确定使用的hander,然后创建p ...
- Vue设计模式,发布订阅,响应式原理(简略版)
Vue mvvm框架是什么? mvvm框架(model-view-viewMode),本质是mvc框架的改进版,mvc框架一旦项目复杂度越来越高,代码量大,维护起来很难,尤其管理层,controlle ...
- Vue源码学习之Computed与Watcher原理
前言 computed与watch是我们在vue中常用的操作,computed是一个惰性求值观察者,具有缓存性,只有当依赖发生变化,第一次访问computed属性,才会计算新的值.而watch则是当 ...
- 手机壁纸网站源码 带全自动采集 响应式手机电脑端模板
介绍: 可以看很多手机壁纸图片哦! 源码采用bootstrap和ajax异步获取数据,响应式的页面 在电脑和手机端的体验都不错. 网盘下载地址: http://kekewl.net/tyRzITFdH ...
- java官网门户源码 SSM框架 自适应-响应式 freemarker 静态模版引擎
来源:http://www.fhadmin.org/webnewsdetail3.html 前台:支持(5+1[时尚单页风格])六套模版,可以在后台切换 官网:www.fhadmin.org 系统介绍 ...
- wap商城源码php,ecshop商城源码html5手机wap响应式自适应php网站模板带后端dossy
购买须知: (1)因送吗安装费用的调整,故需要安装的用户请先联络我们!无联络硬拍的慎重! (2)联络好了安装的用户请自行备好服务器域名等... (3)素质低下,贪小便宜,追求完美者请绕道! (4)手动 ...
- 【Vue源码初探】五.diff算法原理
五.diff算法原理 文章目录 五.diff算法原理 一.基本Diff算法 不是同一个节点 同一个节点 递归比较儿子节点 二.完整的Diff算法 在开头和结尾新增元素 头移尾,尾移头 图示 乱序比对 ...
- Vue源码之渲染watcher
1. 前文回顾 在上一篇文章<Vue源码之计算属性watcher>中,我们学习了计算属性watcher是如何与计算属性的computedGetter协作,在计算属性所依赖的数据发生变化时, ...
最新文章
- 操作系统学习:Linux0.12文件异步IO
- Windows GPT磁盘GUID结构详解
- yii2史上最简单式安装教程,没有之一
- 计算机网络实验报告嗅探器,计算机网络实验(Wireshark)
- 8-2:C++继承之父类和子类对象赋值转换(公有继承)也即切片
- 高频交易的思路模型简介
- 牛腩新闻发布系统--总结
- 金蝶记账王和易记账哪个好_金蝶易记账和记账王的区别是什么?金蝶易记账的具体操作如下...
- Paper Reading:BigGAN
- 建筑工程PPP项目财务风险控制探析
- Matlab的对角阵、三角阵,矩阵变换:矩阵的转置、旋转、翻转、求逆、方阵的行列式、矩阵的秩求解
- 熊出没电锯机器人哪一集_熊出没伐木机器人第几集 熊出没光头强造伐木机器人是哪一集?...
- 转载: 找不到MSVCR90.dll、Debug vs Release及cppLapack相关
- 沃尔沃旗下豪华纯电动轿跑Polestar 2全球首发
- PostMan测试接口,出现415报错,Unsupported Media Type
- 编程初学者如何缓解迷茫和焦虑?墙裂推荐此文,助你赢在起跑线
- android listview 的下拉刷新
- (JAVA练习)输入一个四位数,各个位的数字相加
- 10种基于MATLAB的方程组求解方法
- dparsf是什么_用rs-fmri数据画脑功能连接图
热门文章
- 手把手教你部署Docker(手撸官网)
- 蓝桥杯 填数字游戏 20分。
- android webview goback 刷新,解决webview调用goBack()返回上一页自动刷新闪白的情况
- 爬山法求解八皇后问题的全部解法
- 5.8日 ksjsb 小黄鸟抓快手ck教程
- [10.21][转贴][中国][成龙作品集34部][DVD-RMVB/16.2G][中文字幕/影片截图]
- OneNote中英文格式不同,OneNote无法修改英文字体,OneNote默认英文字体为Calibri无法修改的问题。
- 28岁程序员从字节退休:IT是改命的唯一出路吗?
- 小米手机深陷“返修门” 售后成软肋
- flex布局下文字超出省略号代替不起作用解决方法