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 = null class 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个场景的栗子

vue 源码自问自答-响应式原理的更多相关文章

  1. Vue 源码解析:深入响应式原理(上)

    原文链接:http://www.imooc.com/article/14466 Vue.js 最显著的功能就是响应式系统,它是一个典型的 MVVM 框架,模型(Model)只是普通的 JavaScri ...

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

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

  3. Vue2.0源码阅读笔记(二):响应式原理

      Vue是数据驱动的框架,在修改数据时,视图会进行更新.数据响应式系统使得状态管理变的简单直接,在开发过程中减少与DOM元素的接触.而深入学习其中的原理十分有必要,能够回避一些常见的问题,使开发变的 ...

  4. 手摸手带你理解Vue响应式原理

    前言 响应式原理作为 Vue 的核心,使用数据劫持实现数据驱动视图.在面试中是经常考查的知识点,也是面试加分项. 本文将会循序渐进的解析响应式原理的工作流程,主要以下面结构进行: 分析主要成员,了解它 ...

  5. vue响应式原理,去掉优化,只看核心

    Vue响应式原理 作为写业务的码农,几乎不必知道原理.但是当你去找工作的时候,可是需要造原子弹的,什么都得知道一些才行.所以找工作之前可以先复习下,只要是关于vue的,必定会问响应式原理. 核心: / ...

  6. vue 数组 新增元素 响应式原理 7种方法

    1.问题 思考一个问题,以下代码: <!DOCTYPE html> <html> <head> <meta charset="utf-8" ...

  7. Vue 源码解读(3)—— 响应式原理

    前言 上一篇文章 Vue 源码解读(2)-- Vue 初始化过程 详细讲解了 Vue 的初始化过程,明白了 new Vue(options) 都做了什么,其中关于 数据响应式 的实现用一句话简单的带过 ...

  8. 学习 vue 源码 -- 响应式原理

    概述 由于刚开始学习 vue 源码,而且水平有限,有理解或表述的不对的地方,还请不吝指教. vue 主要通过 Watcher.Dep 和 Observer 三个类来实现响应式视图.另外还有一个 sch ...

  9. vue源码之响应式数据

    分析vue是如何实现数据响应的. 前记 现在回顾一下看数据响应的原因. 之前看了vuex和vue-i18n的源码, 他们都有自己内部的vm, 也就是vue实例. 使用的都是vue的响应式数据特性及$w ...

随机推荐

  1. 1392:繁忙的都市(city)

    [题目描述] 城市C是一个非常繁忙的大都市,城市中的道路十分的拥挤,于是市长决定对其中的道路进行改造.城市C的道路是这样分布的:城市中有n个交叉路口,有些交叉路口之间有道路相连,两个交叉路口之间最多有 ...

  2. Zookeeper(1、3、5节点)集群安装

    1节点 1 week110的zookeeper的安装 + zookeeper提供少量数据的存储 3节点 hadoop-2.6.0.tar.gz的集群搭建(3节点) hadoop-2.6.0-cdh5. ...

  3. C++面向对象程序设计举例

    [例8.1]最简单的例子. #include <iostream> using namespace std; class Time //定义Time类 { public : //数据成员为 ...

  4. Vue-cli构建项目, 组件中js代码引入图片路径问题

    问题描述 .vue的组件分成三个部分, template结构部分, script路径代码, style页面样式 首先, 我们可以在template可以正确引入, 无论是dev, 还是build都没有问 ...

  5. Hibernate Could not obtain transaction-synchronized Session for current thread问题处理

    项目通过Hibernate查询时报出如下错误: Hibernate Could not obtain transaction-synchronized Session for current thre ...

  6. 转 Oracle中merge into的使用

    http://www.cnblogs.com/highriver/archive/2011/08/02/2125043.html

  7. pyinstaller 打包.exe文件记录遇到的问题

    用pyinstaller打包py2.7的程序有时会出现不匹配的错误,在python的idle下运行没有问题,打包之后却会报一些错误,所以打包的话还是尽量用py3.5版本,而且用 -F 将程序打包成一个 ...

  8. CF 602 D. Lipshitz Sequence 数学 + 单调栈 + 优化

    http://codeforces.com/contest/602/problem/D 这题需要注意到的是,对于三个点(x1, y1)和(x2, y2)和(x3, y3).如果要算出区间[1, 3]的 ...

  9. K-th Number 线段树的区间第K大

    http://poj.org/problem?id=2104 由于这题的时间限制不紧,所以用线段树水一水. 每个节点保存的是一个数组. 就是对应区间排好序的数组. 建树的时间复杂度需要nlogn 然后 ...

  10. Apache下禁止使用IP直接访问本站的配置方法

    现在管的严啊,上面要求不能使用IP直接访问服务器,把apache配置做下调整就行了.方法如下: 打开apache的配置文件 # vi /usr/local/apache2/conf/extra/htt ...