标签:cto 技术 基础 alt 查看 发送 har || 经典
我们先从初始化数据开始,再介绍几个比较核心的方法。
文件位置:src/core/instance/state.js
在Vue的初始化阶段,_init?法执?的时候,会执?initState(vm)?法,。是对props、methods 、data、computed和wathcer等属性做了初始化操作。
export?function?initState?(vm:?Component)?{
??vm._watchers?=?[]
??const?opts?=?vm.$options
??if?(opts.props)?initProps(vm,?opts.props)
??if?(opts.methods)?initMethods(vm,?opts.methods)
??if?(opts.data)?{
????initData(vm)
??}?else?{
????observe(vm._data?=?{},?true?/*?asRootData?*/)
??}
??if?(opts.computed)?initComputed(vm,?opts.computed)
??if?(opts.watch?&&?opts.watch?!==?nativeWatch)?{
????initWatch(vm,?opts.watch)
??}
}
此处我们仅大致看一下对于props和data的初始化。
源码略
作用:遍历定义的props配置。遍历的过程主要做两件事情:?个是调用defineReactive ?法把每个prop对应的值变成响应式;另?个是通过proxy 把vm._props.xxx 的访问代理到 vm.xxx上.
源码略
作用:两件事,?个是对定义data函数返回对象的遍历,通过proxy 把每?个值 vm._data.xxx都代理到vm.xxx上;另?个是调?observe?法观测整个data的变化,把data 也变成响应式,
源码位置:src/core/instance/state.js
作?是把props和data上的属性代理到vm实例上,,通过Object.defineProperty把 target[sourceKey][key]的读写 变成了对target[key]的读写。
const?sharedPropertyDefinition?=?{
??enumerable:?true,
??configurable:?true,
??get:?noop,
??set:?noop
}
export?function?proxy?(target:?Object,?sourceKey:?string,?key:?string)?{
??sharedPropertyDefinition.get?=?function?proxyGetter?()?{
????return?this[sourceKey][key]
??}
??sharedPropertyDefinition.set?=?function?proxySetter?(val)?{
????this[sourceKey][key]?=?val
??}
??Object.defineProperty(target,?key,?sharedPropertyDefinition)
}
经过这种代理的操作。所以对于props??,对vm._props.xxx的读写变成了vm.xxx 的读写,?对于vm._props.xxx我们可以访问到定义在 props中的属性,所以我们就可 以通过 vm.xxx访问到定义在props中的xxx属性了。同理,对于data??,对vm._data.xxxx的读写变成了对vm.xxxx的读写,?对于vm._data.xxxx我们可以访问到定义在data函数返回对象中的属性,所以我们就可以通过vm.xxxx访问到定义在data函数返回对 象中的xxxx属性了。
源码位置:src/core/observer/index.js
observe的功能就是?来监测数据的变化
/**
?*?Attempt?to?create?an?observer?instance?for?a?value,
?*?returns?the?new?observer?if?successfully?observed,
?*?or?the?existing?observer?if?the?value?already?has?one.
?*/
export?function?observe?(value:?any,?asRootData:??boolean):?Observer?|?void?{
??if?(!isObject(value)?||?value?instanceof?VNode)?{
????return
??}
??let?ob:?Observer?|?void
??if?(hasOwn(value,?'__ob__')?&&?value.__ob__?instanceof?Observer)?{
????ob?=?value.__ob__
??}?else?if?(
????shouldObserve?&&
????!isServerRendering()?&&
????(Array.isArray(value)?||?isPlainObject(value))?&&
????Object.isExtensible(value)?&&
????!value._isVue
??)?{
????ob?=?new?Observer(value)
??}
??if?(asRootData?&&?ob)?{
????ob.vmCount++
??}
??return?ob
}
在这里vue源码的注释已经说的很明白。通过这个函数会给?VNode的对象类型数据添加?个Observer ,如果已经添加过则直接返回,否则在满??定条件下去实例化?个Observer对象实例。下面看一下这个Observer类的具体内容
作?是给对象的属性添加 getter 和 setter,?于依赖收集和派发更新
/**
?*?Observer?class?that?is?attached?to?each?observed //这个类是附加再每个被observed重
?*?object.?Once?attached,?the?observer?converts?the?target//对象一旦被驱动,观察者就转换(观察到这个)目标
?*?objects?property?keys?into?getter/setters?that //将对象属性键放入getter/setter中
?*?collect?dependencies?and?dispatch?updates. //收集依赖项并分发更新。!!!!!重要
**/
export?class?Observer?{
??value:?any;
??dep:?Dep;
??vmCount:?number;?//?number?of?vms?that?have?this?object?as?root?$data
??constructor?(value:?any)?{
????this.value?=?value
????this.dep?=?new?Dep()
????this.vmCount?=?0
????def(value,?'__ob__',?this)
????if?(Array.isArray(value))?{
??????if?(hasProto)?{
????????protoAugment(value,?arrayMethods)
??????}?else?{
????????copyAugment(value,?arrayMethods,?arrayKeys)
??????}
??????this.observeArray(value)
????}?else?{
??????this.walk(value)
????}
??}
??/**
???*?Walk?through?all?properties?and?convert?them?into
???*?getter/setters.?This?method?should?only?be?called?when
???*?value?type?is?Object.
???*/
??walk?(obj:?Object)?{
????const?keys?=?Object.keys(obj)
????for?(let?i?=?0;?i?<?keys.length;?i++)?{
??????defineReactive(obj,?keys[i])
????}
??}
??/**
???*?Observe?a?list?of?Array?items.
???*/
??observeArray?(items:?Array<any>)?{
????for?(let?i?=?0,?l?=?items.length;?i?<?l;?i++)?{
??????observe(items[i])
????}
??}
}
Observer的构造函数逻辑很简单,?先实例化Dep对象,接着通过执?def函数把??实例添加到数据对象 value的 ob 属性上,
源码位置:src/core/util/lang.js
/**
* Define a property.
*/
export function def (obj: Object, key: string, val: any, enumerable?: boolean) {
Object.defineProperty(obj, key, {
value: val,
enumerable: !!enumerable,
writable: true,
configurable: true
})
}
def函数是?个?常简单的Object.defineProperty的封装,这就是为什么我在开发中输出data 上对象类型的数据,会发现该对象多了?个__ob__的属性。
继续看Observer 的构造函数,不得不说Vue的源码注释还是相当好的,大致翻译我写在上面了,请关注一下,然后看这个类的具体操作包括
它会对value做判断,对于数组会调?observeArray?法,否则对纯对象调?walk?法。可以看到observeArray是遍历数组再次调?observe?法,?walk?法是遍历对象的key调?defineReactive?法,
源码位置:src/core/observer/index.js
defineReactive的功能就是定义?个响应式对象,给对象动态添加 getter 和 setter.
源码略了,太长了,有兴趣的可自行查看,下面贴一个自己学着写的简易版,大致是这么个意思
//定义响应式
????definReactive(obj,key,value){
????????let?that?=?this
????????let?dep?=?new?Dep()?//每个变化的数据,都会对应一个数组,这个数组是存放所有更新的操作
????????Object.defineProperty(obj,key,{
????????????enumerable:true,
????????????configurable:true,
????????????get(){
????????????????Dep.target?&&?dep.addSub(Dep.target)
????????????????return?value;
????????????},
????????????set(newValue){?//当给data属性中设置值。更改获取属性的值
????????????????if(newValue!=value){
????????????????????//这里的this不是实例
????????????????????that.observer(newValue)?//如果是对象继续劫持
????????????????????value=newValue
????????????????????dep.notify()?//通知所有人?数据更新了
????????????????}
????????????},
????????})
????}
函数最开始初始化Dep对象的实例,接着拿到obj的属性描述符,然后对?对 象递归调?observe ?法,这样就保证了?论obj的结构多复杂,它的所有?属性也能变成响应式的对象,
小结:所谓响应式对象的总体就是利?Object.defineProperty给数据添加了getter和setter,?的就是为了在我们访问数据以及写数据的时候能?动执??些逻辑,究其核心就是使用defineReactive这个方法进行处理,同时也将dep的添加subs方法存放在了get 中,将dep的notify方法放在set中。从而为后面的依赖收集和派发更新提供了入口?(不知道怎么形容好,,,,,)。
前置:所谓依赖收集其实和后面的派发更新完全是以watcher和dep为核心的关系。
接下来我们根绝不同的目的来大致看一下这两个方法,
我们去回看 defineReactive方法,对于依赖收集我们关注两点,?个是const dep = newDep()实例化?个Dep的实例,另?个是在get函数中通过dep.depend做依赖收集。
源码位置:src/core/observer/dep.js
Dep是整个 getter 依赖收集的核? 对于依赖收集主要是(dep.depend()),派发更新主要是(dep.notify())
/**
* A dep is an observable that can have multiple
* directives subscribing to it.
*/
export default class Dep {
static target: ?Watcher;
id: number;
subs: Array<Watcher>;
constructor () {
this.id = uid++
this.subs = []
}
addSub (sub: Watcher) {
this.subs.push(sub)
}
removeSub (sub: Watcher) {
remove(this.subs, sub)
}
depend () {
if (Dep.target) {
Dep.target.addDep(this)
}
}
notify () {
// stabilize the subscriber list first
const subs = this.subs.slice()
if (process.env.NODE_ENV !== 'production' && !config.async) {
// subs arent sorted in scheduler if not running async
// we need to sort them now to make sure they fire in correct
// order
subs.sort((a, b) => a.id - b.id)
}
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
}
// The current target watcher being evaluated.
// This is globally unique because only one watcher
// can be evaluated at a time.
Dep.target = null
const targetStack = []
export function pushTarget (target: ?Watcher) {
targetStack.push(target)
Dep.target = target
}
export function popTarget () {
targetStack.pop()
Dep.target = targetStack[targetStack.length - 1]
}
Dep是?个Class,它定义了?些属性和?法,这?需要特别注意的是它有?个静态属性target , 这是?个全局唯?Watcher ,这是?个?常巧妙的设计,因为在同?时间只能有?个全局的Watcher 被计算,另外它的??属性subs也是Watcher的数组。Dep实际上就是对Watcher的?种管理,Dep脱离Watcher单独存在是没有意义的,为了完整地讲清楚依赖收集过程,我们有必要看?下Watcher的?些内容
源码位置: src/core/observer/watcher.js
源码太长略
下面贴一个自己学着写的简易版
注释已经和很详细了,
//观察者 的目的就是给需要观察的元素天机一个观察这,当数据变化后执行相对应的方法
class Watcher{
constructor(vm,expr,cb){
this.vm=vm
this.expr=expr
this.cb=cb;
//先获取一下老的值
this.value = this.get();
}
getVal(vm,expr){ //获取实例上对应的数据
expr= expr.split('.');
return expr.reduce((prev,next)=>{ //vm.$data.a
return prev[next]
},vm.$data)
}
get (){
//
Dep.target=this //这个地方就是上面在1.3.1 Observer中注释中说到的大致意思,要将所有被观察者的数据都要放在dep的target中后面,dep会将这个target存起来,等到notify时一个个分发出去。然后走这里的update方法,去更新视图
//
let value= this.getVal(this.vm,this.expr);
Dep.tatget=null
return value
}
//对外暴露的方法
update(){
let newValue = this.getVal(this.vm,this.expr);
let oldValue = this.value;
if(newValue!=oldValue){
this.cb(newValue); //调用watch的callback
}
}
}
//用新值和老值对比,如果变化就调用更新方法
我们继续回去看 defineReactive方法,对于派发更新集我们关注两点,?个是childOb =!shallow &&observe(newVal),如果shallow为false的情况,会对新设置的值变成?个响应式对象;另?个是 dep.notify(),通知所有的订阅者,
好了,到这里我们总算是把整个响应式的大致流程走完了,下面我先放一个我自己做的关系图,根据图我再做个总结
标签:cto 技术 基础 alt 查看 发送 har || 经典
原文地址:https://www.cnblogs.com/jiayiyi/p/12443976.html