Vue 3.0 Как назначить опору референсу без изменения опоры

Я отправляю из родительского компонента опору: user. Теперь в дочернем компоненте я хочу сделать его копию без изменения значения свойства.

Я пробовал сделать это так:

export default defineComponent({
  props: {
    apiUser: {
      required: true,
      type: Object
    }
  },
  setup(props) {
    const user = ref(props.apiUser);

    return { user };
  }
});

Но затем, если я изменю значение объекта пользователя, это также изменит опору apiUser. Я думал, что, возможно, использование Object.assign будет работать, но тогда ref больше не реагирует.

В Vue 2.0 я бы сделал это так:

export default {
  props: {
     apiUser: {
       required: true,
       type: Object
     }
  },
  data() {
    return {
      user: {}
    }
  },
  mounted() {
    this.user = this.apiUser;
    // Now I can use this.user without changing this.apiUser's value.
  }
};

Кредиты @butttons за комментарий, который привел к ответу.

const user = reactive({ ...props.apiUser });


person Ezrab_    schedule 25.08.2020    source источник
comment
Ну нет, это не будет реактивным, но не могли бы вы Object.assign () / JSON.parse (JSON.stringify ()) / lodash cloneDeep () добавить ссылку на данные, внести изменения в это и затем $ отправить обратно изменения в родительский объект, где вы должны реактивно объединить изменения либо слиянием, либо $ set, а затем оттолкнуть его обратно в качестве опоры? Я понимаю, что вы используете новый api композиции, и я ни в коем случае не эксперт по Vue 3, но, по крайней мере, так я всегда делал с Vue 2 для больших приложений.   -  person Abarth    schedule 25.08.2020
comment
@Abarth, правда? Это кажется неэффективным способом сделать это. Я надеюсь, что кто-то знает более эффективный способ сделать это. В любом случае спасибо!   -  person Ezrab_    schedule 25.08.2020
comment
Если вы хотите сделать копию опоры, вам нужно использовать Object.assign или что-то подобное. Настоящий вопрос в том, что именно вы хотите сделать? Зачем вам нужна копия реквизита?   -  person Beyers    schedule 25.08.2020
comment
В вашем первом фрагменте кода return user должен находиться внутри функции setup(). И вы можете деструктурировать входящую apiUser опору, назначив ее user, например: const user = ref(...props.apiUser); Теперь apiUser и user - это две разные копии.   -  person butttons    schedule 25.08.2020
comment
Я, конечно, с радостью узнаю о лучших способах. Я добавлю свой метод ниже в качестве ответа, очевидно, проигнорирую его, если окажется, что это не ответ, но тогда, по крайней мере, я чувствую, что объяснил себя.   -  person Abarth    schedule 25.08.2020
comment
@butttons, я думаю, вы имели в виду ref([...props.apiUser]);? В противном случае машинописный текст выдает ошибку, что он ожидал 1 или более аргументов, но не получил ни одного. Когда я использую его так, как написал, он генерирует такую ​​ошибку: Type '{ [key: string]: any; }' must have a '[Symbol.iterator]()' method that returns an iterator.   -  person Ezrab_    schedule 25.08.2020
comment
Ах, моя беда. Для значений объекта рекомендуется использовать reactive. Совершенно уверен, что это не вызовет ошибку типа. Попробуйте использовать const user = reactive(...props.apiUser);. Вот когда использовать ref vs reactive.   -  person butttons    schedule 25.08.2020
comment
@butttons почти все понял! Это работало следующим образом: const user = reactive({ ...props.apiUser });   -  person Ezrab_    schedule 25.08.2020
comment
Милая! Вы можете отредактировать свой вопрос, указав решение, которое найдут другие :)   -  person butttons    schedule 25.08.2020


Ответы (2)


Как обсуждалось в разделе комментариев, метод Vue 2, который мне лично нравится в этих случаях, заключается в следующем: он в основном выполняет двусторонний обход при обновлении модели.

Родитель (apiUser) - ›
Дочерний (клонировать apiUser пользователю, вносить изменения, выдавать) -›
Родитель (устанавливать изменения реактивно) - ›
Дочерний (автоматически получает изменения и создает новый клон)

Родитель

<template>
   <div class="parent-root"
      <child :apiUser="apiUser" @setUserData="setUserData" />
   </div>
</template>

// ----------------------------------------------------
// (Obviously imports of child component etc.)
export default {
   data() {
      apiUser: {
         id: 'e134',
         age: 27
      }
   },

   methods: {
      setUserData(payload) {
         this.$set(this.apiUser, 'age', payload);
      }
   }
}

Ребенок

<template>
   <div class="child-root"
      {{ apiUser }}
   </div>
</template>

// ----------------------------------------------------
// (Obviously imports of components etc.)
export default {
   props: {
      apiUser: {
         required: true,
         type: Object
      }
   },

   data() {
      user: null
   },

   watch: {
      apiUser: {
         deep: true,
         handler() {
            // Whatever clone method you want to use
            this.user = cloneDeep(this.apiUser);
         }
      }
   },

   mounted() {
      // Whatever clone method you want to use
      this.user = cloneDeep(this.apiUser);
   },

   methods: {
      // Whatever function catching the changes you want to do
      setUserData(payload) {
         this.$emit('setUserData', this.user);
      }
   }
}

Приносим извинения за промахи

person Abarth    schedule 25.08.2020

props: {
 apiUser: {
   required: true,
   type: Object
 }
},
setup(props) {
   const userCopy = toRef(props, 'apiUser')
}

Вместе с API композиции у нас есть API toRef, который позволяет создавать копия с любого source reactive object. Поскольку объект props - это reactive, вы используете toRef(), и он не изменит вашу опору.

person Julian    schedule 03.06.2021