在 Vue 中,this 的指向是一个常见的问题。理解 this 的绑定规则,可以避免很多难以排查的 bug。
在 Vue 实例中,this 指向 Vue 实例本身:
new Vue({
data: {
message: 'Hello'
},
created() {
console.log(this.message)
console.log(this === vm)
}
})
普通函数中的 this 由调用方式决定:
const obj = {
name: 'John',
greet() {
console.log(this.name)
}
}
obj.greet()
const fn = obj.greet
fn()
箭头函数没有自己的 this,继承外层作用域的 this:
const obj = {
name: 'John',
greet: () => {
console.log(this.name)
}
}
obj.greet()
new Vue({
data: {
message: 'Hello'
},
methods: {
fetchData() {
setTimeout(function() {
console.log(this.message)
}, 1000)
}
}
})
setTimeout 的回调是普通函数,this 指向 window。
解决方案一:使用箭头函数
methods: {
fetchData() {
setTimeout(() => {
console.log(this.message)
}, 1000)
}
}
解决方案二:保存 this 引用
methods: {
fetchData() {
const self = this
setTimeout(function() {
console.log(self.message)
}, 1000)
}
}
解决方案三:使用 bind
methods: {
fetchData() {
setTimeout(function() {
console.log(this.message)
}.bind(this), 1000)
}
}
new Vue({
methods: {
handleClick() {
console.log(this.message)
}
},
mounted() {
document.addEventListener('click', this.handleClick)
}
})
事件监听器的 this 指向触发事件的元素。
解决方案:使用 bind
mounted() {
this.handleClick = this.handleClick.bind(this)
document.addEventListener('click', this.handleClick)
},
beforeDestroy() {
document.removeEventListener('click', this.handleClick)
}
解决方案:使用箭头函数
mounted() {
this.handler = (e) => this.handleClick(e)
document.addEventListener('click', this.handler)
},
beforeDestroy() {
document.removeEventListener('click', this.handler)
}
new Vue({
data: {
numbers: [1, 2, 3]
},
methods: {
double(n) {
return n * 2
},
process() {
this.numbers.forEach(function(n) {
console.log(this.double(n))
})
}
}
})
解决方案:使用箭头函数
process() {
this.numbers.forEach(n => {
console.log(this.double(n))
})
}
解决方案:传递 this
process() {
this.numbers.forEach(function(n) {
console.log(this.double(n))
}, this)
}
new Vue({
methods: {
fetchData() {
fetch('/api/data')
.then(function(response) {
return response.json()
})
.then(function(data) {
this.data = data
})
}
}
})
解决方案:使用箭头函数
methods: {
async fetchData() {
const response = await fetch('/api/data')
this.data = await response.json()
}
}
new Vue({
data: {
message: 'Hello'
},
methods: {
greet: () => {
console.log(this.message)
}
},
computed: {
upper: () => this.message.toUpperCase()
},
created: () => {
console.log(this.message)
}
})
箭头函数没有自己的 this,会指向外层作用域。
new Vue({
data: {
message: 'Hello'
},
methods: {
greet() {
console.log(this.message)
}
},
computed: {
upper() {
return this.message.toUpperCase()
}
},
created() {
console.log(this.message)
}
})
回调函数:
methods: {
fetchData() {
fetch('/api/data')
.then(response => response.json())
.then(data => {
this.data = data
})
}
}
数组方法:
methods: {
process() {
return this.items
.filter(item => item.active)
.map(item => item.value)
}
}
定时器:
methods: {
delayedAction() {
setTimeout(() => {
this.doSomething()
}, 1000)
}
}
methods 定义:
methods: {
greet: () => console.log(this.message)
}
生命周期钩子:
created: () => {
console.log(this.message)
}
computed 定义:
computed: {
upper: () => this.message.toUpperCase()
}
watch 定义:
watch: {
message: (newVal) => {
console.log(this)
}
}
new Vue({
data: {
searchQuery: ''
},
created() {
this.debouncedSearch = debounce((query) => {
this.search(query)
}, 300)
},
methods: {
handleInput() {
this.debouncedSearch(this.searchQuery)
},
search(query) {
console.log('Searching:', query)
}
}
})
function debounce(fn, delay) {
let timer = null
return function(...args) {
clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, args)
}, delay)
}
}
const bus = new Vue()
Vue.component('listener', {
created() {
bus.$on('event', (data) => {
console.log(this.name, data)
})
},
data() {
return {
name: 'Listener'
}
}
})
new Vue({
data: {
user: null,
loading: false
},
methods: {
async fetchUser(id) {
this.loading = true
try {
const response = await fetch(`/api/users/${id}`)
this.user = await response.json()
} catch (error) {
console.error(error)
} finally {
this.loading = false
}
}
}
})
new Vue({
mounted() {
this.handleResize = () => {
this.windowWidth = window.innerWidth
}
window.addEventListener('resize', this.handleResize)
},
beforeDestroy() {
window.removeEventListener('resize', this.handleResize)
},
data() {
return {
windowWidth: window.innerWidth
}
}
})
methods: {
someMethod() {
console.log('this:', this)
console.log('this is Vue instance:', this instanceof Vue)
}
}
methods: {
someMethod() {
debugger
}
}
Vue Devtools 可以查看组件实例的 this。
new Vue({
methods: {
greet() {}
},
computed: {
foo() {}
},
watch: {
bar() {}
},
created() {}
})
methods: {
fetchData() {
fetch('/api/data')
.then(data => {
this.data = data
})
}
}
const obj = {
name: 'John',
greet() {
console.log(this.name)
}
}
methods: {
process() {
const { items, filter } = this
return items.filter(item => item.includes(filter))
}
}