惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
WordPress大学
WordPress大学
T
Tailwind CSS Blog
V
Visual Studio Blog
月光博客
月光博客
Hugging Face - Blog
Hugging Face - Blog
小众软件
小众软件
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - Franky
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Last Week in AI
Last Week in AI
阮一峰的网络日志
阮一峰的网络日志
量子位
有赞技术团队
有赞技术团队
酷 壳 – CoolShell
酷 壳 – CoolShell
Apple Machine Learning Research
Apple Machine Learning Research
博客园_首页
Jina AI
Jina AI
雷峰网
雷峰网
博客园 - 【当耐特】
博客园 - 叶小钗
美团技术团队
宝玉的分享
宝玉的分享
IT之家
IT之家

蚊子的前端博客

微说 | 有的人觉得只要不考XX,一定能考好!-蚊子的前端博客 微说 | 春天来了-蚊子的前端博客 明天和意外不知道哪个先来-蚊子的前端博客 微说 | 既然错过了路口,就及时止损,重新规划路线上路-蚊子的前端博客 微说 | 2025年的出生人口数是792万-蚊子的前端博客 微说 | 工作年终总结,让写对接的接口的数量?-蚊子的前端博客 微说 | 温家宝:没有政治体制改革的成功 经济体制改革不可能进行到底-蚊子的前端博客 微说 | 违法犯罪了该不该被禁言?-蚊子的前端博客 微说 | 不明白为什么在推荐去俄罗斯旅游-蚊子的前端博客 微说 | 易中天论骗子-蚊子的前端博客 微说 | 一场大风吹散了秋天-蚊子的前端博客 微说 | 既要、又要、还要、更要!-蚊子的前端博客 又是一年的国庆雨季-蚊子的前端博客 微说 | 预估下2025年的出生人口数据-蚊子的前端博客 微说 | 可惜了我那些小时候的书本-蚊子的前端博客 微说 | 牛马有的是,驴不够了!-蚊子的前端博客 微说 | 能否有一条非户口也能高考的路-蚊子的前端博客 微说 | 小聊中医-蚊子的前端博客 微说 | 将要制作的一款新产品-蚊子的前端博客 微说 | 做人要有信-蚊子的前端博客 微说 | 好一个正义联盟-蚊子的前端博客 微说 | 都是见过吃过的主儿-蚊子的前端博客 微说 | 追求8小时工作制有错吗?-蚊子的前端博客 微说 | 如果尖锐的批评完全消失-蚊子的前端博客 微说 | 很好!-蚊子的前端博客 微说 | 封禁用户可以,但要告知具体原因-蚊子的前端博客 微说 | 面朝大海,春暖花开-蚊子的前端博客 微说 | 程序员的悲哀是什么?-蚊子的前端博客 微说 | 2025年出生人口的预测-蚊子的前端博客 前端在 LiveKit 中如何获取所有的参与者-蚊子的前端博客
React 模板中为什么可以用逻辑与运算符-蚊子的前端博客
author · 2024-09-03 · via 蚊子的前端博客

我们在开发 React 项目时,经常会用到逻辑与&&的运算符。

const App = () => {
  const [isShow, setShow] = useState(false);

  // 当 isShow 为 true 时,才会渲染 <div>show me</div>
  return <div>{isShow && <div>show me</div>}</div>;
};

逻辑与运算符&&,在 JavaScript 中,它的作用是,如果前面的表达式为真,则返回后面的表达式,否则返回前面的表达式。

可是这里有个问题:对于完整的逻辑与表达式,若isShow为 false 时,则返回 false,为什么当前区域展示内容是空的,而不是展示false呢?

这其实跟 React 的渲染机制有关。我们在之前的文章React18 源码解析之 reconcileChildren 生成 fiber 的过程提到过。这里再稍微介绍下。

/**
 * 将returnFiber节点(即当前的workInProgress对应的节点)里的element结构转为fiber结构
 * @param returnFiber 当前的workInProgress对应的fiber节点
 * @param currentFirstChild current 树上对应的当前 Fiber 节点的第一个子 Fiber 节点,可能为null
 * @param newChild returnFiber中的element结构,用来构建returnFiber的子节点
 * @param lanes
 * @returns {Fiber|*}
 */
function reconcileChildFibers(
  returnFiber: Fiber, // 当前 Fiber 节点,即 workInProgress
  currentFirstChild: Fiber | null,
  newChild: any,
  lanes: Lanes // 优先级相关
): Fiber | null {
  // 是否是顶层的没有key的fragment组件
  const isUnkeyedTopLevelFragment =
    typeof newChild === "object" &&
    newChild !== null &&
    newChild.type === REACT_FRAGMENT_TYPE &&
    newChild.key === null;

  // 若是顶层的fragment组件,则直接使用其children
  if (isUnkeyedTopLevelFragment) {
    newChild = newChild.props.children;
  }

  // Handle object types
  // 判断该节点的类型
  if (typeof newChild === "object" && newChild !== null) {
    /**
     * newChild是Object,再具体判断 newChild 的具体类型。
     * 1. 是普通React的函数组件、类组件、html标签等
     * 2. portal类型;
     * 3. lazy类型;
     * 4. newChild 是一个数组,即 workInProgress 节点下有并排多个结构,这时 newChild 就是一个数组
     * 5. 其他迭代类型,我暂时也不确定这哪种?
     */
    switch (newChild.$$typeof) {
      case REACT_ELEMENT_TYPE:
        // 一般的React组件,如<App />或<p></p>等
        return placeSingleChild(
          // 调度单体element结构的元素
          reconcileSingleElement(returnFiber, currentFirstChild, newChild, lanes)
        );
      case REACT_PORTAL_TYPE:
        return placeSingleChild(
          reconcileSinglePortal(returnFiber, currentFirstChild, newChild, lanes)
        );
      case REACT_LAZY_TYPE:
        const payload = newChild._payload;
        const init = newChild._init;
        // TODO: This function is supposed to be non-recursive.
        return reconcileChildFibers(returnFiber, currentFirstChild, init(payload), lanes);
    }

    if (isArray(newChild)) {
      // 若 newChild 是个数组
      return reconcileChildrenArray(returnFiber, currentFirstChild, newChild, lanes);
    }

    if (getIteratorFn(newChild)) {
      return reconcileChildrenIterator(returnFiber, currentFirstChild, newChild, lanes);
    }

    throwOnInvalidObjectType(returnFiber, newChild);
  }

  if ((typeof newChild === "string" && newChild !== "") || typeof newChild === "number") {
    // 文本节点
    return placeSingleChild(
      reconcileSingleTextNode(returnFiber, currentFirstChild, "" + newChild, lanes)
    );
  }

  // Remaining cases are all treated as empty.
  // 若没有匹配到任何类型,说明当前newChild无法转为fiber节点,如boolean类型,null等是无法转为fiber节点的
  // deleteRemainingChildren()的作用是删除 returnFiber 节点下,第2个参数传入的fiber节点,及后续所有的兄弟节点
  // 如 a->b->c->d-e,假如我们第2个参数传入的是c,则删除c及后续的d、e等兄弟节点,
  // 而这里,第2个参数传入的是 currentFirstChild,则意味着删除returnFiber节点下所有的子节点
  // 为什么要删除呢?这是因为,为了保证前后两棵树是一致的,若jsx在workInProgress所在树中,无法转为fiber节点,
  // 说明 returnFiber 下所有的fiber节点均无法复用
  return deleteRemainingChildren(returnFiber, currentFirstChild);
}

从上面的部分源码中可以看到,在 React 解析 jsx 时,它只会解析一些固定类型的节点,如:

  • 函数组件类型的;
  • 类组件类型;
  • html 标签类型的;
  • 纯文本类型的,如字符串或数字格式;
  • portal 类型的;
  • lazy 类型的;
  • 数组类型(这个会递归解析每一项);

而诸如 null, undefined, boolean 等类型的节点,则会直接跳过。因此,在一些比较复杂的判断中,有些不希望展示在页面中的,则可以返回这些类型的值。

针对开头样例中的逻辑与运算符,当isShow为 false 时,React 会跳过该数据的渲染,因此这里就会展示为空。

因此,若一个表达式的结果 boolean 类型时,不论是 true 还是 false,都不会转为 html,而是直接跳过。