Vue已经3.0了,Vue2的原理你会了吗(一)?

2,594 阅读3分钟

1、前言

    现在项目中仍然大量使用Vue2的代码,并且短期内不会替换成Vue3,所以,掌握Vue2的原理仍是十分重要的一件事。自己打算用一个系列来完成对Vue2源码的解析,并在github上开源手写版Vue2,希望大家多多支持。

2、数据响应

     Vue2作为数据驱动视图更新的框架,数据响应可以说是最重要的,所以,将他作为Vue2源码解析第一篇。以下代码均为手写Vue2代码,且不涉及视图更新,真实Vue2代码没这么简陋😂。

2.1 一个问题

image.png

如上图:更改item的值,会更新试图吗?

2.2 入口文件

image.png

     如上图,这是一个简写的Vue源码,在new一个Vue实例的时候,会执行挂载在Vue身上的init方法,而该方法由mixInit混入的

image.png

    mixInit:将Vue构造函数作为参数对象传入,并在上面写入init方法,该方法主要用于执行初始化操作,比如状态的初始化操作。

image.png

    state:接收Vue实例作为参数,获取data数据,并进行监听。所以,observe是灵魂方法。

2.3 监听对象

image.png

    如上图:在入口处,先对data进行预处理,选出数组和对象。

image.png

    如上图,在constructor中为value区分对象和数组,分别操作。

为什么要区分数组和对象呢?因为object.defineProperty无法监听数组。

    对象先执行walk方法,进行递归监听:如以下场景

    let vm= new Vue({
        el:"dom",
        data(){
            return {
                obj:{
                    relations:{
                        name:"你猜"
                    }
                }
            }
        }
    })

则需要递归进行监听,所以在defineReactive中又执行了一次observe方法。监听使用的方法是Object.defineProperty,在设置set的时候,同样有可能是对象,同样需要监听,所以,也要执行observe方法。

2.4 监听数组

image.png     如上图,def是一个工具函数,可以往对象身上定义属性。 image.png     在对象value上添加一个__ob__对象,值为Observer类本身,是为了在数组原型劫持方法中调用类中的方法。 code.png     如上图,判定如果是数组,则劫持数组的原型对象,重写能改变自身的七个方法:'push'、'unshift'、'shift'、'pop'、'splice'、'sort'、'reverse',这样做是为了进行视图更新和对象监听。其中新增的三个方法,如果新增的数据也是对象,则也要对新数据进行监听,这里就有用到Observe类中的方法。当然,Vue源码中,也是在此步骤中进行试图更新:

ob.dep.notify()

3、Object.defineProperty的局限性

  • 不能监听数组变化
  • 必须遍历对象中的每个属性
  • 必须深层遍历嵌套的对象:数据庞大,对内存来说就是一笔很大的开销

    基于以上原因,Vue3中使用Proxy来进行数据监听,结果来看,很完美。

4、总结

    Vue2中数据监听分为对象和数组,对象直接使用Object.defineProperty进行监听,数组则是劫持重写数组原型,每个值进行递归,重复上述步骤,这样数组中的旧对象和新增的对象也得到监听。那回到最开始的问题,答案不言而喻:更改item的值,并不能引起视图更新,因为arr[0]不是对象,没有监听他的变化,可以使用$set进行处理。

    那么,$set的原理是啥呢🤓?