在利用 vue
开发一个组件时,可能需要它能够实时响应变化的值,这个时候我们就需要用到
v-model
。
然而在实际使用中,如果有两个相互影响的变量要进行实时变化时,就可能导致无限循环,提升编码难度。
本文给出了一个 v-model
的最佳实践,可以参考使用。
需求示例
假设我们需要编写一个部门用户选择组件,它能够同时返回选择的部门和用户,同时在初始化时,可以回显到界面。
下面大致分析一下可能导致的问题:
如果用户数据改变后,则返回的部门也需要触发变化,而返回的部门改变了,又会去触发用户改变,这样就构成了一个无限循环。
要如何解决这个问题呢?继续向下阅读。
实现代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| <template> <div> <DepartmentTree v-model="_departmentIds">部门树</DepartmentTree> <UsersTable v-model="_users">用户列表</UsersTable> </div> </template>
<script> import DepartmentTree from './departmentTree.vue' import UsersTable from './usersTable.vue'
export default { components: { DepartmentTree, UsersTable }, props: { departmentIds: { type: Array, default: () => [] }, users: { type: Array, default: () => [] } },
data() { return { _departmentIds: this.users, _users: this.departmentIds } },
computed: { usersCp() { return JSON.parse(JSON.stringify(this.users)) } },
watch: { departmentIds(newValues) { if (newValues.lenght === this._departmentIds.length) return
this.usersChanged()
},
usersCp(newValues, oldValues) { if (this._users.length === newValues.lenght) return
this._departmentIds = Array.from(new Set(this.users.map(x => x.departmentId)))
this.departmentIdsChanged() } },
methods: { usersChanged() { this.$emit('update:users', this._users) },
departmentIdsChanged() { this.$emit('update:departmentIds', this._departmentIds) } } } </script>
<style> </style>
|
上述代码的主要思想如下:
- 将外部变化与内部变化分开处理,外部变化处理通过 watch props
来实现,内部变化通过内部方法来实现
- 变化完成后,再向外传递变化结果
- 每次外部变化影响到内部时,都要进行验证,防止循环
watch 数组时新旧值相同处理
如果被 watch
的字段是一个数组时,它的新旧值都是一样的,因为引用相同,为了得到不一样的值,可以增加一个计算属性来进行过渡。
如上例中的 usersCp
。
参考
自定义组件的
v-model