vuex + typecript = vuexok. الدراجة التي ركبت وتجاوزت الجميع

يوم جيد.



مثل العديد من المطورين ، أكتب مشروعي  الصغير نسبيًا في أوقات فراغي  . اعتدت أن أكتب رد فعل ، لكن في العمل أستخدم vue. حسنًا ، من أجل الضخ في vue ، بدأت في رؤية مشروعي عليه. في البداية كان كل شيء على ما يرام ، ورديًا تمامًا ، حتى قررت أنني ما زلت بحاجة إلى تحسين في الكتابة المطبوعة. هذه هي الطريقة التي ظهرت بها الكتابة المطبوعة في مشروعي. وإذا كانت جميع المكونات  جيدة ، فسيكون كل شيء محزنًا. لذلك اضطررت إلى المرور بجميع المراحل الخمس لقبول المشكلة ، حسنًا ، كل شيء تقريبًا.



النفي



المتطلبات الأساسية لمتجر:



  1. يجب أن تعمل أنواع الكتابة المطبوعة في الوحدات النمطية
  2. يجب أن تكون الوحدات النمطية سهلة الاستخدام في المكونات ، وأنواع الحالات ، ويجب أن تعمل الإجراءات والطفرات والأرقام
  3. لا تبتكر واجهة برمجة تطبيقات جديدة لـ vuex ، فأنت بحاجة إلى التأكد من أن أنواع الكتابة المطبوعة تعمل بطريقة ما مع وحدات vuex بحيث لا تضطر إلى إعادة كتابة التطبيق بأكمله مرة واحدة
  4. يجب أن تكون استدعاء الطفرات والإجراءات بسيطة ومباشرة قدر الإمكان
  5. يجب أن تكون العبوة صغيرة قدر الإمكان
  6. لا أريد تخزين الثوابت بأسماء الطفرات والأفعال
  7. يجب أن تعمل (وماذا عنها بدونها)


لا يمكن أن يكون هذا المشروع الناضج مثل vuex لم يكن لديه دعم مطبوع عادي. حسنًا ،   فتحنا Google Yandex وقدنا. كنت متأكدًا بنسبة 100500٪ من أن كل شيء يجب أن يكون على ما يرام مع الكتابة المطبوعة (كم كنت مخطئًا). هناك الكثير من المحاولات المختلفة لتكوين صداقات vuex و type. سأقدم بعض الأمثلة التي أتذكرها ، بدون الكود حتى لا ينفخ المقال. كل شيء موجود في الوثائق الموجودة على الروابط أدناه.



vuex الذكية وحدة



github.com/ktsn/vuex-smart-module

جيد، جيد جدا. كل شيء معي ، لكنني شخصياً لم يعجبني حقيقة أنه بالنسبة للأفعال ، والطفرات ، والحالات ، والحاصل ، تحتاج إلى إنشاء فئات منفصلة. هذا ، بالطبع ، هو الذوق ، لكن هذا أنا ومشروعي) وبشكل عام ، لم يتم حل مشكلة الكتابة بشكل كامل ( موضوع التعليق مع شرح السبب ).



Vuex Typescript Support محاولة



لطيفة ، لكن الكثير من إعادة الكتابة ، ولم يقبلها المجتمع عمومًا.



vuex-module-decorators



بدا هذا وكأنه الطريقة المثلى لتكوين صداقات vuex و type. يبدو أنه مصمم vue-property-decorator الذي أستخدمه في التطوير ، يمكنك العمل مع الوحدة كما هو الحال مع الفصل ، بشكل عام ، ممتاز ، لكن ...



لكن لا يوجد ميراث. لا يتم توريث فئات الوحدات النمطية بشكل صحيح وكانت المشكلة معلقة بهذه المشكلة لفترة طويلة جدًا! وبدون الميراث ، سيكون هناك الكثير من تكرار الكود. فطيرة…



الغضب



ثم لم يكن الأمر كثيرًا ، حسنًا ، أو ± هو نفسه - لا يوجد حل مثالي. هذه هي اللحظة التي تقول فيها لنفسك: لماذا بدأت في كتابة مشروع في vue؟ حسنًا ، كما تعلم ، رد فعل ، حسنًا ، سأكتب على رد الفعل ، لن تكون هناك مثل هذه المشاكل! في العمل الرئيسي ، المشروع جاهز وتحتاج إلى ترقيته - اضغط على الجدل. هل يستحق أن تقضي الأعصاب والليالي الطوال؟ اجلس مثل أي شخص آخر ، اكتب komponentiki ، لا ، أنت بحاجة أكثر من أي شيء آخر! ارمي هذا النظرة! اكتب لتتفاعل ، وقم بالترقية فيه ، وادفع أكثر مقابل ذلك!



في تلك اللحظة ، كنت على استعداد لكراهية vue لا مثيل لها ، لكنها كانت عاطفة ، والذكاء كان لا يزال أعلى من ذلك. تتمتع Vue (في رأيي الشخصي) بالعديد من المزايا على رد الفعل ، لكن لا يوجد كمال ، وكذلك الفائزون في ساحة المعركة. يعتبر كل من vue و response جيدًا بطريقتهما الخاصة ، وبما أن جزءًا كبيرًا من المشروع مكتوب بالفعل في vue ، فسيكون من الغباء قدر الإمكان التبديل إلى الرد الآن. كان علي أن أقرر ما أفعله مع vuex.



صفقة



حسنًا ، الأمور لا تسير على ما يرام. ربما بعد ذلك vuex-smart-module؟ تبدو هذه الحزمة جيدة ، نعم ، عليك إنشاء الكثير من الفصول الدراسية ، لكنها تعمل بشكل رائع. أو ربما يمكنه محاولة كتابة أنواع للطفرات والأفعال يدويًا في المكونات واستخدام vuex الخالص؟ هناك ، vue3 مع vuex4 في الطريق ، ربما يكون أداءهم أفضل مع الكتابة المطبوعة. لذلك دعونا نجرب الدعامة النقية. بشكل عام ، هذا لا يؤثر على عمل المشروع ، فهو لا يزال يعمل ، لا توجد أنواع ، لكنك تنتظر. وانتظر)



في البداية بدأت في القيام بذلك ، لكن تبين أن الشفرة وحشية ...



كآبة



كان علي المضي قدما. لكن أين غير معروف. لقد كانت خطوة يائسة تمامًا. قررت أن أصنع  حاوية دولة من الصفر . تمت صياغة القانون في غضون ساعتين. واتضح حتى بشكل جيد. أنواع العمل ، والحالة هي رد الفعل ، وحتى الميراث موجود. ولكن سرعان ما بدأ اليأس ينحسر ، وبدأ الحس السليم في العودة. بشكل عام ، ذهبت هذه الفكرة إلى سلة المهملات. بشكل عام ، كان هذا هو نمط ناقل الحدث العالمي. وهي جيدة فقط للتطبيقات الصغيرة. وبشكل عام ، لا تزال كتابة vuex الخاصة بك مبالغة (على الأقل في وضعي). ثم خمنت بالفعل أنني مرهق تمامًا. لكن كان الوقت قد فات للتراجع.



ولكن إذا كان أي شخص مهتمًا ، فسيكون الرمز هنا: (ربما تمت إضافة هذا الجزء عبثًا ، لكن المسار سيكون)



لا تبدو عصبي
const getModule = <T>(name:string, module:T) => {
  const $$state = {}
  const computed: Record<string, () => any> = {}

  Object.keys(module).forEach(key => {
    const descriptor = Object.getOwnPropertyDescriptor(
      module,
      key,
    );

    if (!descriptor) {
      return
    }

    if (descriptor.get) {
      const get = descriptor.get

      computed[key] = () => {
        return get.call(module)
      }
    } else if (typeof descriptor.value === 'function') {
      // @ts-ignore
      module[key] = module[key].bind(module)
    } else {
      // @ts-ignore
      $$state[key] = module[key]
    }
  })


  const _vm = new Vue({
    data: {
      $$state,
    },
    computed
  })

  Object.keys(computed).forEach((computedName) => {
    var propDescription = Object.getOwnPropertyDescriptor(_vm, computedName);
    if (!propDescription) {
      throw new Error()
    }

    propDescription.enumerable = true
    Object.defineProperty(module, computedName, {
      get() { return _vm[computedName as keyof typeof _vm]},
      // @ts-ignore
      set(val) { _vm[computedName] = val}
    })
  })

  Object.keys($$state).forEach(name => {
    var propDescription = Object.getOwnPropertyDescriptor($$state,name);
    if (!propDescription) {
      throw new Error()
    }
    Object.defineProperty(module, name, propDescription)
  })

  return module
}

function createModule<
  S extends {[key:string]: any},
  M,
  P extends Chain<M, S>
>(state:S, name:string, payload:P) {
  Object.getOwnPropertyNames(payload).forEach(function(prop) {
    const descriptor = Object.getOwnPropertyDescriptor(payload, prop)

    if (!descriptor) {
      throw new Error()
    }

    Object.defineProperty(
      state,
      prop,
      descriptor,
    );
  });

  const module = state as S & P

  return {
    module,
    getModule() {
      return getModule(name, module)
    },
    extends<E>(payload:Chain<E, typeof module>) {
      return createModule(module, name, payload)
    }
  }
}

export default function SimpleStore<T>(name:string, payload:T) {
  return createModule({}, name, payload)
}

type NonUndefined<A> = A extends undefined ? never : A;

type Chain<T extends {[key:string]: any}, THIS extends {[key:string]: any}> = {
  [K in keyof T]: (
    NonUndefined<T[K]> extends Function 
      ? (this:THIS & T, ...p:Parameters<T[K]>) => ReturnType<T[K]>
      : T[K]
  )
}




التبني  ولادة الدراجة التي تجاوزت الجميع. vuexok



بالنسبة للصبر ، الرمز موجود هنا ، الوثائق القصيرة هنا .



في النهاية ، كتبت مكتبة صغيرة تغطي كل قائمة الرغبات وحتى أكثر بقليل مما هو مطلوب منها. لكن أول الأشياء أولاً.



تبدو أبسط وحدة vuexok كما يلي:



import { createModule } from 'vuexok'
import store from '@/store'

export const counterModule = createModule(store, 'counterModule', {
  state: {
    count: 0,
  },
  actions: {
    async increment() {
      counterModule.mutations.plus(1)
    },
  },
  mutations: {
    plus(state, payload:number) {
      state.count += payload
    },
    setNumber(state, payload:number) {
      state.count = payload
    },
  },
  getters: {
    x2(state) {
      return state.count * 2
    },
  },
})


حسنًا نوعًا ما مثل vuex ، على الرغم من ... ما هو موجود في السطر 10؟



counterModule.mutations.plus(1)


قف! هل هو قانوني؟ حسنًا ، مع vuexok - نعم ، قانونيًا) تُرجع طريقة createModule كائنًا يكرر تمامًا بنية كائن الوحدة النمطية vuex ، فقط بدون الخاصية ذات مساحة الاسم ، ويمكننا استخدامها لاستدعاء الطفرات والإجراءات أو للحصول على الحالة والأرقام ، يتم الاحتفاظ بجميع الأنواع. ومن اي مكان يمكن استيراده فيه.



ماذا عن المكونات؟



ومعهم كل شيء على ما يرام ، لأنه في الواقع هو vuex ، إذن ، من حيث المبدأ ، لم يتغير شيء ، أو يلتزم ، أو يرسل ، أو mapState ، إلخ. العمل كما كان من قبل.



لكن الآن يمكنك جعل الأنواع من الوحدة تعمل في المكونات:



import Vue from 'vue'
import { counterModule } from '@/store/modules/counterModule'
import Component from 'vue-class-component'

@Component({
  template: '<div>{{ count }}</div>'
})
export default class MyComponent extends Vue {
  private get count() {
    return counterModule.state.count // type number
  }
}


تكون خاصية الحالة في الوحدة النمطية تفاعلية تمامًا كما هو الحال في store.state ، لذلك لاستخدام حالة الوحدة النمطية في مكونات Vue ، تحتاج فقط إلى إرجاع جزء من حالة الوحدة النمطية في خاصية محسوبة. لا يوجد سوى تحذير واحد. لقد جعلت الحالة للقراءة فقط نوعًا ما ، فليس من الجيد تغيير حالة vuex بذلك.



استدعاء الإجراءات والطفرات أمر سهل في الخزي كما يتم حفظ أنواع معلمات الإدخال



 private async doSomething() {
   counterModule.mutations.setNumber(10)
   //   this.$store.commit('counterModule/setNumber', 10)
   await counterModule.actions.increment()
   //   await this.$store.dispatch('counterModule/increment')
 }


هنا مثل هذا الجمال. بعد ذلك بقليل ، احتجت أيضًا إلى الرد على التغيير في jwt ، والذي يتم تخزينه أيضًا في المتجر. ثم ظهرت طريقة المشاهدة في وحدات. تعمل مراقبات الوحدة النمطية بنفس طريقة عمل store.watch. الاختلاف الوحيد هو أن الحالة وحسابات الوحدة النمطية يتم تمريرها كمعلمات لوظيفة getter.



const unwatch = jwtModule.watch(
  (state) => state.jwt,
  (jwt) => console.log(`New token: ${jwt}`),
  { immediate: true },
)


إذن ما لدينا:



  1. الجانب المكتوب - نعم
  2. أنواع تعمل في المكونات - نعم
  3. API كما هو الحال في vuex ولا ينكسر كل شيء كان من قبل على vuex النقي - هو
  4. العمل التصريحي مع الجانب - نعم
  5. حجم الحزمة الصغيرة (~ 400 بايت gzip) - نعم
  6. لا حاجة لتخزين أسماء الأفعال والطفرات في الثوابت - هناك
  7. يجب أن يعمل - هو


بشكل عام ، من الغريب أن هذه الميزة الرائعة غير متوفرة في vuex خارج الصندوق ، إنه لأمر رائع مدى ملاءمتها!

بالنسبة إلى دعم vuex4 و vue3 - لم أختبره ، ولكن وفقًا للمستندات ، يجب أن يكون متوافقًا.



يتم أيضًا حل المشكلات الواردة في هذه المقالات:



Vuex - حل نزاع قديم بطرق جديدة

Vuex break encapsulation



أحلام مبللة:



سيكون من الرائع القيام بذلك بحيث تتوفر الطفرات والإجراءات الأخرى في سياق الإجراءات.



كيفية القيام بذلك في سياق الأنواع المطبوعة - يعرفها ديك. ولكن إذا كان بإمكانك القيام بذلك:



{
  actions: {
    one(injectee) {
       injectee.actions.two()
    },
    two() {
      console.log('tada!')
    }
}


أن فرحتي لن يكون لها حدود. لكن الحياة ، مثل الكتابة المطبوعة ، هي شيء قاسٍ.



ها هي المغامرة مع vuex والطباعة. حسنًا ، لقد تحدثت نوعًا ما. شكرا للاهتمام.



All Articles