为什么内部值会对整个 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 的任何 属性 发生变化时 .

上面的构造只是为了观察选定的属性,但显然它们进行了任何更改。

我的代码逻辑有什么问题?

不可重现。查看下面的演示...

  1. archived 的更改不会触发观察器
  2. title 的更改 也不会触发 观察器 - 请注意 toWatch 中的代码已计算 - 代码与示例中的代码相同:Vue.toRef(note, 'title')
  3. 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>