Vue 如何在 data 中使用 this 绑定 methods 中的方法?
Vue 如何在 data 中使用 this 绑定 methods 中的方法?
场景
吾辈需要在 data
块中绑定 methods
的函数,有什么方法可以做到么?
<!DOCTYPE html><html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.11/lodash.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.min.js"></script>
<script>
class Table extends Vue {
constructor({ data, methods, mounted }) {
super({
data,
methods,
mounted,
})
}
}
const table = new Table({
data: {
user: {
birthday: new Date(),
birthdayFormatter: this.calcTime,
},
},
methods: {
calcTime(time) {
console.log(time, this)
},
},
})
console.log(table.$data.user.birthdayFormatter)
</script>
</body>
</html>
现在打印的结果是 undefined
,说明 this
在 data
中就已经被认为是 window
对象了
吾辈将之放到了计算属性中,仍然是没有效果的。
这个问题的本质在于如何在传值的过程中为某个作用域的 this
绑定为 vue
实例本身,而在绑定的时候 vue
实例并未完成创建
class Table extends Vue { constructor({ data, methods, mounted, computed }) {
super({
data,
methods,
mounted,
computed,
})
}
}
const table = new Table({
methods: {
calcTime(time) {
return time.toISOString()
},
},
computed: {
user: () => ({
birthday: new Date(),
birthdayFormatter: this.calcTime,
}),
},
})
console.log(table.user.birthdayFormatter)
回答:
vuejs data 属性中的 this 指向问题
场景
之前在封装 table 组件 BasicTableVue 的时候遇到的问题,在 data
属性中无法使用 this.**
调用 methods
中的函数。
例如下面的代码
class BasicTableData { constructor({
user = {
name: 'rx',
age: 17,
},
} = {}) {
this.user = user
}
}
class Table extends Vue {
constructor({ data, methods, mounted, computed }) {
super({
data: _.merge(new BasicTableData(), data),
methods,
mounted,
computed,
})
}
}
const table = new Table({
data: {
user: {
birthday: new Date(),
birthdayFormatter: this.calcTime,
},
},
methods: {
calcTime(time) {
return time.toISOString()
},
},
})
// 将输出 undefined
console.log(table.user.birthdayFormatter)
吾辈尝试了一下原生的 vuejs,发现这样的 data 仍然不能用。
解决
后来在官方文档找到了 这里,data 如果是一个对象或者箭头函数时,不会绑定 this
,仅当 data
是一个普通函数(使用 function
声明)时,才会被绑定 this
。
那么,知道了原因,解决方案就很简单了。
- 如果需要使用在
data
中使用this
调用methods
中的函数,则data
必须声明为普通函数 - 如果需要默认
data
defaultData
,则Table
可以将合并后的data
声明为函数,并将defaultData
与data
(使用Table
创建实例时传入的)的返回值合并
修改后的代码如下
class BasicTableData { constructor({
user = {
name: 'rx',
age: 17,
},
} = {}) {
this.user = user
}
}
class Table extends Vue {
constructor({ data, methods, mounted, computed }) {
super({
// 关键是这里将 data 声明为普通函数
data() {
// 此处为了简洁使用 lodash 的深度合并
return _.merge(
new BasicTableData(),
// 此处判断 data 是否为函数,是的话就绑定 this 计算结果
typeof data === 'function' ? data.call(this) : data,
)
},
methods,
mounted,
computed,
})
}
}
const table = new Table({
data: function() {
return {
user: {
birthday: new Date(),
birthdayFormatter: this.calcTime,
},
}
},
methods: {
calcTime(time) {
return time.toISOString()
},
},
})
// 打印的结果是
// ƒ calcTime(time) {
// return time.toISOString()
// }
console.log(table.user.birthdayFormatter)
思考
现在问题解决了,那么,为什么 vuejs
就能够在传入 data
函数时就能调用 methods
中的函数了呢?吾辈稍微 debug 进入源码看了一下
- 创建
Table
进入构造函数 - 因为继承了 Vue,所以进入 Vue 的构造函数中
- 因为当前实例属于 Vue,所以进入
_init
进行初始化 - 跳转到
initState(vm);
处,该函数将对 data 属性进行初始化(至于为什么是 state 可能是因为最初就是模仿 react 写的?) - 进入到
initState()
,跳转到initData(vm);
处 进入到
initData()
函数,看到了判断逻辑var data = vm.$options.data
data = vm._data = typeof data === 'function' ? getData(data, vm) : data || {}
注意看,这里的 vue 内部判断了 data 是否为函数,如果是就去
getData(data, vm)
进入
getData()
函数看看,发现了关键代码return data.call(vm, vm)
是的,data 调用时使用
call
绑定this
为 vm,而此时vm.calcTime
已经有值了。那么,
vm.calcTime
是什么时候被初始化的呢?
其实也在initState
函数中,可以看到,vue 的初始化顺序是props
: 外部传递的属性methods
: 组件的函数data
: 组件的属性computed
: 计算属性watch
: 监听函数
总结
相比于 react,vue 做了更多的 黑魔法 呢!就像 this 指向问题,react 是交由用户自行解决的,而 vue 则在后面偷偷的为函数绑定 this 为 vue 实例本身。
回答:
使用计算属性
computed: { user() {
return {
birthday: new Date(),
birthdayFormatter: this.calcTime
}
}
}
你那个time都没传,你打印它不是undefined才怪
以上是 Vue 如何在 data 中使用 this 绑定 methods 中的方法? 的全部内容, 来源链接: utcz.com/a/149935.html