Vue2 组件传函数 this 指向异常:this.$parent 的适用场景与局限
一、Vue2 组件传参时函数 this 指向异常的实际问题
在 Vue2 的日常组件开发过程中,父子组件传值是最基础也最常用的操作,除了传递普通数据、配置项之外,传递函数方法也是很常见的场景,比如把父页面的方法通过 props 传给子组件,供子组件内部调用触发。但很多开发者在实际编码时都会遇到一个典型问题: 将父页面的函数通过 props 传递给子组件后,函数内部的 this 指向发生了改变,不再指向当前父页面实例,而是指向了接收函数的子组件内部 。

这种 this 指向错乱的问题,会直接导致函数内部无法正常访问父页面的 data 数据、methods 方法以及挂载在实例上的全局属性,比如原本想通过 this 调用父页面的接口请求、修改页面状态,结果因为 this 指向子组件,要么报错找不到对应属性,要么操作的是子组件内部的数据,完全达不到预期的执行效果,这也是 Vue2 组件通信里很容易踩坑的一个细节点。下面通过基础代码还原这个问题场景:
<!-- 父页面/父组件 Parent.vue -->
<template>
<div class="parent">
<Child :handleFunc="handleParentFunc" />
</div>
</template>
<script>
import Child from './Child.vue'
export default {
name: 'Parent',
components: { Child },
data() {
return {
parentMsg: '我是父页面数据'
}
},
methods: {
handleParentFunc() {
// 正常情况下 this 应该指向父组件实例
console.log('当前 this 指向', this)
console.log('父页面数据', this.parentMsg)
}
}
}
</script>
<!-- 子组件 Child.vue -->
<template>
<button @click="handleFunc">点击调用父组件传递的函数</button>
</template>
<script>
export default {
name: 'Child',
props: {
handleFunc: {
type: Function,
required: true
}
},
data() {
return {
childMsg: '我是子组件数据'
}
}
}
</script>运行上述代码后点击按钮,控制台会打印出 this 指向子组件 Child 实例,而非父组件 Parent,此时访问 this.parentMsg 会得到 undefined,直接触发属性未找到的报错,这就是最典型的 this 指向异常问题。
二、单层父子组件下的临时解决方式:this.$parent
针对上述 this 指向异常的问题,在只有一层父子组件关系的场景下,大部分开发者会通过 this.$parent 这个 API 来做临时处理,这也是实际项目中比较直观的一种应对方式。当子组件调用从父页面传过来的函数时,函数内部原本指向子组件的 this,通过 this.$parent 就可以直接访问到上一级的父组件实例,也就是原本的当前页面实例,这样就能正常获取父页面的各类属性和方法,顺利完成对应的业务逻辑执行。
在单层嵌套的组件结构里,这种方式操作起来十分简便,不需要额外修改函数的绑定方式,也不用引入其他复杂的通信方案,直接在函数内部通过 this.$parent 就能精准定位到父页面实例,快速解决 this 指向错乱带来的功能异常,对于结构简单的页面组件来说,实用性很强,也符合快速开发的需求。对应的修复代码如下,只需在传递的函数内部改用 this.$parent 访问父级实例即可:
<!-- 优化后的父组件方法 -->
methods: {
handleParentFunc() {
// this 此时指向子组件,通过$parent 获取直接父组件(当前父页面)
console.log('子组件 this', this)
console.log('父页面实例', this.$parent)
// 正常访问父页面数据
console.log('修复后获取父页面数据', this.$parent.parentMsg)
}
}这段代码针对单层父子组件结构,点击子组件按钮后,this.$parent 能准确获取到父页面实例,顺利读取 parentMsg 数据,问题得到临时解决,全程无需改动组件传参和调用逻辑,改动成本极低。
三、this.$parent 方案的核心局限:多层组件嵌套失效
虽说 this.$parent 能解决单层父子组件的 this 指向问题,但这个方法有着很明显的适用边界, 一旦涉及多层组件嵌套的场景,该方案就会直接失效 。Vue2 中的 this.$parent 属性,本质上只能获取当前组件的直接父级实例,无法跨层级追溯到更上层的祖先组件,也就是说,当组件结构变成爷组件→父组件→子组件的多层嵌套模式时,子组件里通过 this.$parent 只能拿到直接父组件,而拿不到最顶层的爷组件(也就是最初传递函数的当前页面)。
这种情况下,继续依赖 this.$parent 就无法实现跨层级的实例访问,原本的 this 指向问题会再次出现,函数内部依旧无法访问到最顶层页面的实例数据和方法,也就意味着这个临时方案只适用于简单的单层父子组件结构,没办法适配复杂的多层嵌套组件场景,这也是该方法最核心的局限性。通过三层嵌套组件代码就能直观看到失效效果:
<!-- 爷组件 GrandFather.vue(最顶层页面) -->
<template>
<Parent :handleFunc="handleGrandFunc" />
</template>
<script>
import Parent from './Parent.vue'
export default {
name: 'GrandFather',
components: { Parent },
data() {
return {
grandMsg: '我是最顶层爷组件数据'
}
},
methods: {
handleGrandFunc() {
// 此处 this 指向最底层子组件
console.log('this.$parent 只能拿到父组件 Parent', this.$parent)
// 拿不到顶层 GrandFather 实例,访问 grandMsg 报错
console.log('顶层数据', this.$parent.grandMsg)
}
}
}
</script>
<!-- 父组件 Parent.vue(中间层)、子组件 Child.vue(最底层) -->
<!-- 三层嵌套后,Child 的$parent 是 Parent,Parent 的$parent 才是 GrandFather -->
<!-- 底层子组件调用函数时,this.$parent 无法直接触达顶层爷组件 -->从代码运行结果能清晰看出,多层嵌套后 this.$parent 只能获取直接父级,完全无法跨层级访问最初传递函数的顶层页面,方案彻底失效,这也是其无法突破的核心短板。
四、实际项目中多层组件嵌套的场景占比
从实际的 Vue2 项目开发经验来看,虽然多层组件嵌套是组件化开发中不可避免的结构,但 需要跨多层级传递函数且出现 this 指向问题的场景其实比较少 。大部分常规业务页面,组件拆分都以单层父子结构为主,复杂的多层嵌套大多出现在通用公共组件、高阶组件或者大型表单、弹窗嵌套的特殊场景里,日常的业务开发、简单页面搭建,基本不会用到多层级的组件嵌套结构。
也正是因为多层嵌套的实际使用频率不高,所以即便 this.$parent 存在多层级失效的问题,在绝大多数常规开发场景下,依旧能满足使用需求,不会对日常的项目开发造成太大影响,这也是很多开发者习惯用这个方式处理单层组件 this 指向问题的原因。
总结
Vue2 中组件传函数出现 this 指向子组件的问题,是组件通信里的常见细节坑,this.$parent 作为单层父子组件下的应对方式,能够快速解决当前问题,但受限于 API 本身的特性,无法适配多层组件嵌套场景。而结合实际开发情况来看,多层嵌套的场景占比偏低,该方案在常规开发中依旧有一定的实用性,也符合大部分简单业务场景的开发需求。




