这是该系列第一期~
主题:vue3 v3.4.24 在 transition 中使用 keep-alive 导致的 bug
复现代码:
1 | <template> |
修复 pr 请看:修复 pr
我们看 3.4.24packages/runtime-core/src/components/BaseTransition.ts 的 getKeepAliveChild 函数,关键代码如下:
1 | function getKeepAliveChild(vnode: VNode): VNode | undefined { |
这里有几个需要解释的:
- vnode:vue3 的虚拟节点,相当于用 js 描述一个真实的 dom,比如一个 DIV 标签,其 className 是 red,id 是 root,并且该标签有一个点击函数 onClick,如果想渲染这个函数,我们就可以传如下参数,第一个参数是 dom 的类型,p、div 等;第二个参数存储标签的信息:
1 | function func() {} |
- ShapeFlags
ShapeFlags 定义在 packages/shared/src/shapeFlags.ts 中,主要是为了用位运算做性能优化。每一个 vnode 都有一个节点类型,而使用位运算来判断节点类型,性能比 a===b 更快。
创建一个 vnode 参考:packages/runtime-core/src/vnode.ts,createBaseVNode 函数会对 shapeFlag 进行赋值:
1 | // createBaseVNode.ts |
因此如果你不懂什么是位运算也没关系,只需要知道这个 if 就是为了判断节点类型而存在。在该 if 中,是为了判断当前节点是否是数组类型。
现在回到 getKeepAliveChild 函数中:
getKeepAliveChild 的作用:获取 Transition.keepAlive 中的节点。
而目前的逻辑显然是判断错了,为什么这么说?其实仅看 getKeepAliveChild 函数就能分析出代码意图:
1 | // 如果当前vnode是个数组,那么返回当前vnode的children,这点就很不能让人理解了 |
这段代码判断的是该 vnode 的类型,return 的是 vnode.children[0]。如果 vnode.children 长度为 0 ,就会报错。
因此就需要加上 children 的判断逻辑:
1 | if (children) { |
现在这段代码就变成了:如果存在 children,那么:如果当前节点是数组节点,就返回 children[0];如果是个 slot,就返回该 slot 的结果。
我们可以看看(该 pr 对应的 issue)[https://github.com/vuejs/core/issues/10771] ,报错都是因为 Cannot read property ‘0’ of null,就是因为下标问题。(生产环境才有,dev 不会)
所以你可以尝试着这样做:1 .找到一个 pr,去看对应的 issue,复现步骤,然后再去研究该 pr 为什么要这样改。
你看这些前端天花板们都会遇到各种各样的 bug,更别提咱了。写 bug 不可怕,关键是要有快速定位&&解决 bug 的能力。加油~