Vue源码理解(一)

Vue源码主要关注了数据驱动,组件化,响应式几个部分。对于源码个人感觉不用太纠结或着急着去看,打好基础使用熟练熟悉之后,再去进一了解会有很大的帮助。我是看huangyi大佬的文章以及一边跟着源码一边加上自己理解而写下来这些内容。
数据驱动(生成获取vnode): 第一篇考虑的是Vue是如何生成节点并挂载的,这里只从以浏览器为平台首次渲染的角度,采用的是runtime+compiler的版本
new Vue(options) Vue源码理解(一)
文章图片

很简单,直接调用_init
this._init(options) Vue源码理解(一)
文章图片

Vue源码理解(一)
文章图片

? 该函数主要调用配置合并函数,同时进行各种初始化工作,然后调用vm.$mount进行挂载,重点看$mount
vm.$mount(el, hydrating) Vue源码理解(一)
文章图片
Vue源码理解(一)
文章图片

? (挂载函数,在platform文件夹内,因为内部的具体实现与平台相关,hydrating参数正是与服务端渲染相关,所以在这不用考虑)
? 1 先用mount缓存原$mount(platform/web/runtime/index),再重新定义$mount
? 2 新的$mount内部通过compileToFunctions获取render以及staticRenderFns函数(或通过sfc(vue-loader解析)获取render函数),这是在runtime+compiler版本中然后返回原mount调用结果。
mount(el, hydrating)(原$mount) Vue源码理解(一)
文章图片

? 原mount函数很简单,主要就是调用mountComponent(this,el,hydrating)函数
mountComponent(this,el,hydrating)
Vue源码理解(一)
文章图片

Vue源码理解(一)
文章图片

步骤:
? 1 触发beforemount钩子函数
? 2 定义updateComponent函数,函数内部调用实例的vm._update(vm._render(), hydrating)
? 函数中的vm._render()实际上是调用render函数而生成一个的vnode(见下)
? 3 new Watcher(vm,updateComponent,noop,{ before },true)生成watcher实例,updateComponent会在watcher实例生成与更新的时候触发,传入的before函数仅仅是一个钩子,这里函数内部就是调用一下组件的beforeUpdate钩子函数而已
? 4 设置实例_isMounted为true,并调用mounted钩子函数
? 5 返回实例vm
_render() Vue源码理解(一)
文章图片

核心部分便是vnode = render.call(vm._renderProxy, vm.$createElement),然后返回生成的vnode
render() ? render函数生成比较复杂,根据不同的环境生成,但是最终是通过调用我们常见的createElement(也叫h函数)生成了vnode
$createElement = createElement(vm,a,b,c,d,true) Vue源码理解(一)
文章图片

Vue源码理解(一)
文章图片

在initRender函数中的createElement函数,即是对_createElement函数的封装,在对传入的参数处理后调用 _createElement(context, tag, data, children, normalizationType)
_createElement(context, tag, data, children, normalizationType) Vue源码理解(一)
文章图片

Vue源码理解(一)
文章图片

Vue源码理解(一)
文章图片

?关注两个重点流程 children 的规范化以及 VNode 的创建。
?1 children规范化(生成vnode数组):
?当normalizationType为ALWAYS_NORMALIZE: normalizeChildren(children)
?当normalizationType为SIMPLE_NORMALIZE: simpleNormalizeChildren(children)
?simpleNormalizeChildren:默认内部生成的children都是Vnode类型(调用场景为编译生成render),只有函数式组件会生成一个数组。所以处理很简单,就是遍历children判断是否为数组并展平并返回
【Vue源码理解(一)】?normalizeChildren:方法的调用场景有 2 种,一个场景是 render 函数是用户手写的,当 children 只有一个节点的时候,Vue.js 从接口层面允许用户把 children 写成基础类型用来创建单个简单的文本节点,这种情况会调用 createTextVNode 创建一个文本节点的 VNode;另一个场景是当编译 slotv-for 的时候会产生嵌套数组的情况,会调用 normalizeArrayChildren 方法
?normalizeArrayChildren:该方法便是遍历children转换为vnode,如果遇到数组,则递归调用本函数
?注意:如果存在两个连续的 text 节点,会把它们合并成一个 text 节点
? 2 Vnode 的创建
? 如果tag为string类型:
? 1 为通常tag,则直接new Vnode
? 2 否则检测tag是否为已注册的component tag,是的话调用createComponent
? 3 否则创建一个未知标签的vnode
? 如果tag为component类型:
component类型后续会有说明: 直接创建一个component类型vnode
? 3 返回生成的vnode
采用vnode的好处有很多,一方面可以不用直接使用原生厚重的dom,虚拟节点更加轻量,舍弃了很多dom的属性方法,更加定制化。其次还能用于vue框架的多平台使用。
源码如下:
Vue源码理解(一)
文章图片


    推荐阅读