
مرحبا بالجميع! في هذه المقالة ، سننظر في تطوير مقدمة مدونة Vue البسيطة باستخدام كل سحر Vue بما في ذلك Vuex و Router. وكذلك دعونا نتحدث عن بنية التطبيق والعمل مع الحاوية والموجه.
أولاً ، سنحدد مراحل إنشاء الواجهة الأمامية للتطبيق (في هذه الحالة ، المدونة):
- تخطيط
- تطبيق الهيكل العظمي
- خلق نماذج
- تنفيذ منطق العمل
- مضيفا الصفحات والطرق
- مضيفا المكونات
- تحرير
1. التخطيط
بادئ ذي بدء ، سنقوم بتدوين ما سوف يحتوي عليه برنامج SPA الخاص بنا. يجب أن تبدأ مباشرة من الصفحات ، حيث أن هذا هو ما يتفاعل معه المستخدم مباشرةً (إذا كان تقريبًا تقريبه مشابهًا لاختبارات TDD - نصف أولاً ما يجب أن يفعله التطبيق ، وكيف سيتفاعل المستخدم معه ، ومن ثم نشارك بالفعل في التنفيذ).
فما هي الصفحات ستكون:
- الصفحة الرئيسية - سيتضمن فئات شعبية وتعليقات ومقالات حديثة
- تصفح الفئة - قائمة المقالات لفئة محددة.
- عرض الأخبار - مباشرة محتوى الأخبار وقائمة من التعليقات والمقالات الأخرى من الفئة
إذا وضعت نماذج أولية للصفحات ، فستبدو كما يلي:
ليس هناك ما هو واضح ، ولكن هذا هو النموذج الأولي :-) سأشرح لك على الفور سبب عدم رسمه باستخدام برنامج النماذج الأولية: لأنه أسرع وأسهل على الورق ، وأحيانًا ما زلت بحاجة إلى تحريك يديك ، لكن عندما يتعلق الأمر بموعد تحتاج إلى التوقيع في مكان ما حزين جدا يصبح.
استنادًا إلى الصفحات التي نحصل عليها ، ندرج المكونات:
- قائمة المواد
- قائمة الفئات
- قائمة التعليقات
- شكل تعليق
سنتعامل مع تحسين المكونات بالفعل أثناء تنفيذ المكونات نفسها ، في هذه المرحلة ، هذا ليس ضروريًا.
أخيرًا ، بعد وصف جميع نقاط تفاعل المستخدم ، نصف جوهر تطبيقنا:
- مقال (العنوان ، المحتوى ، قائمة التعليقات)
- الفئة (العنوان ، قائمة الأخبار)
- التعليق (المحتوى)
من المهم ملاحظة أن جوهر منطق العمل (BL) موصوف ، وليس الجدول الأساسي. عند تطوير الجزء الأمامي من الجزء والتخطيط له ، ومعظم الجزء الخلفي (باستثناء طبقة البيانات فقط) ، من الضروري العمل مع كيانات BL ، وليس مع "الجداول". علاوة على ذلك ، يجب أن يكون لدى الكيانات فقط ما يتم استخدامه في التطبيق نفسه. يعتبر لمس المستقبل جيدًا ، ولكن نادرًا ما يأتي هذا المستقبل ، وفي الحالة التي تكون فيها الوظيفة المستخدمة هي الوحيدة
والقابلة للتوسيع هيكل التطبيق ، لا توجد مشاكل مع إضافة وظيفة في المستقبل وليس هناك أي لزوم لها في الوقت الحالي.
2. هيكل التطبيق
ننتقل إلى إنشاء الهيكل. نقوم بتنفيذ الأوامر التالية في وحدة التحكم:
npm install -g @vue/cli vue create vue-blog-habr -n -d -m npm cd vue-blog-habr
تقوم هذه الأوامر بإنشاء مشروع vue-blog-habr في الدليل المناسب. يمكن العثور على مزيد من المعلومات حول vue-cli والمعلمات المستخدمة
هنا .
وفي النهاية نحصل على هيكل المشروع القياسي:

قم بتثبيت الحزم التي نحتاجها فورًا:
npm install vue-router vuex axios bootstrap-vue sass-loader npm install --save-dev --unsafe-perm node-sass
تسجيل الوحدات المستخدمة:
src / main.js import App from './App.vue' import Vue from 'vue' import VueRouter from 'vue-router' import BootstrapVue from 'bootstrap-vue' import store from './store' import router from './router' Vue.config.productionTip = false Vue.use(VueRouter) Vue.use(BootstrapVue) new Vue({ store, router, render: h => h(App), }).$mount('#app')
نقوم بتصحيح بنية دليل المشروع بهذه الطريقة:

وصف الدليل:
- api - يحتوي على الملفات المسؤولة عن "التواصل" مع الخادم
- الأصول - الموارد الثابتة المختلفة المستخدمة: الصور ، الرموز ، ...
- مكونات - مكونات التطبيق ، دون "صفحات" المستخدمة في جهاز التوجيه
- الطرز - نماذج من منطق الأعمال ، يمكن تشبعها بوظيفة المجال المستخدم في المقدمة
- الصفحات - مكونات الصفحة المستخدمة في جهاز التوجيه
- ملفات التوجيه - التوجيه
- الخدمات - الخدمات المساعدة غير المرتبطة بمنطق الأعمال. على سبيل المثال ، خدمة العرض ، التي تحتوي على طريقة للحصول على إحداثيات عنصر في الصفحة
- تخزين - تخزين ملفات Vuex
- أنماط النمط ملفات
وتشغيل خادم الاختبار:
npm run serve
يبدأ الأمر الأخير في تشغيل الخادم الذي يتم تطبيق جميع تعديلات المشروع عليه في وضع وقت التشغيل. للوصول إلى المتصفح ، انتقل إلى:
المضيف المحلي: 8080
3. إنشاء النماذج
لا يوجد منطق معقد في تطبيقنا ، ولكن مع ذلك ، تحتاج إلى إنشاء نماذج في أي حال.
هناك عدة أسباب لهذا:
- مواصفات الكائنات - أي كائن تعسفي ، قد تحتوي على أي خصائص وطرق. عند استخدام الفئات ، نحن نعرف الخصائص والأساليب الموجودة في كائن معين
- كتابة معلمات المكون - يتبع من السابق: تتحكم مكونات Vue في نوع خصائص الإدخال
- الراحة - فيما يتعلق بالفصول الدراسية ، نعمل مع كيانات مجال الموضوع ، بسبب هذا يصبح الرمز أكثر وضوحًا. توفر الفصول أيضًا ميزات إضافية مثل get / set / static
يتم وضع جميع النماذج في الدليل المناسب. فئة المقالة تبدو كما يلي:
src / models / Article.js export default class Article { constructor(id, title, content) { this.id = id; this.title = title; this.content = content; this.comments = []; } addComment(item) { this.comments.push(item); } static createFrom(data) { const {id, title, content} = data; return new this(id, title, content); } }
4. تنفيذ منطق الأعمال
بشكل غير متوقع ، ولكن في هذه المرحلة ، فإن تنفيذ منطق الأعمال نفسه ليس ضروريًا. تحتاج إلى إنشاء كائن تخزين وتقسيمه على الفور إلى وحدات نمطية:
src / store / index.js import Vue from 'vue' import Vuex from 'vuex' import blog from './modules/blog' Vue.use(Vuex) export default new Vuex.Store({ modules: { blog, }, })
وفي الوحدة نفسها ، سنقوم بوصف كل الطفرات / التحركات / الإجراءات المستخدمة. في البداية ، يبدو المستودع كما يلي:
src / store / modules / blog.js export default { state: {}, getters: {}, mutations: {}, actions: {}, }
بالإضافة إلى ذلك ، نبدأ تشغيل كائن للعمل مع واجهة برمجة التطبيقات التي تمر عبرها جميع الطلبات. في وقت التنفيذ ، الجزء الأمامي من الجزء الخلفي ، ليست الخلفية ضرورية على الإطلاق ، لذلك يمكنك استخدام البيانات الثابتة:
src / api / index.js import Article from '@/models/Article'; import Comment from '@/models/Comment'; import Category from '@/models/Category'; export default { getArticles() { const comments = this.getComments(); const items = [ { id: 1, title: ' 1', content: ' 1', }, { id: 2, title: ' 2', content: ' 2', }, { id: 3, title: ' 3', content: ' 3', }, { id: 4, title: ' 4', content: ' 4', }, { id: 5, title: ' 5', content: ' 5', }, { id: 6, title: ' 6', content: ' 6', }, ]; return items.map((item) => { const article = Article.createFrom(item); article.comments = comments.filter((comment) => comment.article_id == article.id); return article; }); }, getComments() { const items = [ { id: 1, article_id: 1, content: ' 1', }, ]; return items.map((item) => Comment.createFrom(item)) }, getCategories() { const items = [ { id: 1, title: '', articles: [1,3,5], }, { id: 2, title: '', articles: [2,3,4], }, { id: 3, title: '', articles: [], }, ]; return items.map((item) => Category.createFrom(item)) }, addComment(comment) { if (comment) {
ما يعطي استخدام vuex:
- النقاء - المكونات لا تتحول إلى أشياء الله "تعرف الكثير"
- الاتساق - إذا كان لديك العديد من المكونات التي تستخدم نفس البيانات ، فعند استخدام حاوية vuex عند تغيير البيانات ، سيتم تحديثها (البيانات) في جميع أنحاء التطبيق ، وليس فقط في المكون الذي بدأ التحديث
- الراحة - يعد الوصول إلى المتجر أسهل بكثير وأكثر ملاءمة من وصف أنواع المعلمات و / أو مكالمات API في كل مكون
- الاختبارات - على الرغم من الذي يفعل ذلك؟
5. إضافة الصفحات والطرق
استنادًا إلى الهيكل المخطط مسبقًا ، نحتاج إلى إنشاء 4 صفحات: فهرس ، فئة ، مقالة ، بالإضافة إلى 404 صفحة. في دليل src / pages ، يضيف الملفات المناسبة:
- Article.vue
- Category.vue
- Index.vue
- 404.vue
إذا كان يجب أن تحتوي صفحة 404 على صفحة شخصية ذات تصميم فردي ، عندئذٍ يمكن تغيير نقطة الإدخال بهذه الطريقة:
src / app.vue <template> <div id="app"> <template v-if="is404"> <router-view></router-view> </template> <template v-else> <Header></Header> <main> <b-container> <router-view></router-view> </b-container> </main> </template> </div> </template> <script> import '@/styles/index.scss'; import Header from '@/components/Header.vue'; export default { name: 'App', components: { Header, }, computed: { is404() { return this.$route.name === '404'; }, }, } </script>
يبدو ملف الصفحة الرئيسية كما يلي:
src / pages / Index.vue <template> <b-row> <b-col md="8" lg="9"> <ListItems :items="lastArticles"> <template v-slot:default="props"> <ArticleItem :item="props.item"></ArticleItem> </template> <template v-slot:empty> :) </template> </ListItems> </b-col> <b-col md="4" lg="3"> <ListItems :items="popularCategories" v-slot="props"> <router-link :to="getCategoryRoute(props.item)"> {{ props.item.title }} </router-link> </ListItems> <CommentItem v-for="(item, index) in lastComments" :key="index" :item="item"></CommentItem> </b-col> </b-row> </template> <script> import ListItems from '@/components/ListItems.vue' import ArticleItem from '@/components/ArticleItem.vue' import CommentItem from '@/components/CommentItem.vue' import { mapGetters, } from 'vuex' export default { name: 'Index', components: { ListItems, ArticleItem, CommentItem, }, data() { return {}; }, methods: { getCategoryRoute(item) { return { name: 'Category', params: { category_id: item.id, }, }; }, }, computed: { ...mapGetters([ 'lastArticles', 'lastComments', 'popularCategories', ]), }, created() { this.$store.dispatch('loadArticles'); this.$store.dispatch('loadComments'); this.$store.dispatch('loadCategories'); }, } </script>
بعد تنفيذ الصفحات ، نضيف على الفور جميع النصوص والإجراءات المستخدمة إلى مستودع vuex (بدون تطبيق):
src / store / modules / blog.js export default { state: { articles: [], comments: [], categories: [],
هناك عدة نقاط يجب ملاحظتها:
- عندما تحتاج إلى الحصول على البيانات ، تحتاج إلى الاتصال بالدولة. إذا كنت بحاجة إلى القيام ببعض التلاعب بالبيانات بعد تلقي البيانات ، فمن الأفضل إنشاء رسائل لهذا ؛
- عندما تحتاج إلى القيام بشيء ما مع البيانات ، فأنت بحاجة إلى الرجوع إلى الإجراءات ، وليس الطفرات. يمكن أن يتضمن أحد الإجراءات العديد من الطفرات وتنفيذ عمليات معالجة بيانات أخرى بالإضافة إلى الكتابة للحالة وليس لديها قيود غير متزامنة
- لا حاجة لتقديم طلبات مباشرة إلى API / الراحة. عند طلب جميع البيانات من خلال Vuex ، سيضمن ذلك الحالة المتسقة للتطبيق بأكمله (إذا تم التخزين نفسه بشكل صحيح)
- في جميع الروابط وتصفح البرامج ، تحتاج إلى الرجوع إلى المسارات المسماة. سيتيح لك ذلك تغيير المسارات دون أي ألم دون تحرير الروابط والتنقل في البرنامج. هذا بطبيعة الحال يتعلق بالارتباطات داخل SPA ، ويجب تحديد العناوين التي لم تتم معالجتها بواسطة جهاز التوجيه كالمعتاد
بعد إنشاء الصفحات وتعبئتها بنفسها ، تحتاج إلى إضافة القواعد المناسبة إلى جهاز التوجيه:
src / router / index.js import VueRouter from 'vue-router' import blog from './blog' export default new VueRouter({ mode: 'history', routes: [ { path: '/', name: 'Index', component: () => import('@/pages/Index.vue'), }, ...blog, { path: '*', name: '404', component: () => import('@/pages/404.vue'), }, ] })
يمكن العثور على جميع معلمات جهاز التوجيه في الوثائق:
router.vuejs.org/ru/api/#constructor options-router
6. إضافة مكونات
بعد تنفيذ جميع الصفحات ، نحصل على قائمة المكونات التالية ، أهمها:
- CategoryItem
- ArticleItem
- CommentItem
- CommentForm
ومساعد:
بما أننا نستخدم النماذج ، عند تطبيق المكونات ، يمكننا استخدامها لتوصيف المعلمات:
src / components / ArticleItem.vue <template> <b-card :title="item.title" class="article-item-card"> <router-link :to="getArticleRoute" class="card-link"> </router-link> </b-card> </template> <script> import Article from '@/models/Article'; export default { name: 'ArticleItem', props: { item: Article, }, computed: { getArticleRoute() { return { name: 'Article', params: { post_id: this.item.id, }, }; }, }, } </script> <style> .article-item-card { margin-bottom: 1rem; } </style>
بضع كلمات عن تنفيذ المكونات على الرئيسية:
src / pages / Index.vue <template> <b-row> <b-col md="8" lg="9"> <ListItems :items="lastArticles"> <template v-slot:default="props"> <ArticleItem :item="props.item"></ArticleItem> </template> <template v-slot:empty> :) </template> </ListItems> </b-col> <b-col md="4" lg="3"> <ListItems :items="popularCategories" v-slot="props"> <router-link :to="getCategoryRoute(props.item)"> {{ props.item.title }} </router-link> </ListItems> <CommentItem v-for="(item, index) in lastComments" :key="index" :item="item"></CommentItem> </b-col> </b-row> </template> <script> import ListItems from '@/components/ListItems.vue' import ArticleItem from '@/components/ArticleItem.vue' import CommentItem from '@/components/CommentItem.vue' import { mapGetters, } from 'vuex' export default { name: 'Index', components: { ListItems, ArticleItem, CommentItem, }, data() { return {}; }, methods: { getCategoryRoute(item) { return { name: 'Category', params: { category_id: item.id, }, }; }, }, computed: { ...mapGetters([ 'lastArticles', 'lastComments', 'popularCategories', ]), }, created() { this.$store.dispatch('loadArticles'); this.$store.dispatch('loadComments'); this.$store.dispatch('loadCategories'); }, } </script>
تستخدم هذه الصفحة مكون المجمع من ListItems. للوهلة الأولى ، قد يبدو زائداً عن الحاجة ، لأنه يمكنك المضي قدمًا في تصميم v-for عند إجراء التعليقات ، ولكن استخدام فتحات يقلل بشكل كبير من الكود المستخدم ويسمح لك بإعادة استخدام نفس العنصر في عدة أماكن.
ولكن إذا نظرت إلى قائمة المقالات ، فسيتم استخدامها على صفحتين (الفهرس والفئة) بنفس المكالمة تمامًا. في هذه الحالة ، يكون القرار الصحيح هو إنشاء مكون ArticleItems ورثه من ListItems:
src / components / ArticleItems.vue <template> <ListItems :items="items"> <template v-slot:default="props"> <ArticleItem :item="props.item"></ArticleItem> </template> <template v-slot:empty> :) </template> </ListItems> </template> <script> import ListItems from '@/components/ListItems.vue' import ArticleItem from '@/components/ArticleItem.vue' export default { name: 'ArticleItems', components: { ArticleItem, ListItems, }, extends: ListItems, } </script>
في هذه الحالة ، يسمح الميراث بعدم تكرار وصف المعلمات (خاصية الدعائم) ، وهو مأخوذ من المكون الأصلي. المزيد عن الميراث والشوائب:
ru.vuejs.org/v2/api/#extends ،
ru.vuejs.org/v2/guide/mixins.htmlبعد إنشاء مكون جديد ، تحتاج أيضًا إلى إصلاح ملفات الصفحة:
src / pages / Category.vue (كان) <template> <div> <div v-if="category"> <h1> {{ category.title }} </h1> <ListItems :items="articles"> <template v-slot:default="props"> <ArticleItem :item="props.item"></ArticleItem> </template> <template v-slot:empty> :) </template> </ListItems> </div> <div v-else> </div> </div> </template> <script> import ListItems from '@/components/ListItems.vue' import ArticleItem from '@/components/ArticleItem.vue' import { mapActions, } from 'vuex' export default { name: 'Category', components: { ListItems, ArticleItem, }, computed: { categoryId() { return this.$route.params['category_id'] || null; }, category() { return this.$store.state.blog.activeCategory; }, articles() { return this.$store.getters.activeCategoryArticles; }, }, methods: { ...mapActions([ 'loadActiveCategory', ]), }, mounted() { this.loadActiveCategory(this.categoryId); }, } </script>
src / pages / Category.vue (أصبح) <template> <div> <div v-if="category"> <h1> {{ category.title }} </h1> <ArticleItems :items="articles"></ArticleItems> </div> <div v-else> </div> </div> </template> <script> import ArticleItems from '@/components/ArticleItems.vue' import { mapActions, } from 'vuex' export default { name: 'Category', components: { ArticleItems, }, computed: { categoryId() { return this.$route.params['category_id'] || null; }, category() { return this.$store.state.blog.activeCategory; }, articles() { return this.$store.getters.activeCategoryArticles; }, }, methods: { ...mapActions([ 'loadActiveCategory', ]), }, mounted() { this.loadActiveCategory(this.categoryId); }, } </script>
يجب عليك أيضًا الانتباه إلى النموذج لإضافة التعليقات. لا يتم تقديم الطلبات مباشرةً إلى واجهة برمجة التطبيقات ، ولكن يتم تنفيذ إجراء Vuex ويصبح الطلب إلى واجهة برمجة التطبيقات "داخليًا" بالفعل ، ويتم تحديث نموذج المادة الضروري وتحديث قائمة التعليقات. يبدو رمز المكون نفسه كما يلي:
src / components / CommentForm.vue <template> <form @submit.prevent="onSubmit"> <textarea class='form-control' v-model="content"></textarea> <br> <button type="submit" class="btn btn-primary"></button> </form> </template> <script> export default { name: 'CommentForm', props: { articleId: Number, }, data() { return { content: '', }; }, methods: { onSubmit() { if (this.content) { this.$store.dispatch('addComment', { content: this.content, article_id: this.articleId, }); this.content = ''; } }, }, } </script>
بعد تنفيذ جميع المكونات والصفحات ، وتنفيذ الدعامات وإضافة بيانات الاختبار ، في هذه الحالة بالذات ، يكون الجزء الأمامي من المدونة جاهزًا. لكن كما يحدث عادةً في الممارسة العملية ، هذا بعيد عن نهاية المشروع ، لأنه بعد الانتهاء من العمل ، تبدأ التغييرات :-)
7. التعديلات
افترض أننا بحاجة إلى تغيير عرض المقالات في صفحة الفئة: يجب عرضها في عمودين. وعلى الصفحة الرئيسية يجب أن يبقى كل شيء كما هو.
نضيف خاصية cols إضافية إلى عدد الأعمدة في مكون ArticleItems.
src / components / ArticleItems.vue (أصبح) <template> <ListItems :items="items" class="row"> <template v-slot:default="props"> <b-col :cols="itemCols"> <ArticleItem :item="props.item"></ArticleItem> </b-col> </template> <template v-slot:empty> <b-col> :) </b-col> </template> </ListItems> </template> <script> import ListItems from '@/components/ListItems.vue' import ArticleItem from '@/components/ArticleItem.vue' export default { name: 'ArticleItems', components: { ArticleItem, ListItems, }, extends: ListItems, props: { cols: { type: Number, default: 1, }, }, computed: { itemCols() { return 12 / this.cols; }, }, } </script>
في استدعاء المكون في صفحة الفئات ، أضف الخاصية المطلوبة:
src / pages / Category.vue <ArticleItems :items="articles" :cols="2"></ArticleItems>
ثم أردنا الانتقال إلى صفحة عرض المقالة ، وإضافة روابط إلى المقالات المجاورة (إلى الأمام / الخلف). للقيام بذلك ، ستحتاج إلى إضافة حرفين وروابط إلى الصفحة:
src / pages / Article.vue <template> <b-row v-if="article"> <b-col md="8" lg="9"> <h1> {{ article.title }} </h1> <p class="mb-4"> {{ article.content }} </p> <table class="table table-bordered"> <tbody> <tr> <td class="w-50"> <router-link v-if="prevArticle" :to="getArticleRoute(prevArticle)"> {{ prevArticle.title }} </router-link> </td> <td class="text-right"> <router-link v-if="nextArticle" :to="getArticleRoute(nextArticle)"> {{ nextArticle.title }} </router-link> </td> </tr> </tbody> </table> <CommentForm :articleId="article.id"></CommentForm> <CommentItem v-for="(item, index) in article.comments" :key="index" :item="item"></CommentItem> </b-col> <b-col md="4" lg="3"> <CommentItem v-for="(item, index) in lastComments" :key="index" :item="item"></CommentItem> </b-col> </b-row> </template> <script> import CommentForm from '@/components/CommentForm.vue'; import CommentItem from '@/components/CommentItem.vue'; import { mapActions, mapGetters, } from 'vuex' export default { name: 'Article', components: { CommentForm, CommentItem, }, computed: { ...mapGetters([ 'lastComments', 'nextArticle', 'prevArticle', ]), articleId() { return this.$route.params['post_id'] || null; }, article() { return this.$store.state.blog.activeArticle; }, }, methods: { ...mapActions([ 'loadComments', 'loadActiveArticle', ]), getArticleRoute(item) { return { name: 'Article', params: { post_id: item.id, }, }; }, }, mounted() { this.loadComments(); this.loadActiveArticle(this.articleId); }, watch: { articleId(value) { this.loadActiveArticle(value); }, }, } </script>
وتنفيذ الحروف نفسها:
src / store / modules / blog.js ... prevArticle(state) { let prevItem = null; if (state.activeArticle) { state.articles.forEach((item, index) => { if (item.id == state.activeArticle.id) { prevItem = state.articles[index-1] || null; } }); } return prevItem; }, nextArticle(state) { let nextItem = null; if (state.activeArticle) { state.articles.forEach((item, index) => { if (item.id == state.activeArticle.id) { nextItem = state.articles[index+1] || null; } }); } return nextItem; }, ...
وأخيرًا ، نحتاج إلى تغيير عنوان URL لصفحة المقالة ، من "article-123" إلى "post-123". نظرًا لاستخدام المسارات المحددة في جميع أنحاء التطبيق ، يكفي تغيير نمط المسار فقط:
src / router / blog / index.js export default [ { path: '/cat-:category_id', name: 'Category', component: () => import('@/pages/Category.vue'), }, { path: '/post-:post_id', name: 'Article', component: () => import('@/pages/Article.vue'), }, ];
الأدب المفيد
- vuex.vuejs.org/ru/guide
- cli.vuejs.org/ru/guide
- router.vuejs.org/ru
PS
على الرغم من أن هذا تحول إلى تطبيق بسيط للغاية ، إلا أن الهيكل مصنوع بطريقة يمكنك من خلالها تغيير شيء ما و / أو إضافته بسهولة:
- تتعامل المكونات حصريًا مع الصور المرئية ، ويتم أخذ البيانات من المستودع في شكل نماذج ، ولا يتم تقديم طلبات واجهة برمجة التطبيقات المباشرة.
- تحتوي النماذج على منطق العمل الضروري وجميع العلاقات بين الكيانات. أيضا لا تصل إلى API.
- التخزين (vuex) هو رابط الاتصال لجميع المكونات: يتم طلب البيانات من واجهة برمجة التطبيقات وتحويلها إلى طرز ، ثم يتم الوصول إليها بواسطة المكونات.
بصريا ، يمكن تمثيل هذا على النحو التالي:

مع هذا المخطط ، يمكن تقسيم العمل في المشروع بأمان بين ثلاث وحدات:
- السيد تخطيط - يشارك في تنفيذ الصفحات والمكونات ، ويصف أيضًا الحقول التي يجب أن تحتوي على نماذج ، والأخطاء والإجراءات التي يجب أن تكون في المستودع ؛
- السيد Front - يشارك في تنفيذ منطق الأعمال ونماذج التخزين Vuex ، وكذلك في التوجيه وكل شيء آخر لا يهم السيد التصميم ؛
- السيد back - يشارك في تنفيذ خدمات API على الجانب الخلفي.
يمكن أن تعمل جميع الوحدات بشكل مستقل عن بعضها البعض إذا تمت مناقشة كل الأرضية المشتركة بينهما سابقًا (بشكل عام ، إذا كان هناك أي تخطيط قبل العمل في المشروع).
أو إذا لم يكن هناك فريق على هذا النحو ، فمن المنطقي أن يتم الترتيب بالترتيب من الأعلى إلى الأسفل ، بحيث يكون هناك شيء يمكن مشاهدته في أسرع وقت ممكن (طرح MVP).
مستودع به جميع المصادر:
github.com/irpsv/vue-blog-habr