كيفية تنظيم تبعياتك في تطبيق Vue

يعرف كل من يعرف Vue أن تطبيق Vue له نقطة إدخال واحدة - ملف main.js هناك ، بالإضافة إلى إنشاء مثيل لـ Vue ، هناك استيراد ونوع من حقن التبعية لجميع تبعياتك العالمية (التوجيهات والمكونات والمكونات الإضافية). كلما كان المشروع أكبر ، كلما ازدادت التبعيات ، والتي ، علاوة على ذلك ، لكل منها تكوينه الخاص. ونتيجة لذلك ، نحصل على ملف ضخم واحد مع جميع التكوينات.
ستناقش هذه المقالة كيفية تنظيم التبعيات العالمية لتجنب ذلك.



لماذا تكتبها بنفسك؟


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

العقل المدبر


كان منظم مثل هذه المنظمة Nuxt. لقد استخدمته في مشروع كبير مع Vue.
Nuxt لديه ميزة رائعة - الإضافات. كل مكون إضافي هو ملف يصدر دالة. يتم تمرير التكوين إلى الوظيفة ، والتي سيتم تمريرها أيضًا إلى مُنشئ Vue عند إنشاء المثيل ، بالإضافة إلى المتجر بأكمله.

بالإضافة إلى ذلك ، تتوفر ميزة مفيدة للغاية ، inject في كل مكون إضافي. يجعل حقن التبعية إلى مثيل الجذر من Vue وإلى كائن store . وهذا يعني أنه في كل مكون ، في كل وظيفة تخزين ، ستتوفر التبعية المحددة من خلال this .

أين يمكن أن يكون هذا مفيدًا؟


بالإضافة إلى حقيقة أن main.js "تفقد الوزن" بشكل ملحوظ ، ستحصل أيضًا على فرصة لاستخدام التبعية في أي مكان في التطبيق دون عمليات استيراد غير ضرورية.

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

مثال آخر هو الانتظار . ذهب مطورو هذا المكون الإضافي إلى أبعد من ذلك وأضافوا خاصية $wait ليس فقط إلى مثيل Vue ، ولكن أيضًا إلى متجر vuex. بالنظر إلى تفاصيل البرنامج المساعد ، يثبت هذا أنه مفيد للغاية. على سبيل المثال ، يحتوي المتجر على إجراء يتم استدعاؤه على عدة مكونات. وفي كل حالة ، تحتاج إلى إظهار اللودر على بعض العناصر. بدلاً من استدعاء $wait.start('action') و $wait.end('action') قبل وبعد كل استدعاء إجراء ، يمكنك ببساطة استدعاء هذه الأساليب مرة واحدة في الإجراء نفسه. وهذا أكثر قابلية للقراءة وأقل قيمة من dispatch('wait/start', 'action' {root: true}) . في حالة المتجر ، هذا هو السكر النحوي.

من الكلمات إلى التعليمات البرمجية


الهيكل الأساسي للمشروع


دعنا نرى كيف يبدو المشروع الآن:
src
- store
- App.vue
- main.js

يبدو main.js شيء من هذا القبيل:
 import Vue from 'vue'; import App from './App.vue'; import store from './store'; new Vue({ render: h => h(App), store }).$mount('#app'); 


نحن نربط التبعية الأولى


نريد الآن توصيل المحاور بمشروعنا وإنشاء نوع من التكوين له. لقد اتبعت مصطلحات Nuxt وأنشأت دليل plugins في src . يوجد داخل الدليل axios.js index.js و axios.js .

src
- plugins
-- index.js
-- axios.js
- store
- App.vue
- main.js

كما ذكر أعلاه ، يجب على كل مكون إضافي تصدير وظيفة. في الوقت نفسه ، داخل الوظيفة نريد الوصول إلى المخزن ومن ثم وظيفة inject .

axios.js
 import axios from 'axios'; export default function (app) { //       – , , interceptors  .. axios.defaults.baseURL = process.env.API_BASE_URL; axios.defaults.headers.common['Accept'] = 'application/json'; axios.defaults.headers.post['Content-Type'] = 'application/json'; axios.interceptors.request.use(config => { ... return config; }); } 

index.js :
 import Vue from 'vue'; import axios from './axios'; export default function (app) { let inject = () => {}; //   inject,        Dependency Injection axios(app, inject); //       Vue    } 


كما ترى ، يقوم ملف index.js أيضًا بتصدير الوظيفة. يتم ذلك حتى تتمكن من تمرير كائن app هناك. الآن دعنا نغير main.js هذه الوظيفة.

main.js :
 import Vue from 'vue'; import App from './App.vue'; import store from './store'; import initPlugins from './plugins'; //    // ,    Vue,  ,     initPlugins const app = { render: h => h(App), store }; initPlugins(app); new Vue(app).$mount('#app'); //   initPlugins    


النتيجة


في هذه المرحلة ، حققنا أننا أزلنا تكوين المكون الإضافي من main.js في ملف منفصل.

بالمناسبة ، فائدة تمرير كائن app إلى جميع المكونات الإضافية لدينا هي أنه داخل كل مكون إضافي لدينا الآن إمكانية الوصول إلى المتجر. يمكنك استخدامه بحرية عن طريق استدعاء commit dispatch ، وكذلك الوصول إلى store.state و store.getters .

إذا كنت تحب نمط ES6 ، فيمكنك حتى القيام بذلك:

axios.js
 import axios from 'axios'; export default function ({store: {dispatch, commit, state, getters}}) { ... } 

المرحلة الثانية - حقن التبعية


لقد أنشأنا بالفعل المكون الإضافي الأول والآن يبدو مشروعنا كما يلي:

src
- plugins
-- index.js
-- axios.js
- store
- App.vue
- main.js

نظرًا لأنه في معظم المكتبات حيث يكون ذلك ضروريًا حقًا ، يتم تطبيق Dependency Injection بالفعل باستخدام Vue.use ، سنقوم بإنشاء مكون إضافي بسيط خاص بنا.

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

انتظر البرنامج المساعد


قم wait.js ملف آخر في دليل plugins - wait.js

لدي بالفعل وحدة vuex ، والتي دعوتها أيضًا wait . يقوم بثلاث خطوات بسيطة:

- start - يقوم بتعيين خاصية الحالة لكائن يسمى action إلى true
- end - يزيل من الحالة خاصية كائن مسمى action
- is - يحصل من الحالة على خاصية كائن يسمى action

في هذا البرنامج المساعد سوف نستخدمه.

wait.js
 export default function ({store: {dispatch, getters}}, inject) { const wait = { start: action => dispatch('wait/start', action), end: action => dispatch('wait/end', action), is: action => getters['wait/waiting'](action) }; inject('wait', wait); } 


وربط البرنامج المساعد لدينا:

index.js :
 import Vue from 'vue'; import axios from './axios'; import wait from './wait'; export default function (app) { let inject = () => {}; Injection axios(app, inject); wait(app, inject); } 


وظيفة الحقن


الآن نقوم بتنفيذ وظيفة inject .
 //   2 : // name – ,       this.  ,   Vue        Dependency Injection // plugin – ,       this.  ,  ,           let inject = (name, plugin) => { let key = `$${name}`; //      app[key] = plugin; //     app app.store[key] = plugin; //     store //  Vue.prototype Vue.use(() => { if (Vue.prototype.hasOwnProperty(key)) { return; } Object.defineProperty(Vue.prototype, key, { get () { return this.$root.$options[key]; } }); }); }; 


سحر Vue.prototype


الآن عن السحر. تقول وثائق Vue أنه يكفي كتابة Vue.prototype.$appName = ' '; و $appName ستكون متاحة في this .

ومع ذلك ، في الواقع اتضح أن الأمر ليس كذلك. بسبب جوجل ، لم يكن هناك إجابة لماذا لم ينجح هذا التصميم. لذلك ، قررت الاتصال بمؤلفي البرنامج المساعد الذين قاموا بالفعل بتنفيذ هذا.

مزيج عالمي


كما في مثالنا ، نظرت إلى كود البرنامج المساعد vue-wait . إنهم يقدمون مثل هذا التنفيذ (يتم مسح شفرة المصدر للوضوح):

 Vue.mixin({ beforeCreate() { const { wait, store } = this.$options; let instance = null; instance.init(Vue, store); // inject to store this.$wait = instance; // inject to app } }); 

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

ولكن ماذا لو النموذج الأولي؟


تم اقتراض الفكرة وراء حل النموذج الأولي المستخدم في inject وظيفة inject من Nuxt. يبدو الطريق الصحيح أكثر بكثير من المزيج العالمي ، لذلك استقرت عليه.

  Vue.use(() => { // ,        if (Vue.prototype.hasOwnProperty(key)) { return; } //    ,         app  Object.defineProperty(Vue.prototype, key, { get () { return this.$root.$options[key]; //  ,    this } }); }); 


النتيجة


بعد هذه التلاعبات ، نحصل على فرصة الوصول إلى this.$wait من أي مكون ، بالإضافة إلى أي طريقة في المتجر.

ماذا حدث


هيكل المشروع:

src
- plugins
-- index.js
-- axios.js
-- wait.js
- store
- App.vue
- main.js


index.js :
 import Vue from 'vue'; import axios from './axios'; import wait from './wait'; export default function (app) { let inject = (name, plugin) => { let key = `$${name}`; app[key] = plugin; app.store[key] = plugin; Vue.use(() => { if (Vue.prototype.hasOwnProperty(key)) { return; } Object.defineProperty(Vue.prototype, key, { get () { return this.$root.$options[key]; } }); }); }; axios(app, inject); wait(app, inject); } 


wait.js
 export default function ({store: {dispatch, getters}}, inject) { const wait = { start: action => dispatch('wait/start', action), end: action => dispatch('wait/end', action), is: action => getters['wait/waiting'](action) }; inject('wait', wait); } 


axios.js
 import axios from 'axios'; export default function (app) { axios.defaults.baseURL = process.env.API_BASE_URL; axios.defaults.headers.common['Accept'] = 'application/json'; axios.defaults.headers.post['Content-Type'] = 'application/json'; } 


main.js :
 import Vue from 'vue'; import App from './App.vue'; import store from './store'; import initPlugins from './plugins'; const app = { render: h => h(App), store }; initPlugins(app); new Vue(app).$mount('#app'); 

الخلاصة


نتيجة للمعالجة ، تلقينا استدعاء استيراد واحد ودالة واحدة في ملف main.js والآن أصبح من الواضح على الفور مكان البحث عن التهيئة لكل مكون إضافي وكل تبعية عالمية.

عند إضافة مكون إضافي جديد ، ما عليك سوى إنشاء ملف يقوم بتصدير الوظيفة واستيرادها إلى index.js واستدعاء هذه الوظيفة.

في ممارستي ، أثبتت هذه البنية أنها مريحة للغاية ، علاوة على ذلك ، يتم نقلها بسهولة من مشروع إلى آخر. الآن لا يوجد ألم إذا كنت بحاجة إلى القيام بحقنة التبعية أو تكوين مكون إضافي آخر.

شارك تجربتك مع إدارة التبعية في التعليقات. مشاريع ناجحة!

Source: https://habr.com/ru/post/ar423013/


All Articles