Vue源码理解(一)

Vue源码主要关注了数据驱动,组件化,响应式几个部分。对于源码个人感觉不用太纠结或着急着去看,打好基础使用熟练熟悉之后,再去进一了解会有很大的帮助。我是看huangyi大佬的文章以及一边跟着源码一边加上自己理解而写下来这些内容。

数据驱动(生成获取vnode):

第一篇考虑的是Vue是如何生成节点并挂载的,这里只从以浏览器为平台首次渲染的角度,采用的是runtime+compiler的版本

new Vue(options)

Vue源码理解(一)_第1张图片
很简单,直接调用_init

this._init(options)

Vue源码理解(一)_第2张图片
Vue源码理解(一)_第3张图片

​ 该函数主要调用配置合并函数,同时进行各种初始化工作,然后调用vm.$mount进行挂载,重点看$mount

vm.$mount(el, hydrating)

Vue源码理解(一)_第4张图片Vue源码理解(一)_第5张图片

​ (挂载函数,在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源码理解(一)_第6张图片

​ 原mount函数很简单,主要就是调用mountComponent(this,el,hydrating)函数

mountComponent(this,el,hydrating)


Vue源码理解(一)_第7张图片
Vue源码理解(一)_第8张图片
步骤:
​ 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源码理解(一)_第9张图片
核心部分便是vnode = render.call(vm._renderProxy, vm.$createElement),然后返回生成的vnode

render()

​ render函数生成比较复杂,根据不同的环境生成,但是最终是通过调用我们常见的createElement(也叫h函数)生成了vnode

$createElement = createElement(vm,a,b,c,d,true)

WeChat2619ce6bc206c6f86ec2b9bb54b9701e.png

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

_createElement(context, tag, data, children, normalizationType)

Vue源码理解(一)_第11张图片
Vue源码理解(一)_第12张图片
Vue源码理解(一)_第13张图片

​关注两个重点流程 children 的规范化以及 VNode 的创建。

1 children规范化(生成vnode数组):
​当normalizationType为ALWAYS_NORMALIZE: normalizeChildren(children)
​当normalizationType为SIMPLE_NORMALIZE: simpleNormalizeChildren(children)

simpleNormalizeChildren:默认内部生成的children都是Vnode类型(调用场景为编译生成render),只有函数式组件会生成一个数组。所以处理很简单,就是遍历children判断是否为数组并展平并返回

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源码理解(一)_第14张图片

你可能感兴趣的:(vue.js前端)