1.初始化过程:
首先会创建vue实例new Vue();调用初始化方法_init();生成唯一标识_uid;合并传入的选项options;初始化代理initProxy,判断如果支持proxy (hasProxy) 就用proxy,不支持就用 defineProperty();然后确定父子组件关系,并初始化组件实例相关属性($parent,$children,$root,$refs,_watcher等);初始化父组件传递过来的自定义事件;初始化render方法;后面会执行beforecreate生命周期钩子函数;初始化inject让组件可以访问到父组件传递的依赖;初始化data,递归遍历每一个属性,添加代理并创建dep实例,使其变为响应式数据;初始化props、methods、computed、props并挂载到this上;执行created生命周期;然后执行$mount开始编译挂载
2.编译加载过程:
watcher、dep、observe解读参考文章:
响应式源码分析:zhuanlan.zhihu.com/p/575403904
Dep,Observer和Watcher:www.jianshu.com/p/eb7fa2dab…
依赖收集的时机:zhuanlan.zhihu.com/p/392285475
总体流程图:
依赖收集:
渲染Watcher派发更新流程:
1.先确定挂载的目标元素,且必须保证该元素不能为 html,body 这类跟节点,判断选项中是否有 render 这个属性
(默认我们使用的是 runtime-only**只包含运行时版**(对应js文件vue.runtime.js),单文件组件SFC会通过vue-loader、vue-template-compiler在打包阶段将.vue文件解析生成组件选项对象,template内容会解析成render函数)的版本,有render属性会跳过模板编译阶段,调用挂载函数$mount,直接进入挂载过程。当传入template模板选项时(即在不使用外置编译器:vue-loader的情况下,使用 runtime+compile完整版本时,runtime-only版本不支持template属性写法),会先进入解析编译阶段,首先将模板使用正则匹配的方法解析生成ast(抽象语法树),然后根据ast生成render函数,然后再调用$mount方法,进入挂载阶段。
单文件组件SFC会通过
**vue-loader、vue-template-compiler**在打包阶段将.vue文件解析生成组件选项对象,template内容会解析成render函数
2.$mount实际会执行mountComponent方法,先调用beforeMount生命周期钩子,然后定义updateComponent方法,然后生成一个watcher实例,watcher接受一个回调函数,传入上面定义的updateComponent方法,(会在初始化和数据更新时执行,回调函数里会执行vm._update(vm._render(), hydrating)方法,通过patch方法将render函数生成的虚拟dom转换成真实dom)
3.在watcher构造函数中,会将传入的updateComponent赋值给this.getter,然后执行this.get方法(
依赖收集就是发生在这个get方法中),get方法中,会调用pushTarget设置Dep.target = this(当前实例),然后调用this.getter(即updateComponent),执行vm._update(vm._render(), hydrating)方法
4.先调用_render()方法生成虚拟dom,在这个过程中会调用_c(createElement)、_v(createTextVNode)、_s(toString)方法去访问绑定的变量,触发绑定变量的getter函数
5.getter中会去判断Dep.target是否存在,上面pushTarget已经设置过,这时候会执行depend,触发Watcher实例的addDep,在addDep方法中会首先判断newDepIds中是否存在该dep实例的id是否存在,不存在将dep.id存入newDepId数组中,并将这个Watcher实例添加到dep的subs数组中,至此依赖收集完成。
Watcher类addDep方法:
Dep类addSub、depend方法:
6.然后通过patch方法将render函数生成的虚拟dom转换成真实dom,触发mounted生命钩子