洞悉细节!react 16.8.6源码分析-4 children遍历

前言作为一个前端页面仔和需求粉碎机,在日常的工作中重复雷同的业务需求,能够获得的提高是很有限的。要想跳出此山中,开阔新视野,笔者墙裂建议大家阅读市面上顶尖开源库的源码。这是学习和掌握js语言特性的绝佳机会(前端发展到现在,大型应用高度依赖框架,正常情况下普通开发者是没有机会接触底层的语言特性),同时也是深刻理解框架底层思维的契机。这里笔者选择react第一个开刀,市面上不少关于react源码分析的文章要么过于老旧,要么只截取部分代码或者是伪代码,笔者这里将选取react的16.8.6版本作为示例,从第0行
摘要由CSDN通过智能技术生成

前言

作为一个前端页面仔和需求粉碎机,在日常的工作中重复雷同的业务需求,能够获得的提高是很有限的。要想跳出此山中,开阔新视野,笔者墙裂建议大家阅读市面上顶尖开源库的源码。这是学习和掌握js语言特性的绝佳机会(前端发展到现在,大型应用高度依赖框架,正常情况下普通开发者是没有机会接触底层的语言特性),同时也是深刻理解框架底层思维的契机。这里笔者选择react第一个开刀,市面上不少关于react源码分析的文章要么过于老旧,要么只截取部分代码或者是伪代码,笔者这里将选取react的16.8.6版本作为示例,从第0行开始,不漏过任何一个源码细节,和大家分享笔者在源码阅读过程中的体会。希望和大家共同进步,本系列博文中涉及的源码本人会放在git仓库中,链接在文末。

正文

子节点遍历

/**
 * @param {?*} children Children tree container.
 * @param {!string} nameSoFar Name of the key path so far.
 * @param {!function} callback Callback to invoke with each child found.
 * @param {?*} traverseContext Used to pass information throughout the traversal
 * process.
 * @return {!number} The number of children in this subtree.
 */

 // 这个是个递归函数,来统计子节点数目,也会执行回调
 // 遍历所有子节点的接口实现
 // traverseContext这个上下文本质上就是一个存储处理结果的对象
function traverseAllChildrenImpl(children, nameSoFar, callback, traverseContext) {
   
  //  获取children的类型
  var type = typeof children;
  //  如果类型为undifined或者布尔,children为null
  if (type === 'undefined' || type === 'boolean') {
   
    // All of the above are perceived as null.
    children = null;
  }

  var invokeCallback = false;

  //  如果type为undefined、boolean、string、number、REACT_ELEMENT_TYPE、REACT_PORTAL_TYPE时,表示已经调用到底层元素,要调用回调
  if (children === null) {
   
    invokeCallback = true;
  } else {
   
    switch (type) {
   
      case 'string':
      case 'number':
        invokeCallback = true;
        break;
      case 'object':
        switch (children.$$typeof) {
   
          case REACT_ELEMENT_TYPE:
          case REACT_PORTAL_TYPE:
            invokeCallback = true;
        }
    }
  }

  if (invokeCallback) {
   
    //  使用上级传下来的上下文跑一下回调,同时计数,第三个参数,累加当前的组件名
    //  针对mapIntoWithKeyPrefixInternal,这个callback其实是mapSingleChildIntoContext
    callback(traverseContext, children,
    // If it's the only child, treat the name as if it was wrapped in an array
    // so that it's consistent if the number of children grows.

    //  如果这是唯一的子元素,把这个名字当做包裹在数组里面的处理,是的子元素增加的时候保持名字不变
    nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar);
    //  返回计数1
    return 1;
  }

  var child = void 0;
  //  往下传递的名字
  var nextName = void 0;
  //  当前子树下子元素的节点个数
  var subtreeCount = 0; // Count of children found in the current subtree.
  //  下一个名字的前缀,如果当前的名字是空串,设置为.,否则是当前的名字+分隔符:
  var nextNamePrefix = nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR;

  //  数组的话继续递归
  if (Array.isArray(children)) {
   
    for (var i = 0; i < children.length; i++) {
   
      child = children[i];
      //  拼出下一个名字
      nextName = nextNamePrefix + getComponentKey(child, i);
      //  递归调用,获得当前子树下挂载的节点数
      subtreeCount += traverseAllChildrenImpl(child, nextName, callback, traverseContext);
    }
  } else {
   
    //  如果是迭代器的话也继续递归
    //  获取children的迭代器
    var iteratorFn = getIteratorFn(children);
    if (typeof iteratorFn === 'function') {
   
      {
   
        // Warn about using Maps as children
        //  如果使用map当做子元素,报错
        if (iteratorFn === children.entries) {
   
          //  控制这个报错只出现一次
          !didWarnAboutMaps ? warning$1(false, 'Using Maps as children is unsupported and will likely yield ' + 'unexpected results. Convert it to a sequence/iterable of keyed ' + 'ReactElements instead.'
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值