Как сделать сервис реактивным в одну строку в Vue.js + Typescript

С выходом Composition API в Vue появилось новые возможности повторного использования кода. Больше нет необходимости в миксинах, компонентах высшего порядка и прочих “хаках”, если вам нужно вынести общую логику для нескольких компонентов. Но что если у вас есть нереактивный сервис, инкапсулирующий бизнес-логику, а переписывать все на composition api не хочется? 

К примеру возьмем простой класс с состоянием:

export class MyService {   foo: Object = {}    setFoo (foo: Object) {     this.foo = foo   } }

В нем все прекрасно, но что если мы хотим сделать его свойство реактивным? Согласно документации, мы можем сделать, например, вот так: 

import { Ref, ref } from 'vue'  export class MyService {   foo: Ref<Object> = ref({})    setFoo (foo: Object) {     this.foo.value = foo   } }

Вроде бы тоже неплохо, но если ваш сервис большой и иерархичный, переводить каждое свойство в ref будет трудно и больно. Тут на помощь приходят декораторы Typescript:

export class MyService {   @Reactive foo: Object = {}    setFoo (foo: Object) {     this.foo = foo   } }

Успех! Наше свойство реактивно, но теперь уже без манипуляций c ref-ами. Как это работает? Рассмотрим сам декоратор:

import { shallowRef } from 'vue'  function initRefs (target: any, key: string, value ?: any) {   target.__refs = target.__refs ?? {}    if (!target.__refs[key]) {     target.__refs[key] = shallowRef(value)   } }  export function Reactive (target: any, key: string): void {   Object.defineProperty(target, key, {     configurable: true,     enumerable: true,     get () {       initRefs(this, key)       return this.__refs[key].value     },     set (value) {       initRefs(this, value, key)       this.__refs[key].value = value     }   }) }

Благодаря Object.defineProperty, свойство попросту заменяется геттером и сеттером, обращающимся к ref-у, который хранится в том же классе. Его инициализация происходит при первом чтении либо записи.

Заключение

Декораторы — мощный инструмент, позволяющий вынести низкоуровневый код на уровень инфраструктуры и не засорять им приложение. Но какие минусы у такого подхода?

  1. Декораторы все еще являются экспериментальной функцией, и ее реализация в будущем может измениться, о чем говорится в документации. Кроме того, для их работы необходимо явно проставить параметр experimentalDecorators.

  2. Этот подход похож на магию и прячет нюансы реализации от разработчика, что может привести к неожиданным багам и непредсказуемому поведению.

  3. Это не работает с SSR из коробки, хотя это преодолимая проблема, например, с помощью Nuxt и его ssrRef

Тем не менее, лично мне такой путь нравится, он красив и элегантен и не накладывает драконовских ограничений. А как вы считаете? Комментарии и предложения к подходу охотно принимаются.


ссылка на оригинал статьи https://habr.com/ru/post/674432/

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *