首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

vue源码解析(3)-render和createElement

ok,又到了晚上10点,每到这个时候就是自己总结知识充电时候,上一节我们讲到$mount的实现,今天我们来分析一下vue源码中的render的实现。

说到vue的渲染过成我们不得不先提一下浏览器的大致渲染过程,这里简单介绍一下:

处理 HTML 标记并构建 DOM 树

处理 CSS 标记并构建 CSSOM 树

将 DOM 与 CSSOM 合并成一个渲染树

根据渲染树来布局,计算每个节点的几何信息,再将各个节点绘制到屏幕上整理流程为:

简单介绍html 和css的解析过程,用文字描述下:

一 构建dom树的过程

转换: 浏览器从磁盘或网络读取 HTML 的原始字节,并根据文件的指定编码(例如 UTF-8)将它们转换成各个字符。

令牌化: 浏览器将字符串转换成 W3C HTML5 标准规定的各种令牌,例如,“”、“”,以及其他尖括号内的字符串。每个令牌都具有特殊含义和一组规则。

词法分析: 发出的令牌转换成定义其属性和规则的“对象”。

DOM 构建: 最后,由于 HTML 标记定义不同标记之间的关系(一些标记包含在其他标记内),创建的对象链接在一个树数据结构内,此结构也会捕获原始标记中定义的父项-子项关系:HTML 对象是 body 对象的父项,body 是 paragraph 对象的父项。

二 解析 css 过程

先创建 CSSStyleSheet 对象。将 CSSStyleSheet 对象的指针存储到 CSSParser 对象中。

CSSParser 识别出一个 simple-selector ,形如 “div”, 创建一个 CSSParserSelector 对象。

CSSParser 识别出一个关系符和另一个 simple-selecotr ,那么修改之前创建的 simple-selecotr, 创建组合关系符。

如果碰到逗号,那么取出 CSSParser 的 reuse vector,然后将堆栈尾部的 CSSParserSelector 对象弹出存入 Vecotr 中,最后跳转至第2步。如果碰到左大括号,那么跳转至第6步。

识别属性名称,将属性名称的 hash 值压入解释器堆栈。

识别属性值,创建 CSSParserValue 对象,并将 CSSParserValue 对象存入解释器堆栈。

将属性名称和属性值弹出栈,创建 CSSProperty 对象。并将 CSSProperty 对象存入 CSSParser 成员变量m_parsedProperties 中。

如果识别处属性名称,那么转至第6步。如果识别右大括号,那么转至第10步。

将 reuse vector 从堆栈中弹出,并创建 CSSStyleRule 对象。CSSStyleRule 对象的选择符就是 reuse vector, 样式值就是 CSSParser 的成员变量 m_parsedProperties 。

把 CSSStyleRule 添加到 CSSStyleSheet 中。

清空 CSSParser 内部缓存结果。

我们知道在render函数解析中最后会根据用户自己创建的render函数以及template自身编译出来的render函数调用不同的方法

两者唯一的区别就在于第六个参数 true或者false

我们来看下createElement函数中的定义,它是在vue/src/core/vdom/create-element.js 里面有定义

createElement首先会传几个参数,有context(vm实例),tag(vnode的tag标签),data(vnode的data相关数据),children(它的一些子节点,包含一系列的vnode,形成vnode树,完美映射到dom tree)

接下来第一个if判断实际上是一种参数的重载,它判断的作用就是判断data是否存在,如果不存在那么参数就向前进一个,第二个if实际判断是对children采用哪种normalizationconst SIMPLE_NORMALIZE = 1const ALWAYS_NORMALIZE = 2

然后,createElement最红会调的是_createElement函数

我们可以看到createElement这个函数首先对data做了一层封装,判断vnode data不可以为响应式的,_createElement 方法5个参数,context表示vnode上上线文环境,代表vm实例,tag是个标签,可以为字符串,组件,以及函数。data代表是vnode数据,是vnodedata类型。children表示当前vnode子节点。但是需要被规范为标准的vnode数组。

接下来就调用了createEmptyVNode这个函数

这个方法的作用是创建一个文本vnode

截下来会有对children的处理是根据normalizationType不同对children做不同的处理,类型不同规范的方法也不同。造成这种类型不同主要是由于render函数是用户自己手写的还是template生成的。接下来我们来看下children的规范化

下面根据normalizationType===ALWAYS_NORMALIZE,那么ALWAYS_NORMALIZE是从哪里来的?

我们看到normalizationType是根据createElement传入的第六个参数alwaysNormalize确定的

当alwaysNormalize是true时候normalizationType=2

_createElement也会传入normalizationType 如果 它和之上createElement的ALWAYS_NORMALIZE相等,那么就调用normalizeChildren,如果等于1那么就调用simpleNormalizeChildren

接下来我们来看下normalizeChildren和simpleNormalizeChildren的实现

normalizeChildren中主要是调用normalizeArrayChildren函数去处理children类数组,判断children中的元素是不是数组,如果是的话,就递归调用数组,并将每个元素保存在数组中返回。normalizeArrayChildren 接收 2 个参数,children 表示要规范的子节点,nestedIndex 表示嵌套的索引,因为单个 child 可能是一个数组类型。 normalizeArrayChildren 主要的逻辑就是遍历 children,获得单个节点 c,然后对 c 的类型判断,如果是一个数组类型,则递归调用 normalizeArrayChildren; 如果是基础类型,则通过 createTextVNode 方法转换成 VNode 类型;否则就已经是 VNode 类型了,如果 children 是一个列表并且列表还存在嵌套的情况,则根据 nestedIndex 去更新它的 key。这里需要注意一点,在遍历的过程中,对这 3 种情况都做了如下处理:如果存在两个连续的 text 节点,会把它们合并成一个 text 节点。经过对 children 的规范化,children 变成了一个类型为 VNode 的 Array接下来我们讨论下vnode的创建

tag可以是个字符串也可以是个component,if(config.isReservedTag(tag))判断是否为html的保留标签,如果是的话就创建平台的保留标签,实例化一个vnode,如果是为已注册的组件名,则通过createComponent创建一个组件类型的 VNode,否则创建一个未知的标签的 VNode,如果是tag一个Component类型,则直接调用createComponent创建一个组件类型的 VNode 节点 ._createElement已经完成了创建VNode的功能,将其返回给render函数。

到此,我们已经知道创建一个element大致过程,createElement函数主要是通过对children进行扁平化处理之后,通过对标签(tag)的判断,使用VNode类来生成vnode,并将其返回。

下一节,我们会继续讨论_update过程

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20190222G03QSL00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券