Nuxt^2.14.12路由跳转时Vue的createPatchFunction报nodeOps.tagName未定义错误
错误根因
该错误是Vue 2.6.x版本的边界兼容问题,结合Nuxt 2 SSR渲染的场景触发:
- Vue的
emptyNodeAt方法默认假设传入的DOM节点是元素节点,没有兼容注释节点的场景:注释节点的nodeType为8,本身不存在tagName属性,调用nodeOps.tagName(elm)会返回undefined,后续执行toLowerCase()方法就会触发报错。 - 你使用的是Nuxt 2的universal模式,服务端渲染返回的HTML中包含注释节点,路由切换触发DOM patch时,Vue的diff算法尝试复用该注释节点作为元素节点处理,直接触发崩溃。
排查步骤
- 先验证是否为SSR hydrate不匹配导致:将
nuxt.config.js中的mode改为spa,重启项目走纯客户端渲染,如果报错消失即可确认是SSR渲染与客户端hydrate结构不匹配导致。 - 定位注释节点来源:
- 在报错的
emptyNodeAt方法处加断点,查看入参elm对应的父节点所属的组件,定位到具体产生注释的页面/组件。 - 检查对应组件的条件渲染逻辑:如果存在服务端渲染时
v-if条件为false输出注释、客户端hydrate时v-if条件为true需要渲染元素的场景,会导致diff时取到错误的注释节点。 - 排查第三方组件:你依赖的
vue-numeric-input、@chenfengyuan/vue-number-input早期版本存在SSR下生成多余注释节点的问题,优先检查这两个组件的使用逻辑。
解决方法
- 版本升级方案:将Vue和
vue-template-compiler同步升级到2.7.x版本,Vue 2.7已经修复了该边界问题,会自动跳过注释节点处理,无业务侵入性。 - 移除注释节点方案:如果无法升级Vue,在
nuxt.config.js的build配置中添加如下规则,强制移除模板编译产生的所有注释节点:
export default { // 其他原有配置 build: { extend(config) { const vueLoaderRule = config.module.rules.find(rule => rule.loader === 'vue-loader') vueLoaderRule.options.compilerOptions = vueLoaderRule.options.compilerOptions || {} vueLoaderRule.options.compilerOptions.comments = false } } }
- 第三方组件兼容方案:将可能导致问题的第三方组件改为仅客户端注册,比如在注册组件的插件中增加
process.client判断,或者在页面引用组件时添加.client后缀。 - 临时规避方案:如果需要快速恢复业务,将路由跳转逻辑用
$nextTick包裹,确保当前页面DOM更新完成后再执行路由切换:
this.$nextTick(() => { this.$router.push({ path: '/path' }) })
内容的提问来源于stack exchange,提问作者Babak Karimi Asl
相关产品推荐
相关产品推荐

