zoukankan      html  css  js  c++  java
  • vnodec创建之标签

      var SIMPLE_NORMALIZE = 1;
      var ALWAYS_NORMALIZE = 2;
    
      // wrapper function for providing a more flexible interface
      // without getting yelled at by flow

    createElement 方法实际上是对 _createElement 方法的封装,它允许传入的参数更加灵活,在处理这些参数后,调用真正创建 VNode 的函数 _createElement

     

    每个 VNode 有 childrenchildren 每个元素也是一个 VNode,这样就形成了一个 VNode Tree,它很好的描述了我们的 DOM Tree。

      function createElement (
        context,
        tag,
        data,
        children,
        normalizationType,
        alwaysNormalize
      ) {
        if (Array.isArray(data) || isPrimitive(data)) {  //传入的参数不一致的时候处理,往前进一位,data可以是没有的,所以更灵活处理
          normalizationType = children;
          children = data;
          data = undefined;
        }
        if (isTrue(alwaysNormalize)) {
          normalizationType = ALWAYS_NORMALIZE; //就是看是一层扁平还是深层
        }
        return _createElement(context, tag, data, children, normalizationType)
      }
      function _createElement (
        context, //vnode的上下文环境,是Component类型
        tag, //标签,可以String,也可以是Component
        data, vnode的数据
        children, //子节点可以是任意类型
        normalizationType //子节点规范的类型,主要参考render函数是手写还是编译生成的
      ) {
        if (isDef(data) && isDef((data).__ob__)) {  //不能是响应式数据,这个我不太懂
          warn(
            "Avoid using observed data object as vnode data: " + (JSON.stringify(data)) + "
    " +
            'Always create fresh vnode data objects in each render!',
            context
          );
          return createEmptyVNode()  //就是注释节点
        }
        // object syntax in v-bind
        if (isDef(data) && isDef(data.is)) {  
          tag = data.is;
        }
        if (!tag) {
          // in case of component :is set to falsy value
          return createEmptyVNode()  
        }
        // warn against non-primitive key
        if (isDef(data) && isDef(data.key) && !isPrimitive(data.key)
        ) {
          {
            warn(
              'Avoid using non-primitive value as key, ' +
              'use string/number value instead.',
              context
            );
          }
        }
        // support single function children as default scoped slot
        if (Array.isArray(children) &&
          typeof children[0] === 'function'
        ) {
          data = data || {};
          data.scopedSlots = { default: children[0] };
          children.length = 0;
        }
        if (normalizationType === ALWAYS_NORMALIZE) {  
          children = normalizeChildren(children);  //这里最重要了,重点看这两个函数,下面的都可以先跳过看这两个函数 
        } else if (normalizationType === SIMPLE_NORMALIZE) {  
          children = simpleNormalizeChildren(children); //重点
        }

    这里先对 tag 做判断,如果是 string 类型,则接着判断如果是内置的一些节点,则直接创建一个普通 VNode,如果是为已注册的组件名,则通过 createComponent 创建一个组件类型的 VNode,

    否则创建一个未知的标签的 VNode。 如果是 tag 一个 Component 类型,则直接调用 createComponent 创建一个组件类型的 VNode 节点。对于 createComponent 创建组件类型的 VNode 的过程,

    本质上它还是返回了一个 VNode。

    
        var vnode, ns;
        if (typeof tag === 'string') {
          var Ctor;
          ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag);
          if (config.isReservedTag(tag)) {
            // platform built-in elements
            if (isDef(data) && isDef(data.nativeOn)) { //.native只能用在组件里
              warn(
                ("The .native modifier for v-on is only valid on components but it was used on <" + tag + ">."),
                context
              );
            }
            vnode = new VNode(
              config.parsePlatformTagName(tag), data, children,
              undefined, undefined, context
            );
          } else if ((!data || !data.pre) && isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {
            // component
            vnode = createComponent(Ctor, data, context, children, tag);
          } else {
            // unknown or unlisted namespaced elements
            // check at runtime because it may get assigned a namespace when its
            // parent normalizes children
            vnode = new VNode(
              tag, data, children,
              undefined, undefined, context
            );
          }
        } else {
          // direct component options / constructor
          vnode = createComponent(tag, data, context, children);
        }
        if (Array.isArray(vnode)) {
          return vnode
        } else if (isDef(vnode)) {
          if (isDef(ns)) { applyNS(vnode, ns); }
          if (isDef(data)) { registerDeepBindings(data); }
          return vnode
        } else {
          return createEmptyVNode()
        }
      }

    normalizeChildren 方法的调用场景有 2 种,一个场景是 render 函数是用户手写的,当 children 只有一个节点的时候,Vue.js 从接口层面允许用户把 children 写成基础类型用来创建单个简单的文本节点,

    这种情况会调用 createTextVNode 创建一个文本节点的 VNode;另一个场景是当编译 slotv-for 的时候会产生嵌套数组的情况,会调用 normalizeArrayChildren 方法

     function normalizeChildren (children) { 
        return isPrimitive(children)
          ? [createTextVNode(children)]
          : Array.isArray(children)
            ? normalizeArrayChildren(children)
            : undefined
      }
    
      function isTextNode (node) {
        return isDef(node) && isDef(node.text) && isFalse(node.isComment)
      }

     normalizeArrayChildren 主要的逻辑就是遍历 children,获得单个节点 c,然后对 c 的类型判断,如果是一个数组类型,则递归调用 normalizeArrayChildren; 如果是基础类型,

    则通过 createTextVNode 方法转换成 VNode 类型;否则就已经是 VNode 类型了,如果 children 是一个列表并且列表还存在嵌套的情况,则根据 nestedIndex 去更新它的 key。

    这里需要注意一点,在遍历的过程中,对这 3 种情况都做了如下处理:如果存在两个连续的 text节点,会把它们合并成一个 text 节点。

     

    经过对 children 的规范化,children 变成了一个类型为 VNode 的 Array。

    
      function normalizeArrayChildren (children, nestedIndex) {  //children表示要规范的子节点,nestedIndex表示嵌套的索引,因为单个child可能是一个数组类
        var res = [];
        var i, c, lastIndex, last;
        for (i = 0; i < children.length; i++) {
          c = children[i];
          if (isUndef(c) || typeof c === 'boolean') { continue }
          lastIndex = res.length - 1;
          last = res[lastIndex];
          //  nested
          if (Array.isArray(c)) {
            if (c.length > 0) {
              c = normalizeArrayChildren(c, ((nestedIndex || '') + "_" + i));
              // merge adjacent text nodes
              if (isTextNode(c[0]) && isTextNode(last)) {
                res[lastIndex] = createTextVNode(last.text + (c[0]).text);
                c.shift();
              }
              res.push.apply(res, c);
            }
          } else if (isPrimitive(c)) {
            if (isTextNode(last)) {
              // merge adjacent text nodes
              // this is necessary for SSR hydration because text nodes are
              // essentially merged when rendered to HTML strings
              res[lastIndex] = createTextVNode(last.text + c);
            } else if (c !== '') {
              // convert primitive to vnode
              res.push(createTextVNode(c));
            }
          } else {
            if (isTextNode(c) && isTextNode(last)) {  //都是文本节点优化
              // merge adjacent text nodes
              res[lastIndex] = createTextVNode(last.text + c.text);
            } else {
              // 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
      }
     // 1. When the children contains components - because a functional component
      // may return an Array instead of a single root. In this case, just a simple
      // normalization is needed - if any child is an Array, we flatten the whole
      // thing with Array.prototype.concat. It is guaranteed to be only 1-level deep
      // because functional components already normalize their own children.

    simpleNormalizeChildren 方法调用场景是 render 函数当函数是编译生成的。理论上编译生成的 children 都已经是 VNode 类型的,

    但这里有一个例外,就是 functional component 函数式组件返回的是一个数组而不是一个根节点,所以会通过 Array.prototype.concat 方法把整个 children 数组打平,让它的深度只有一层

      function simpleNormalizeChildren (children) {  
        for (var i = 0; i < children.length; i++) {
          if (Array.isArray(children[i])) {
            return Array.prototype.concat.apply([], children)
          }
        }
        return children
      }
    //
  • 相关阅读:
    Ansible安装配置
    Git 工作流程
    使用 Docker 搭建 Tomcat 运行环境
    Linux的cron与%
    配置sonar和jenkins进行代码审查
    Jenkins配置基于角色的项目权限管理
    Jenkins和maven自动化构建java程序
    Jenkins修改workspace和build目录
    Git 进阶指南
    git代码回滚:Reset、Checkout、Revert的选择
  • 原文地址:https://www.cnblogs.com/TTblog5/p/12862598.html
Copyright © 2011-2022 走看看