Vue3 报错:Cannot read properties of null (reading 'insertBefore')

一不小心又来了一个线上问题。搜了网上的资料,有一些其他的情况,目前我遇到的是与v-if相关的,仅记录此例。

场景

同一个 div 上,既绑定了 v-else又绑定了一个自定义指令。

  1. 组件刚挂载时,v-if 不满足,显示该 div
  2. 该 div 执行mounted钩子函数时,自定义指令会把该元素移除
  3. 过了一会,v-if条件满足,然后触发 vue 的派发更新,此时 vue 内部抛出异常

测试文件:

html 复制代码
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.3.4/vue.global.js"></script>
<div id="app">
  <div v-if="hasData">hasData</div>
  <div v-else v-permission-or="['admin']">else</div>
</div>

<script>
  const { ref, nextTick } = window.Vue
  // 用户的权限只有 dev
  const myPermissions = ['dev']
  // mock 请求
  const request = () => {
    return new Promise(resolve => {
      setTimeout(() => {
        resolve()
      }, 500)
    })
  }

  Vue.createApp({
    directives: {
      'permission-or': {
        mounted(el, binding, vnode, prevVnode) {
          if (!myPermissions.includes(binding.value)) {
            // 用户是没有权限的,所以必然会执行此处
            el.remove()
            // 移除后,其父节点为 null
            console.log('el.parentNode', el.parentNode)
          }
        }
      }
    },
    setup() {
      const hasData = ref(false)
      request().then(() => {
        hasData.value = true
      })
      return { hasData }
    }
  }).mount('#app')
</script>

源码分析

首先依赖收集部分就不赘述了,在依赖更新时,会触发effect:

componentUpdateFun函数中,会调用patch函数:

注意这里传入的第三个参数,实际取的是该节点的父元素: 取到的值是什么呢?是null,因为文档中已经不存在该节点了。

继续看,到patch函数内,首先会判断新旧元素是否相同,是则直接 return。这里显然不满足,于是会继续判断isSameVNodeType,该方法的作用是判断是否为同一个节点,我们这里也不是同一个,是要从第二个 div 变为第一个 div,所以isSameVNodeType(n1, n2)为 false,这个if会进去,然后会把旧节点n1设为 null. 继续往下,有个 switch,在这里面会进入default里的第一个if判断中,执行processElement方法,这里传入的container就是外面传进来的父元素null

这个函数就很简单了,判断旧节点是否为null,注意到刚才已经被修改为null了,因此会进入到if内:

然后到mountElement方法中,会执行hostInsert方法:

这个方法的定义处,与之前看到的patch第三个参数的取值,是同一个文件:

问题就在这里了,一路传下来的containernull,到insert方法中,传入了第二个参数parent,于是浏览器报错:

arduino 复制代码
Uncaught (in promise) TypeError: Cannot read properties of null (reading 'insertBefore')

这个错误是 Promise 抛出的,一开始我还查不到日志,因为没有写全局捕获方法。不过即使捕获,也只是提供一下日志罢了,反正 vue 的代码不能继续执行的,所以页面更新也还是异常的。

------为什么是 Promise 抛出的?

因为vue3的依赖更新,本来就是通过 Promise 实现的。就像那道经典题------"nextTick"的原理......

算了,不扯了吧,我也不懂了......

相关推荐
成都渲染101云渲染66662 小时前
如何在3ds Max中实现更快、更高质量的渲染
前端·javascript·人工智能
闲猫4 小时前
Spring AI 对接Deepseek ChatModel 聊天对话
java·前端·spring
自信的未来6 小时前
JSON 工具|Web Worker 工程化打包 + 语法自动修复 + 多语言代码生成实战
java·前端·json
全栈前端老曹6 小时前
【MongoDB】安全与权限管理 —— 用户认证、角色权限、SSL 加密
前端·javascript·数据库·安全·mongodb·nosql·ssl
zhangxingchao7 小时前
AI大模型核心八:从 Agent Skill、长文档 RAG 到知识库更新与训练策略
前端·人工智能·后端
zhangxingchao8 小时前
AI大模型核心七:从 Workflow、RAG、记忆治理到幂等性
前端·人工智能·后端
gyx_这个杀手不太冷静8 小时前
Agent开发进阶指南(第 1 章):AI Agent 到底是什么?
前端·agent·ai编程
小小小小宇9 小时前
模型相关知识
前端
小小小小宇9 小时前
模型相关知识二
前端
IT_陈寒9 小时前
Python装饰器竟然偷偷改了函数名?这个坑我爬了三天
前端·人工智能·后端