为什么内部值会对整个 ref 的变化做出反应?
Why does an internal value react to changes from the whole ref?
我有一个对象...
export class Note {
type = ''
id: string = v4()
creationDate: string = DateTime.now().toISO()
modificationDate: string = DateTime.now().toISO()
text = ''
title = ''
tags: string[] = []
deleted = false
deletedAt?: string = ''
erased = false
erasedAt?: string = ''
archived = false
archivedAt?: string = ''
deleteOn?: string = ''
pinned = false
// methods follow
}
... 我将其作为道具传递给组件
<script lang='ts' setup>
const props = defineProps({
currentNote: {type: Object, default: () => {return {}}},
})
const note = toRef(props, 'currentNote') as Ref<Note>
const toWatch = computed(() => [toRef(note, 'title'), toRef(note, 'text'), toRef(note, 'tags'), toRef(note, 'type')])
watch(toWatch, debounce(() => {console.log(JSON.stringify(note)); note.value.writeToOffline()}, 1000), {deep: true})
(...)
但是,当 watch
回调 每当 note
的任何 属性 发生变化时 .
上面的构造只是为了观察选定的属性,但显然它们进行了任何更改。
我的代码逻辑有什么问题?
不可重现。查看下面的演示...
- 对
archived
的更改不会触发观察器
- 对
title
的更改 也不会触发 观察器 - 请注意 toWatch
中的代码已计算 - 代码与示例中的代码相同:Vue.toRef(note, 'title')
- 对
text
的更改会触发观察器 - 要解决上述问题,您需要使用 Vue.toRef(note.value, 'text')
例如。将 ref 值而不是 ref 本身传递给 toRefs
所有这一切在没有 {deep: true}
的情况下都有效,因为 watch
可以观察一个引用数组(而且 ref
似乎也包含 refs
数组)
class Note {
constructor() {
this.type = '';
this.id = 'asdsad';
this.text = 'text';
this.title = 'title';
this.tags = [];
this.deleted = false;
this.deletedAt = '';
this.erased = false;
this.erasedAt = '';
this.archived = false;
this.archivedAt = '';
this.deleteOn = '';
this.pinned = false;
}
}
const app = Vue.createApp({
setup(){
const note = Vue.reactive(new Note())
const modifyArchived = function() {
note.archived = !note.archived
}
const modifyTitle = function() {
note.title = note.title + '+'
}
const modifyText = function() {
note.text = note.text + '+'
}
return {
note,
modifyArchived,
modifyTitle,
modifyText
}
},
})
app.component('child', {
props: ['currentNote'],
setup(props) {
const note = Vue.toRef(props, 'currentNote')
const changes = Vue.ref(0)
const toWatch = Vue.computed(() => [Vue.toRef(note, 'title'), Vue.toRef(note.value, 'text'), Vue.toRef(note, 'tags'), Vue.toRef(note, 'type')])
Vue.watch(toWatch, () => { changes.value++ } /*, {deep: true} */)
return {
note,
changes
}
},
template: `
<div>Child: (changes = {{ changes }})</div>
<div>archived: {{ note.archived }}</div>
<div>title: {{ note.title }}</div>
<div>text: {{ note.text }}</div>
`
})
app.mount('#app')
<script src="https://unpkg.com/vue@3.2.23/dist/vue.global.js"></script>
<div id='app'>
<div>Parent:</div>
<div><button @click="modifyArchived">Modify archived (no trigger)</button></div>
<div><button @click="modifyTitle">Modify title (no trigger - not ok!)</button></div>
<div><button @click="modifyText">Modify text (trigger - ok!)</button></div>
<child :current-note="note"></child>
</div>
我有一个对象...
export class Note {
type = ''
id: string = v4()
creationDate: string = DateTime.now().toISO()
modificationDate: string = DateTime.now().toISO()
text = ''
title = ''
tags: string[] = []
deleted = false
deletedAt?: string = ''
erased = false
erasedAt?: string = ''
archived = false
archivedAt?: string = ''
deleteOn?: string = ''
pinned = false
// methods follow
}
... 我将其作为道具传递给组件
<script lang='ts' setup>
const props = defineProps({
currentNote: {type: Object, default: () => {return {}}},
})
const note = toRef(props, 'currentNote') as Ref<Note>
const toWatch = computed(() => [toRef(note, 'title'), toRef(note, 'text'), toRef(note, 'tags'), toRef(note, 'type')])
watch(toWatch, debounce(() => {console.log(JSON.stringify(note)); note.value.writeToOffline()}, 1000), {deep: true})
(...)
但是,当 watch
回调 每当 note
的任何 属性 发生变化时 .
上面的构造只是为了观察选定的属性,但显然它们进行了任何更改。
我的代码逻辑有什么问题?
不可重现。查看下面的演示...
- 对
archived
的更改不会触发观察器 - 对
title
的更改 也不会触发 观察器 - 请注意toWatch
中的代码已计算 - 代码与示例中的代码相同:Vue.toRef(note, 'title')
- 对
text
的更改会触发观察器 - 要解决上述问题,您需要使用Vue.toRef(note.value, 'text')
例如。将 ref 值而不是 ref 本身传递给toRefs
所有这一切在没有 {deep: true}
的情况下都有效,因为 watch
可以观察一个引用数组(而且 ref
似乎也包含 refs
数组)
class Note {
constructor() {
this.type = '';
this.id = 'asdsad';
this.text = 'text';
this.title = 'title';
this.tags = [];
this.deleted = false;
this.deletedAt = '';
this.erased = false;
this.erasedAt = '';
this.archived = false;
this.archivedAt = '';
this.deleteOn = '';
this.pinned = false;
}
}
const app = Vue.createApp({
setup(){
const note = Vue.reactive(new Note())
const modifyArchived = function() {
note.archived = !note.archived
}
const modifyTitle = function() {
note.title = note.title + '+'
}
const modifyText = function() {
note.text = note.text + '+'
}
return {
note,
modifyArchived,
modifyTitle,
modifyText
}
},
})
app.component('child', {
props: ['currentNote'],
setup(props) {
const note = Vue.toRef(props, 'currentNote')
const changes = Vue.ref(0)
const toWatch = Vue.computed(() => [Vue.toRef(note, 'title'), Vue.toRef(note.value, 'text'), Vue.toRef(note, 'tags'), Vue.toRef(note, 'type')])
Vue.watch(toWatch, () => { changes.value++ } /*, {deep: true} */)
return {
note,
changes
}
},
template: `
<div>Child: (changes = {{ changes }})</div>
<div>archived: {{ note.archived }}</div>
<div>title: {{ note.title }}</div>
<div>text: {{ note.text }}</div>
`
})
app.mount('#app')
<script src="https://unpkg.com/vue@3.2.23/dist/vue.global.js"></script>
<div id='app'>
<div>Parent:</div>
<div><button @click="modifyArchived">Modify archived (no trigger)</button></div>
<div><button @click="modifyTitle">Modify title (no trigger - not ok!)</button></div>
<div><button @click="modifyText">Modify text (trigger - ok!)</button></div>
<child :current-note="note"></child>
</div>