[TOC]
vue render 函数 跟 template 一样都是创建 html 模板的。render 函数即渲染函数,它是个函数,它的参数也是个函数(即 createElement),下面我们重点来说 createElement 。
表层:createElement用法
|
|
createElement参数
接下来你需要熟悉的是如何在 createElement 函数中使用模板中的那些功能。这里是 createElement 接受的参数:
- 第一个参数:【必填】{String | Object | Function} 要渲染的标签名称
- 第二个参数:【可选】{Object} 模板中的数据
- 第三个参数:【可选】子级虚拟节点 (VNodes),由
createElement()
构建而成,也可以使用字符串来生成“文本虚拟节点”
createElement源码解析
首先对
createElement
函数对参数做一层处理,对参数个数不一致情况做处理,然后调用_createElement
函数123456789101112131415161718192021/*** 返回vnode或者vnode组件的数组数据*/export function createElement (context: Component, // 上下文thistag: any, // 标签data: any, // vnode数据children: any, // 子节点normalizationType: any,alwaysNormalize: boolean): VNode | Array<VNode> {if (Array.isArray(data) || isPrimitive(data)) { // 当vnode数据是数组或者基本类型的时候,对参数个数不一致的处理normalizationType = childrenchildren = datadata = undefined}if (isTrue(alwaysNormalize)) {normalizationType = ALWAYS_NORMALIZE}return _createElement(context, tag, data, children, normalizationType) // 真实调用}_createElement
函数:- 中先对节点data做判断,如果data是响应式的则报警告并生成一个vnode的注释节点
- 对children做规范化处理:分children是个二维数组和children多层数组嵌套两种情况,将他们拍平成一个一维数组
- 创建vnode节点:html标签、组件…
_createElement函数全部
children规范化:
simpleNormalizeChildren:场景是render函数是编译生成的,
12345678export function simpleNormalizeChildren (children: any) {for (let i = 0; i < children.length; i++) {if (Array.isArray(children[i])) {return Array.prototype.concat.apply([], children)}}return children}normalizeChildren:场景是render 函数是用户手写的,当 children 只有一个节点的时候,Vue 从接口层面允许用户把 children 写成基础类型用来创建单个简单的文本节点,这种情况下回调用 createTextVNode 创建一个文本节点的 VNode;另一个场景就是当编译 slot、 v-for 的时候回产生嵌套数组的情况,回调用 normalizeArrayChildren 方法。
normalizeArrayChildren 接收 2 个参数,children 表示要规范的子节点,nestedIndex 表示嵌套的索引,因为单个 child 可能是一个数组类型。normalizeArrayCHildren 主要的逻辑就是遍历 children,获得单个节点 c,然后对 c 的类型判断:
如果是一个数组类型,则递归调用 normalizeArrayChildren;
如果是基础类型,则通过 createTextVNode 方法转换成 VNode 类型;否则就已经是 VNode 类型了;
如果 children 是一个列表并且列表还存在嵌套的情况,则根据 nestedIndex 去更新它的 key。12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667/*** 场景是render 函数是用户手写的, 当 children 只有一个节点的时候, Vue 从接口层面允许用户把 children 写成基础类型用来创建单个简单的文本节点,* 这种情况下回调用 createTextVNode 创建一个文本节点的 VNode; 另一个场景就是当编译 slot、 v-for 的时候回产生嵌套数组的情况,* 回调用 normalizeArrayChildren 方法。* @param {*} children 要规范的子节点* @param {string} [nestedIndex] 嵌套的索引* @returns {Array<VNode>} 返回拍平的一维数组*/function normalizeArrayChildren (children: any, nestedIndex?: string): Array<VNode> {const res = []let i, c, lastIndex, lastfor (i = 0; i < children.length; i++) {c = children[i] // c是单个节点if (isUndef(c) || typeof c === 'boolean') continuelastIndex = res.length - 1last = res[lastIndex]// nestedif (Array.isArray(c)) { // 如果是一个数组类型,则递归调用 normalizeArrayChildrenif (c.length > 0) {c = normalizeArrayChildren(c, `${nestedIndex || ''}_${i}`)// merge adjacent text nodesif (isTextNode(c[0]) && isTextNode(last)) {res[lastIndex] = createTextVNode(last.text + (c[0]: any).text)c.shift()}res.push.apply(res, c)}} else if (isPrimitive(c)) { // 如果是基础类型,则通过 createTextVNode 方法转换成 VNode 类型;否则就已经是 VNode 类型了if (isTextNode(last)) {// merge adjacent text nodes// this is necessary for SSR hydration because text nodes are// essentially merged when rendered to HTML stringsres[lastIndex] = createTextVNode(last.text + c)} else if (c !== '') {// convert primitive to vnoderes.push(createTextVNode(c))}} else { // 其他类型if (isTextNode(c) && isTextNode(last)) { // 如果当前节点是文本节点,那就和上一个文本节点合并(优化)// merge adjacent text nodesres[lastIndex] = createTextVNode(last.text + c.text)} else {/** 当前节点c是一个嵌套数组(比如v-for产生的) 比如:render: function (createElement) {if (this.items.length) {return createElement('ul', this.items.map(function (item) {return createElement('li', item.name)}))} else {return createElement('p', 'No items found.')}}根据 nestedIndex 去更新它的 key*/// default key for nested array children (likely generated by v-for)if (isTrue(children._isVList) &&isDef(c.tag) &&isUndef(c.key) &&isDef(nestedIndex)) {c.key = `__vlist${nestedIndex}_${i}__`}res.push(c)}}}return res}
上面也大致了解了 createElement 创建 VNode 的过程。每个 VNode 有 children,children 每个元素也是一个 VNode,这样就形成了一个 VNode Tree,它很好的描述了 DOM Tree。下一个步骤就是要把这个 VNode 渲染成一个真实的 DOM 并且渲染出来,这个过程是通过 vm._update 完成的。
参考:
https://blog.csdn.net/sansan_7957/article/details/83014838
https://cn.vuejs.org/v2/guide/render-function.html
https://www.jianshu.com/p/709fc34e27b9