
inb4: هذا ليس آخر "إعداد" مشروع جديد مع البرنامج التعليمي Vue و TypeScript. دعونا نفعل بعض الغوص العميق في مواضيع أكثر تعقيدا!
typescript
هو رائع. Vue
رائع. لا شك أن الكثير من الناس يحاولون تجميعهم معًا . ولكن ، لأسباب مختلفة ، من الصعب حقًا كتابة تطبيق Vue
. دعنا نتعرف على المشكلات وما الذي يمكن عمله لحلها (أو على الأقل تقليل التأثير).
TLDR
لدينا هذا القالب الرائع مع Nuxt
و Vue
و Vuex
و jest
المكتوب بالكامل. مجرد تثبيته وسيتم تغطية كل شيء بالنسبة لك. انتقل إلى المستندات لمعرفة المزيد.
وكما قلت ، لن أرشدك خلال الإعداد الأساسي لثلاثة أسباب:
- هناك الكثير من الدروس الموجودة حول هذا الموضوع
- هناك الكثير من الأدوات للبدء بنقرة واحدة مثل
Nuxt
و vue-cli
Nuxt
مع البرنامج المساعد typescript
- لدينا بالفعل
wemake-vue-template
حيث يتم بالفعل تغطية كل جزء من الإعداد الذي سأتحدث عنه
كتابة المكونات
أول توقّع عند بدء العمل باستخدام Vue
و typescript
وبعد كتابة مكونات فصلك بالفعل هو أن علامات <template>
و <style>
لا تزال غير مكتوبة. دعني أريك مثالاً:
<template> <h1 :class="$style.headr"> Hello, {{ usr }}! </h1> </template> <script lang="ts"> import Vue from 'vue' import Component from 'vue-class-component' import { Prop } from 'vue-property-decorator' @Component({}) export default class HelloComponent extends Vue { @Prop() user!: string } </script> <style module> .header { /* ... */ } </style>
لقد قمت بعمل خطأين هنا: {{ usr }}
بدلاً من {{ user }}
و $style.headr
بدلاً من $style.header
. هل typescript
من هذه الأخطاء؟ كلا ، لن.
ما الذي يمكن عمله لإصلاحه؟ حسنا ، هناك العديد من الخارقة.
كتابة القالب
يمكن للمرء استخدام Vetur
مع خيار vetur.experimental.templateInterpolationService
لكتابة القوالب الخاصة بك. نعم ، هذه مجرد عملية تدقيق تعتمد على المحرر ولا يمكن استخدامها داخل CI حتى الآن. ولكن ، فريق Vetur
يعمل بجد لتوفير CLI للسماح بذلك. تتبع القضية الأصلية في حال كنت مهتما.

الخيار الثاني هو اثنين من اختبارات لقطة الكتابة مع jest
. وسوف يمسك الكثير من الأخطاء المستندة إلى القالب. وهي رخيصة جدا في الصيانة.
لذلك ، يوفر لك الجمع بين هاتين الأداتين تجربة مطور لطيفة مع ردود فعل سريعة وطريقة موثوقة للوقوف على الأخطاء داخل CI.
أنماط الكتابة
تتم تغطية الكتابة css-module
s أيضًا بالعديد من الأدوات الخارجية:
الفكرة الرئيسية لهذه الأدوات هي جلب css-module
s ثم إنشاء ملفات .d.ts
منها. ثم سيتم كتابة الأنماط الخاصة بك بالكامل. لا يزال غير مطبق على Nuxt
أو Vue
، ولكن يمكنك Nuxt
هذه المشكلة للتقدم.
ومع ذلك ، لا أستخدم أيًا من هذه الأدوات شخصيًا في مشاريعي. قد تكون مفيدة للمشروعات ذات قواعد الشفرة الكبيرة والكثير من الأنماط ، لكنني بخير مع لقطات فقط.
كما تساعد دلائل التصميم مع اختبارات الانحدار المرئي الكثير. @storybook/addon-storyshots
مثال جميل على هذه التقنية.
Vuex
الشيء الكبير التالي هو Vuex
. لديها بعض التعقيد المدمج في التصميم للكتابة:
const result: Promise<number> = this.$store.dispatch('action_name', { payload: 1 })
المشكلة هي أنه قد لا يوجد 'action_name'
، أو يأخذ وسيطات أخرى ، أو يعرض نوعًا مختلفًا. هذا ليس شيئًا تتوقعه للتطبيق المكتوب بالكامل.
ما هي الحلول الحالية؟
vuex من الدرجة
vuex-class
هي مجموعة من الديكورات تتيح الوصول بسهولة من المكونات القائمة على الفصل إلى Vuex
الداخلية من Vuex
.
لكن ، لم يتم كتابتها بشكل آمن نظرًا لأنه لا يمكن أن يتداخل مع أنواع الحالة والأخطاء والطفرات والإجراءات.

بالطبع ، يمكنك تعليق أنواع الخصائص يدويًا.

ولكن ماذا ستفعل عندما يتغير النوع الحقيقي لحالتك أو حيلتك أو طفراتك أو أفعالك؟ سيكون لديك عدم تطابق نوع مخفي.
vuex-بسيطة
حيث vuex-simple
. إنه يوفر بالفعل طريقة مختلفة تمامًا لكتابة كود Vuex
وهذا ما يجعله آمنًا. دعونا نلقي نظرة:
import { Action, Mutation, State, Getter } from 'vuex-simple' class MyStore { // State @State() public comments: CommentType[] = [] // Getters @Getter() public get hasComments (): boolean { return Boolean(this.comments && this.comments.length > 0) } // Mutations @Mutation() public setComments (payload: CommentType[]): void { this.comments = updatedComments } // Actions @Action() public async fetchComments (): Promise<CommentType[]> { // Calling some API: const commentsList = await api.fetchComments() this.setComments(commentsList) // typed mutation return commentsList } }
في وقت لاحق ، يمكن تسجيل هذه الوحدة النمطية المكتوبة داخل Vuex
مثل:
import Vue from 'vue' import Vuex from 'vuex' import { createVuexStore } from 'vuex-simple' import { MyStore } from './store' Vue.use(Vuex) // Creates our typed module instance: const instance = new MyStore() // Returns valid Vuex.Store instance: export default createVuexStore(instance)
الآن لدينا نسخة Vuex.Store
أصلية 100 ٪ من Vuex.Store
وجميع معلومات النوع Vuex.Store
به. لاستخدام هذا المتجر المكتوب في المكون ، يمكننا كتابة سطر واحد فقط من الكود:
import Vue from 'vue' import Component from 'nuxt-class-component' import { useStore } from 'vuex-simple' import MyStore from './store' @Component({}) export default class MyComponent extends Vue { // That's all we need! typedStore: MyStore = useStore(this.$store) // Demo: will be typed as `Comment[]`: comments = typedStore.comments }
الآن Vuex
التي يمكن استخدامها بأمان داخل مشروعنا.
عندما نغير شيئًا ما داخل تعريف متجرنا ، فإنه ينعكس تلقائيًا على المكونات التي تستخدم هذا المتجر. إذا فشل شيء ما - فنحن نعرف ذلك في أسرع وقت ممكن.
هناك أيضًا مكتبات مختلفة تفعل الشيء نفسه ولكن لها واجهة برمجة تطبيقات مختلفة. اختر ما يناسبك.
مكالمات API
عندما يكون لدينا إعداد Vuex
بشكل صحيح ، نحتاج إلى ملء البيانات.
دعونا نلقي نظرة على تعريف عملنا مرة أخرى:
@Action() public async fetchComments (): Promise<CommentType[]> { // Calling some API: const commentsList = await api.fetchComments() // ... return commentsList }
كيف يمكننا أن نعرف أنه سيعود حقًا بقائمة CommentType
وليس رقمًا واحدًا أو مجموعة من مثيلات AuthorType
؟
لا يمكننا التحكم في الخادم. وقد يكسر الخادم العقد بالفعل. أو يمكننا ببساطة اجتياز مثيل api
الخاطئ أو عمل خطأ مطبعي في عنوان URL أو أيًا كان.
كيف يمكن أن نكون آمنين؟ يمكننا استخدام الكتابة وقت التشغيل! اسمحوا لي أن أعرض io-ts
لك:
import * as ts from 'io-ts' export const Comment = ts.type({ 'id': ts.number, 'body': ts.string, 'email': ts.string, }) // Static TypeScript type, that can be used as a regular `type`: export type CommentType = ts.TypeOf<typeof Comment>
ماذا نفعل هنا؟
- نحدد مثيل
ts.type
مع الحقول التي نحتاج إلى التحقق منها في وقت التشغيل عندما نتلقى استجابة من الخادم - نحدد نوعًا ثابتًا ليتم استخدامه في التعليق التوضيحي دون أي غليان إضافي
وفي وقت لاحق يمكننا استخدامه ندعو api
لدينا:
import * as ts from 'io-ts' import * as tPromise from 'io-ts-promise' public async fetchComments (): Promise<CommentType[]> { const response = await axios.get('comments') return tPromise.decode(ts.array(Comment), response.data) }
بمساعدة io-ts-promise
، يمكننا إرجاع Promise
في حالة فاشلة إذا كانت استجابة الخادم لا تتطابق مع نوع ts.array(Comment)
. انها تعمل حقا مثل التحقق من الصحة.
fetchComments() .then((data) => /* ... */ .catch(/* Happens with both request failure and incorrect response type */)
علاوة على ذلك ، فإن التعليقات التوضيحية لنوع الإرجاع تكون متزامنة مع طريقة .decode
. ولا يمكنك وضع هراء عشوائي هناك:

مع الجمع بين وقت التشغيل والشيكات الثابتة ، يمكننا أن نتأكد من أن طلباتنا لن تفشل بسبب عدم تطابق النوع.
ولكي أكون متأكداً 100 ٪ من أن كل شيء يعمل ، أوصي باستخدام اختبار قائم على العقود: إلقاء نظرة على pact
كمثال. ومراقبة التطبيق الخاص بك مع Sentry
.
جهاز التوجيه فو
المشكلة التالية هي أن this.$router.push({ name: 'wrong!' })
لا يعمل بالطريقة التي نريدها.
أود أن أقول أنه سيكون من المثالي أن يحذر من قبل المترجم أننا نوجه إلى الاتجاه الخاطئ وهذا الطريق غير موجود.
لكن هذا غير ممكن. ولا يمكن القيام بالكثير: هناك الكثير من الطرق الديناميكية ، و regex ، و backbacks ، والأذونات ، وغيرها التي يمكن أن تنهار في النهاية. الخيار الوحيد هو اختبار كل this.$router
call في تطبيقك.
VUE اختبار تيلس
بالحديث عن الاختبارات ، ليس لدي أي أعذار ناهيك عن ذكرها @vue/test-utils
التي لديها أيضًا بعض المشكلات في الكتابة.
عندما سنحاول اختبار typedStore
اللامع الجديد باستخدام خاصية typedStore
، typedStore
أننا لا نستطيع فعل ذلك وفقًا typescript
:

لماذا يحدث هذا؟ يحدث ذلك لأن استدعاء mount()
لا يعرف شيئًا عن نوع المكون الخاص بك ، لأن جميع المكونات لها نوع VueConstructor<Vue>
افتراضيًا:

هذا هو المكان الذي تأتي منه جميع المشاكل. ما الذي يمكن عمله؟
يمكنك استخدام vuetype
لإنتاج YouComponent.vue.d.ts
YouComponent.vue.d.ts التي ستخبر اختباراتك بالنوع الدقيق للمكون المركب.
يمكنك أيضًا تتبع هذه المشكلة للتقدم.
ولكن ، أنا لا أحب هذه الفكرة. هذه اختبارات ، يمكن أن تفشل. لا صفقة كبيرة.
لهذا السبب أنا (wrapper.vm as any).whatever
. (wrapper.vm as any).whatever
النهج. هذا يوفر لي الكثير من الوقت لكتابة الاختبارات. ولكن يفسد تجربة المطور قليلاً.
اتخذ قرارك الخاص هنا:
- استخدام
vuetype
على طول الطريق - قم بتطبيقه جزئيًا على أهم المكونات مع أكبر قدر من الاختبارات وقم بتحديثه بانتظام
- استخدم
any
كاحتياطي
استنتاج
زاد متوسط مستوى دعم typescript
في نظام Vue
على مدار العامين الماضيين:
Nuxt
أولاً قدم nuxt-ts
والآن السفن ts
يبني بشكل افتراضي- سوف
Vue@3
قد تحسنت دعم typescript
- المزيد من تطبيقات الطرف الثالث والإضافات ستوفر تعريفات النوع
ولكن ، هو الإنتاج جاهزة في الوقت الراهن. هذه مجرد أشياء يجب تحسينها! إن كتابة كود Vue
الآمن من نوعه يحسن حقًا تجربة Developer الخاصة بك ويسمح لك بالتركيز على الأشياء المهمة مع ترك الرفع الثقيل للمترجم.
ما هي الاختراقات والأدوات المفضلة لكتابة تطبيقات Vue
؟ دعنا نناقشه في قسم التعليقات.