Skip to content

Latest commit

 

History

History
111 lines (84 loc) · 4.67 KB

children的归一化处理.md

File metadata and controls

111 lines (84 loc) · 4.67 KB

归一化操作其实就是将多维的数组,合并转换成一个一维的数组。在我们Vue源码中,它分为三个级别,0表示不需要进行归一化处理,1表示只需要简单的归一化处理,2表示要考虑到所有的情况,此时归一化处理会比较复杂。

模板编译器尝试在编译时静态分析模板来使归一化的需求降到最低(就是尽量不用归一化)。

简单的html标签由于生成的render函数返回的是一个VNode数组,可以直接跳过归一化。有两种情况需要额外的归一化处理:

  1. 子元素包含自定义component——因为函数式的component可能返回一个数组而不是一个根节点,这种情况下,我们只需要简单的归一化,如果子元素是数组,则通过Array.prototype.concat来把子元素合并为一个数组。最终生成的是一个一维数组,因为component已经对他自己的子内容进行了相同的操作。

  2. 子元素包含生成嵌套数组的结构,比如<template><slot>v-for或子元素是用户手写的render函数。这个时候我们就需要完全归一化来处理所有可能的children值。

我们用户手写的render函数,都会进行完全的归一化处理,而通过HTML模板解析生成的render函数,会根据内容的不同而进行不同级别的归一化处理。

该部分代码在src/core/vdom/helpers/normalize-children.js文件中。

simpleNormalizeChildren

简单归一化处理:

export 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
}

该过程很简单,如果children中有元素是数组,则直接通过Array.prototype.concat.apply([], children)来把整个children合并为一个数组,而不用去考虑里面的具体情况。

normalizeChildren

完全归一化处理:

export function normalizeChildren (children: any): ?Array<VNode> {
  return isPrimitive(children)
    ? [createTextVNode(children)]
    : Array.isArray(children)
      ? normalizeArrayChildren(children)
      : undefined
}

如果我们传入的children是字符串或者数字,则直接返回文本结点数组,比如如下例子:

render: function(h){
  // return h('div', "test");
  return h('div', 213);
}

如果我们传入的children是一个数组,则通过normalizeArrayChildren进行处理。

function normalizeArrayChildren (children: any, nestedIndex?: string): Array<VNode> {
  const res = []
  let i, c, last
  for (i = 0; i < children.length; i++) {
    c = children[i]
    if (c == null || typeof c === 'boolean') continue
    last = res[res.length - 1]
    //  nested
    if (Array.isArray(c)) {
      res.push.apply(res, normalizeArrayChildren(c, `${nestedIndex || ''}_${i}`))
    } else if (isPrimitive(c)) {
      if (last && last.text) {
        last.text += String(c)
      } else if (c !== '') {
        // convert primitive to vnode
        res.push(createTextVNode(c))
      }
    } else {
      if (c.text && last && last.text) {
        res[res.length - 1] = createTextVNode(last.text + c.text)
      } else {
        // default key for nested array children (likely generated by v-for)
        if (c.tag && c.key == null && nestedIndex != null) {
          c.key = `__vlist${nestedIndex}_${i}__`
        }
        res.push(c)
      }
    }
  }
  return res
}

我们来简单看一下它都做了哪些处理。

首先创建了一个新的数组res,然后循环children数组:

1、如果cundefinednullBoolean类型的值,则直接忽略。

2、如果c是一个数组,则递归的执行normalizeArrayChildren`方法,使多维数组转换为一维的。例:

var h = vm.$createElement;
h('div', ["test", [h('p'), h('p')], null, true])

最终转换之后的children包含三个元素VNode对象,第一个是文本为“text”的文本结点,然后依次是两个p标签。而最后两个被忽略。

3、如果c是字符串或者数字,则判断res中最后一个元素是不是文本结点,如果是则合并结点,如果不是且c不是空字符串,则创建一个新的文本结点。

4、如果c是一个文本VNode对象,且res中最后一个元素也是文本结点,则合并两个VNode为一个。否则如果cVnode对象,若c.tag存在,且c.key不存在,且nestedIndex存在(第二步中递归调用时会传入),则设置c.key的值,也就是说会给嵌套的VNode对象添加key

最终返回res数组。