一、数据绑定的原理分析
数据绑定 :一旦更新了 data
中的某个属性数据, 所有界面上直接使用或间接使用了此属性的节点都会更新。数据劫持 :vue
实现数据绑定的一种技术,其核心思想是通过 defineProperty()
来监视 data
中所有属性(任意层次)数据的变化,一旦变 化就去更新界面。dep 和watcher 的理解:
dep
dep
在初始化给data
的属性进行数据劫持的时候会被创建。dep
的个数与data
中的属性一一对应。dep
的结构中,id
是作为标识,subs: []
是n
个相关的watcher
容器。 watcher
dep
与watcher
之间的关系
dep
和watcher
之间存在多对多的关系,data属性--> dep --> n个watcher
(模版中有多个表达式使用了此属性), 表达式 --> watcher --> n个dep
(多层表达式)data
中属性的get()
方法建立的初始化解析模块中的表达式创建watcher
对象时建立关系的
实现数据绑定Observer
,创建dep
对象,核心过程如下:
defineReactive: function ( data, key, val) {
var dep = new Dep ( ) ;
var childObj = observe ( val) ;
Object. defineProperty ( data, key, {
enumerable: true ,
configurable: false ,
get : function ( ) {
if ( Dep. target) {
dep. depend ( ) ;
}
return val;
} ,
set : function ( newVal) {
if ( newVal === val) {
return ;
}
val = newVal;
childObj = observe ( newVal) ;
dep. notify ( ) ;
}
} ) ;
}
给dep
对象上需要进行原型挂载,核心过程如下:
addSub: function ( sub) {
this . subs. push ( sub) ;
} ,
depend: function ( ) {
Dep. target. addDep ( this ) ;
} ,
removeSub: function ( sub) {
var index = this . subs. indexOf ( sub) ;
if ( index != - 1 ) {
this . subs. splice ( index, 1 ) ;
}
} ,
notify: function ( ) {
this . subs. forEach ( function ( sub) {
sub. update ( ) ;
} ) ;
}
对于watcher
对象,核心过程如下:
function Watcher ( vm, exp, cb) {
this . cb = cb;
this . vm = vm;
this . exp = exp;
this . depIds = { } ;
this . value = this . get ( ) ;
}
dep
与watcher
之间需要建立关系,核心过程如下:
addDep: function ( dep) {
if ( ! this . depIds. hasOwnProperty ( dep. id) ) {
dep. addSub ( this ) ;
this . depIds[ dep. id] = dep;
}
} ,
get : function ( ) {
Dep. target = this ;
var value = this . getVMVal ( ) ;
Dep. target = null ;
return value;
} ,
二、数据绑定的实现
Observer.js
function Observer ( data) {
this . data = data;
this . walk ( data) ;
}
Observer. prototype = {
walk: function ( data) {
var me = this ;
Object. keys ( data) . forEach ( function ( key) {
me. convert ( key, data[ key] ) ;
} ) ;
} ,
convert: function ( key, val) {
this . defineReactive ( this . data, key, val) ;
} ,
defineReactive: function ( data, key, val) {
var dep = new Dep ( ) ;
var childObj = observe ( val) ;
Object. defineProperty ( data, key, {
enumerable: true ,
configurable: false ,
get : function ( ) {
if ( Dep. target) {
dep. depend ( ) ;
}
return val;
} ,
set : function ( newVal) {
if ( newVal === val) {
return ;
}
val = newVal;
childObj = observe ( newVal) ;
dep. notify ( ) ;
}
} ) ;
}
} ;
function observe ( value, vm) {
if ( ! value || typeof value !== 'object' ) {
return ;
}
return new Observer ( value) ;
} ;
var uid = 0 ;
function Dep ( ) {
this . id = uid++ ;
this . subs = [ ] ;
}
Dep. prototype = {
addSub: function ( sub) {
this . subs. push ( sub) ;
} ,
depend: function ( ) {
Dep. target. addDep ( this ) ;
} ,
removeSub: function ( sub) {
var index = this . subs. indexOf ( sub) ;
if ( index != - 1 ) {
this . subs. splice ( index, 1 ) ;
}
} ,
notify: function ( ) {
this . subs. forEach ( function ( sub) {
sub. update ( ) ;
} ) ;
}
} ;
Dep. target = null ;
Watcher.js
function Watcher ( vm, exp, cb) {
this . cb = cb;
this . vm = vm;
this . exp = exp;
this . depIds = { } ;
this . value = this . get ( ) ;
}
Watcher. prototype = {
update: function ( ) {
this . run ( ) ;
} ,
run: function ( ) {
var value = this . get ( ) ;
var oldVal = this . value;
if ( value !== oldVal) {
this . value = value;
this . cb. call ( this . vm, value, oldVal) ;
}
} ,
addDep: function ( dep) {
if ( ! this . depIds. hasOwnProperty ( dep. id) ) {
dep. addSub ( this ) ;
this . depIds[ dep. id] = dep;
}
} ,
get : function ( ) {
Dep. target = this ;
var value = this . getVMVal ( ) ;
Dep. target = null ;
return value;
} ,
getVMVal: function ( ) {
var exp = this . exp. split ( '.' ) ;
var val = this . vm. _data;
exp. forEach ( function ( k) {
val = val[ k] ;
} ) ;
return val;
}
} ;
数据绑定的实现
<!DOCTYPE html>
< html lang = " en" >
< head>
< meta charset = " UTF-8" >
< meta name = " viewport" content = " width=device-width, initial-scale=1.0" >
< meta http-equiv = " X-UA-Compatible" content = " ie=edge" >
< title> 数据劫持与数据绑定</ title>
</ head>
< body>
< div id = " test" >
< p> {{ name }}</ p>
< p v-text = " name" > </ p>
< p v-text = " friend.name" > </ p>
< button v-on: click= " update" > 更新</ button>
</ div>
</ body>
< script type = " text/javascript" src = " ./js/mvvm/compile.js" > </ script>
< script type = " text/javascript" src = " js/mvvm/mvvm.js" > </ script>
< script type = " text/javascript" src = " ./js/mvvm/observer.js" > </ script>
< script type = " text/javascript" src = " ./js/mvvm/watcher.js" > </ script>
< script type = " text/javascript" >
new MVVM ( {
el: "#test" ,
data: {
name: "张三" ,
friend: {
name: "李四" ,
age: 28
}
} ,
method: {
update ( ) {
this . name = "王五" ;
}
}
} )
</ script>
</ html>
关于vue
的源码分析,我在github
上建立了一个项目,项目地址如下https://github.com/jiuchengTk279/vueSourceCode.git
,欢迎大家访问下载,也希望可以多给予一些建议交流。