我是如何看Vue源码的(1)

这里我们可以看到几个beforeCreate,createdMount关键字,大概就能够猜到vue实例的部分生命周期方法就是在这里进行了挂载,再结合 vue官方文档的图示

关于初始化整个vue的状态,可以举例来说,例如initLifecycle中就赋值了parent,children,以及一些isMounted,isDestroy的标识符。initRender中就将attrs,listeners响应化,等等,诸如此类。

initMixin=>initState=>initData,便可以看到挂载props,methods,data,computed,watch了,

可以看到,此处先挂载了props,methods,然后是data的顺序,其实再往下探究逻辑就可以知道,如果存在变量重名,优先级是props>methods>data的,这也就解释了为什么初始化的顺序是这样安排的

initData中,先是获取了data数据,判断props,methods变量重名问题,然后是走了一个代理,将变量名代理到vue实例上,这样的话你的vue实例中,使用this.x指向就可以访问到this.data.x,这类代理也用在了propsmethods

initData获取数据中可以看到一个判断typeof data === 'function' ? getData(data, vm) : data || {}, 支持两种方式获取,实际上如果是自己写这样一个逻辑是会藏有隐患的,如果你的data是直接使用对象,而js的复杂数据类型是地址引用,这意味着,你实例化了两个vue对象,实际上他们的data引用地址是同一个地址,对其中一个vue data的修改会触发另一个vue数据的变动,带来的问题是巨大的

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)

}

这个逻辑处理的设计也是非常巧妙,他覆盖了实例中对该key的访问,使用settergetter将实际访问指向了this.data[key]

这里可以说一下computed的逻辑,实际上也是取巧使用了原本用于data的响应式逻辑,其实看到上面贴出来的proxy代码,大概就能猜到,既然proxy能够改变一个变量读取的指向,那么他也能创造一个虚假变量的指向,这个创造出来的这个变量实际上就是computed所使用的变量,将每次computed函数赋给getter,再加上响应式处理,就完全实现了computed,

走到最后,就是observe(data),也就是开始处理vue数据的双向绑定

二、双向绑定


不同于react的单向数据流,vue使用的双向绑定,单向数据流可以理解为当源头的数据发生变动则触发行为,当然这个变动是主动的,即你需要setState才能触发,而双向绑定则可以抽象为,每一个数据旁边都有一个监护人(一种处理逻辑),当数据发生变化,这个监护人就会响应行为,这个流程是被动发生的,只要该数据发生变动,就会通过监护人触发行为。

如果你之前有过了解,大概就会知道,js每个数据的变动都是通过Object原型链中的setter去改变值,而如果你在他改变值之前,去通知监护人,就能够实现上述的逻辑,这一点很多博客文章都写的非常清楚了。

接着第一部分的initData知道最后observe(data),这里开始正式处理响应式。

2.1 前置条件

前面一直提到,通过Object的原型链改变对象的默认行为:gettersetter,首先我们需要知道,在js中,读取一个对象的值并不是直接读取,而是通过Object的原型链上的默认行为getter拿到对应的值,而改变这种行为实际上是通过Object.defineProperty,来重新定义一个对象的gettersetter,在/src/core/observer/index.js中我们可以看一个defineReactive方法,他就是vue用来实现这种行为的方法,也是这个响应式的核心

function defineReactive(obj, key, val, … ) {

// 此处需要保留getter、setter是因为,开发者可能自己基于defineProperty已经做过一层覆盖,

// 而响应式又会覆盖一次,所以为了保留开发者自己的行为,此处需要兼容原有的getter、setter

const getter = property && property.get // 拿到默认的getter、setter行为

const setter = property && property.set

Object.defineProperty(obj, key, {

enumerable: true, // 是否可以被枚举出来(例如Object.keys(),for in)

configurable: true, // 是否可以被配置,是否可以被删除

get: function() {

const value = getter ? getter.call(obj) : val

return value

}</

  • 23
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值