vue3的源码解析(一)

122 阅读3分钟

Vue源码解析(一)

一、前置知识

1、Proxy:

首先需要先熟悉proxy的对象

1、const p = new Proxy(target, handler)

target:包装的目标对象 handler:处理函数

handler包括方法、属性捕捉器

举个校验器例子:

const target = {
  _id: '1024',
  name:  'vuejs'
}

const validators = {  
    name(val) {
        return typeof val === 'string';
    },
    _id(val) {
        return typeof val === 'number' && val > 1024;
    }
}

const createValidator = (target, validator) => {
  return new Proxy(target, {
    _validator: validator,
    set(target, propkey, value, proxy){
      let validator = this._validator[propkey](value)
      if(validator){
        return Reflect.set(target, propkey, value, proxy)	// 在目标对象上设置一个属性
      }else {
        throw Error(`Cannot set ${propkey} to ${value}. Invalid type.`)
      }
    }
  })
}

const proxy = createValidator(target, validators)

proxy.name = 'vue-js.com' // vue-js.com
proxy.name = 10086 // Uncaught Error: Cannot set name to 10086. Invalid type.
proxy._id = 1025 // 1025
proxy._id = 22  // Uncaught Error: Cannot set _id to 22. In

2、为什么要用Proxy重构?

在vue3.0之前vue一直是使用的Object.defineProperty进行的拦截实现的双向数据绑定

Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      // ...
      if (Dep.target) {
        // 收集依赖
        dep.depend()
      }
      return value
    },
    set: function reactiveSetter (newVal) {
      // ...
      // 通知视图更新
      dep.notify()
    }
  })
这个是vue2.x的双向绑定的核心:收集依赖、配置watcher监控、更新改变后去通知视图更新
但是有个问题,就是手动新增属性和值后或者直接通过数组索引去修改数组项,视图并不会去更新:原因就是,
1、data init是在生命周期created之前进行的操作,会对data绑定一个观察者observer,之后data中的字段更新都会通知依赖收集器Dep触发视图更新,但是在Observer data的时候,新增的属性并不存在,自然就不会有setter,getter了,所以就不会更新,当然vue也提供了方法$set()自己手动去给新增的属性observer

对比之下,Proxy优势:能观察的类型比defineProperty更丰富;Object.defineProperty是劫持对象的属性,新增元素需要再次Definepeoperty,但是Proxy劫持的是整个对象,不需要做特殊处理

使用definepeoperty时,我们修改原来的obj对象就可以触发拦截,但是Proxy必须修改代理对象,即Proxy的实例才可以触发拦截。

二、全局概览

1、目录结构

其他文件都是一些配置相关的文件,有兴趣的可以自行查阅,我们现在只关注package目录

.
├── compiler-core // 顾名思义,核心中的核心,抽象语法树和渲染桥接实现
├── compiler-dom // Dom的实现
├── compiler-sfc // Vue单文件组件(.vue)的实现
├── compiler-ssr
├── global.d.ts
├── reactivity
├── runtime-core
├── runtime-dom
├── runtime-test
├── server-renderer // 服务端渲染实现
├── shared  // package 之间共享的工具库
├── size-check
├── template-explorer
└── vue

Compile time 我们可以理解为程序编译时,是指我们写好的源代码在被编译成为目标文件这段时间,但我们可以通俗的看成是我们写好的源代码在被转换成为最终可执行的文件这段时间,在这里可以理解为我们讲.vue文件编译成浏览器能识别的.html文件的一些工作

run time可以理解为程序运行时,即是程序被编译之后,打开程序并运行它知道程序关闭的这段时间的系列处理

                      +---------------------+    +----------------------+
                      |                     |    |                      |
        +------------>|  @vue/compiler-dom  +--->|  @vue/compiler-core  |
        |             |                     |    |                      |
   +----+----+        +---------------------+    +----------------------+
   |         |
   |   vue   |
   |         |
   +----+----+        +---------------------+    +----------------------+    +-------------------+
        |             |                     |    |                      |    |                   |
        +------------>|  @vue/runtime-dom   +--->|  @vue/runtime-core   +--->|  @vue/reactivity  |
                      |                     |    |                      |    |                   |
                      +---------------------+    +----------------------+    +-------------------+