【Web前端问题】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,说明 thisdata 中就已经被认为是 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

那么,知道了原因,解决方案就很简单了。

  1. 如果需要使用在 data 中使用 this 调用 methods 中的函数,则 data 必须声明为普通函数
  2. 如果需要默认 datadefaultData,则 Table 可以将合并后的 data 声明为函数,并将 defaultDatadata(使用 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 进入源码看了一下

  1. 创建 Table 进入构造函数
    构造函数

  2. 因为继承了 Vue,所以进入 Vue 的构造函数中
    进入 Vue 的构造函数中

  3. 因为当前实例属于 Vue,所以进入 _init 进行初始化
    进入 _init 初始化

  4. 跳转到 initState(vm); 处,该函数将对 data 属性进行初始化(至于为什么是 state 可能是因为最初就是模仿 react 写的?)
    跳转到 initState()

  5. 进入到 initState(),跳转到 initData(vm);
    initData(vm) 处

  6. 进入到 initData() 函数,看到了判断逻辑
    判断逻辑

    var data = vm.$options.data

    data = vm._data = typeof data === 'function' ? getData(data, vm) : data || {}

    注意看,这里的 vue 内部判断了 data 是否为函数,如果是就去 getData(data, vm)

  7. 进入 getData() 函数看看,发现了关键代码
    关键代码

    return data.call(vm, vm)

    是的,data 调用时使用 call 绑定 this 为 vm,而此时 vm.calcTime 已经有值了。

  8. 那么,vm.calcTime 是什么时候被初始化的呢?
    其实也在 initState 函数中,可以看到,vue 的初始化顺序是

    1. props: 外部传递的属性

    2. methods: 组件的函数

    3. data: 组件的属性

    4. computed: 计算属性

    5. watch: 监听函数

初始化顺序

总结

相比于 react,vue 做了更多的 黑魔法 呢!就像 this 指向问题,react 是交由用户自行解决的,而 vue 则在后面偷偷的为函数绑定 this 为 vue 实例本身。

回答:

使用计算属性

computed: {

user() {

return {

birthday: new Date(),

birthdayFormatter: this.calcTime

}

}

}

你那个time都没传,你打印它不是undefined才怪

以上是 【Web前端问题】Vue 如何在 data 中使用 this 绑定 methods 中的方法? 的全部内容, 来源链接: utcz.com/a/142674.html

回到顶部