نظرًا لأصغر مدونة تُعرف باسم مدونة صغيرة على جميع الشرائع



مرحبا بالجميع! في هذه المقالة ، سننظر في تطوير مقدمة مدونة Vue البسيطة باستخدام كل سحر Vue بما في ذلك Vuex و Router. وكذلك دعونا نتحدث عن بنية التطبيق والعمل مع الحاوية والموجه.

أولاً ، سنحدد مراحل إنشاء الواجهة الأمامية للتطبيق (في هذه الحالة ، المدونة):

  1. تخطيط
  2. تطبيق الهيكل العظمي
  3. خلق نماذج
  4. تنفيذ منطق العمل
  5. مضيفا الصفحات والطرق
  6. مضيفا المكونات
  7. تحرير

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: [], // activeArticle: null, activeCategory: null, }, getters: { lastArticles(state) { return []; }, lastComments(state) { return []; }, popularCategories(state) { return []; }, activeCategoryArticles(state) { return []; }, }, mutations: {}, actions: { async loadArticles({ commit, state }) {}, async loadComments({ commit, state }) {}, async loadCategories({ commit, state }) {}, async loadActiveCategory(context, id) {}, async loadActiveArticle(context, id) {}, async addComment({ commit }, payload) {}, }, } 


هناك عدة نقاط يجب ملاحظتها:

  • عندما تحتاج إلى الحصول على البيانات ، تحتاج إلى الاتصال بالدولة. إذا كنت بحاجة إلى القيام ببعض التلاعب بالبيانات بعد تلقي البيانات ، فمن الأفضل إنشاء رسائل لهذا ؛
  • عندما تحتاج إلى القيام بشيء ما مع البيانات ، فأنت بحاجة إلى الرجوع إلى الإجراءات ، وليس الطفرات. يمكن أن يتضمن أحد الإجراءات العديد من الطفرات وتنفيذ عمليات معالجة بيانات أخرى بالإضافة إلى الكتابة للحالة وليس لديها قيود غير متزامنة
  • لا حاجة لتقديم طلبات مباشرة إلى 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

ومساعد:

  • ListItems
  • رأس

بما أننا نستخدم النماذج ، عند تطبيق المكونات ، يمكننا استخدامها لتوصيف المعلمات:

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'), }, ]; 



الأدب المفيد


  1. vuex.vuejs.org/ru/guide
  2. cli.vuejs.org/ru/guide
  3. router.vuejs.org/ru


PS


على الرغم من أن هذا تحول إلى تطبيق بسيط للغاية ، إلا أن الهيكل مصنوع بطريقة يمكنك من خلالها تغيير شيء ما و / أو إضافته بسهولة:

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

بصريا ، يمكن تمثيل هذا على النحو التالي:


مع هذا المخطط ، يمكن تقسيم العمل في المشروع بأمان بين ثلاث وحدات:

  • السيد تخطيط - يشارك في تنفيذ الصفحات والمكونات ، ويصف أيضًا الحقول التي يجب أن تحتوي على نماذج ، والأخطاء والإجراءات التي يجب أن تكون في المستودع ؛
  • السيد Front - يشارك في تنفيذ منطق الأعمال ونماذج التخزين Vuex ، وكذلك في التوجيه وكل شيء آخر لا يهم السيد التصميم ؛
  • السيد back - يشارك في تنفيذ خدمات API على الجانب الخلفي.

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

أو إذا لم يكن هناك فريق على هذا النحو ، فمن المنطقي أن يتم الترتيب بالترتيب من الأعلى إلى الأسفل ، بحيث يكون هناك شيء يمكن مشاهدته في أسرع وقت ممكن (طرح MVP).

مستودع به جميع المصادر: github.com/irpsv/vue-blog-habr

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


All Articles