Как использовать vue для маршрутизации на разные страницы и отображения их заголовка и текста?

#vue.js #vuex #vue-router

#vue.js #vuex #vue-маршрутизатор

Вопрос:

Я создаю веб-сайт post в vue. Это моя проблема:

Есть 2 страницы, одна для всех блогов, а другая, где вы можете просматривать блог. Я работаю с vuex. Когда я нажимаю на один из блогов, меня отправляют на страницу просмотра блога, и я вижу заголовок правильного блога, так что с этим проблем нет.

То, что я хочу сделать, кажется простым. Мне нужна страница viewblog с двумя элементами: заголовок и текст. Эти два должны поступать из хранилища (vuex).

На данный момент это страница viewblog:

 <template>
  <div class="post">
      <h1>{{ $route.params.title }}</h1>
      // here I want my text from the store.
  </div>
</template>

<script>
export default {
}
</script>

<style>

</style>
 

И это библиотека:

 

<template>
  <div class="card-wrapper">
    <Card :post="post" v-for="(post, index) in cards" :key="index" />
  </div>
</template>

<script>
import Card from "../components/Card.vue";

export default {
  components: {
    Card,
  },
  computed: {
    cards() {
      return this.$store.state.cards;
    },
  },
};
</script>

<style>
// ...
</style>
 

Библиотеке принадлежит карточка:

 <template>
  <div class="card">
    <router-link
      :post="post"
      :to="{ name: 'Post', params: { id: post.index, title: post.title } }"
    >
      <div class="image"></div>
      <div class="title">{{ post.title }}</div>
    </router-link>
  </div>
</template>

<script>
export default {
  name: "Card",
  props: ["post"],
};
</script>
<style>
// ...
</style>
 

Это мой vue-маршрутизатор:

 import { createRouter, createWebHashHistory } from 'vue-router'
import Home from '../views/Home.vue'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    component: () => import('../views/About.vue')
  },
  {
    path: '/post/:id',
    name: 'Post',
    component: () => import('../views/Post.vue')
  }
]

const router = createRouter({
  history: createWebHashHistory(),
  routes
})

router.beforeEach((to, from, next) => {
  console.log(to)
  let documentTitle = `${ process.env.VUE_APP_TITLE } - ${to.name}`
  if (to.params.title) {
    documentTitle  = ` - ${ to.params.title }`
  }
  document.title = documentTitle
  next()
})

export default router
 

Наконец, хранилище (vuex):

 import { createStore } from "vuex";

export default createStore({
    state: {
        cards: [{
                title: "Blog 1",
                text: "This is blog 1",
                index: 1,
            },
            {
                title: "Blog 2",
                text: "This is blog 2",
                index: 2,
            },
            {
                title: "Blog 3",
                text: "This is blog 3",
                index: 3,
            },
            {
                title: "Blog 4",
                text: "This is blog 4",
                index: 4,
            },
            {
                title: "Blog 5",
                text: "This is blog 5",
                index: 5,
            },
            {
                title: "Blog 6",
                text: "This is blog 6",
                index: 6,
            },
            {
                title: "Blog 7",
                text: "This is blog 7",
                index: 7,
            },
        ],
    },
    mutations: {},
    actions: {},
    modules: {},
});
 

Это результат в библиотеке:
Результат (библиотека)
И это результат на странице просмотра блога, когда я нажимаю на блог 1:
Результат (просмотр блога)
Для наглядности: под заголовком будет текст, который поступает из магазина. Итак, в этом примере этот текст будет This is blog 1

Спасибо, что помогли мне!

Комментарии:

1. Я бы посоветовал вам написать средство получения vueX с параметром для выбора заданной карты, соответствующей критериям (тот же идентификатор)

2. не могли бы вы показать решение с вашим кодом, основанным на моем?

Ответ №1:

Подход 1: передача по параметрам маршрута

Вы можете определить функцию получения для выбора заданного текста блога. Передайте :id blog-route его как a param , чтобы вы могли использовать его в качестве фильтра в своем getter . Затем в этом получателе определите простую логику фильтра. Я сделал это с forEach помощью , но есть и другие варианты фильтрации массива, выберите тот, который соответствует вашим потребностям.

Внутри component вы можете сопоставить средство получения и вызвать его в вычисляемом свойстве с идентификатором, переданным через маршрут, и вуаля, он должен вернуть текст этого конкретного сообщения в блоге.

Хранилище VueX

 import { createStore } from 'vuex';

export default createStore({
    state: {
        cards: [{
                title: "Blog 1",
                text: "This is blog 1",
                index: 1,
            },
            {
                title: "Blog 2",
                text: "This is blog 2",
                index: 2,
            },
            {
                title: "Blog 3",
                text: "This is blog 3",
                index: 3,
            },
            {
                title: "Blog 4",
                text: "This is blog 4",
                index: 4,
            },
            {
                title: "Blog 5",
                text: "This is blog 5",
                index: 5,
            },
            {
                title: "Blog 6",
                text: "This is blog 6",
                index: 6,
            },
            {
                title: "Blog 7",
                text: "This is blog 7",
                index: 7,
            },
        ],
    },
    getters: {
      blogTextById: (state) => (index) => {
         let foundText;
         state.cards.forEach((card) => { 
           // cast both indexes to Number, so that we won't run into 
           //unexpected type-mismatch. Since index might be coming 
           // from an URL, it's likely that it is stored as a splitted 
           // string, holding only a number. 
           // e.g.: index = "2" instead of index = 2 
           if (Number(card.index) === Number(index)) foundText = card.text;
         });
         return foundText;
      },
    },
});
 

ViewBlogComponent

 <template>
  <div class="post">
      <h1>{{ $route.params.title }}</h1>
      <p>{{ blogText }}</p>
  </div>
</template>

<script>
import { mapGetters } from 'vuex';

export default {
  computed: {
    ...mapGetters({
      blogTextGetter: 'blogTextById',
    }),
    blogText() {
      return this.blogTextGetter(this.$route.params.id);
    },
  },
}
</script>
 

Подход 2: передайте идентификатор в качестве реквизита, получите целую карточку

Этот подход требует еще нескольких изменений, некоторые из которых изменяют ваш общий подход к использованию параметров маршрута. Мы бы ввели новый реквизит для вашего viewBlogPage , который представляет только id || index блог для просмотра. Затем мы используем этот идентификатор для извлечения всего элемента карты и отображаем все данные, которые мы хотим отобразить.

В вашем LibraryCardComponent вы перегружаете вызов маршрута title параметром, что по сути не является плохой практикой, но ее трудно выполнить, не зная об этом конкретном поведении. Это также обеспечивает тесную связь между компонентом и $route объектом. Но поскольку вы используете его в своем beforeEach хуке, я оставлю его там.

Итак, что нам делать?

Мы изменяем определение маршрута Post , чтобы включить передачу реквизитов компонентам маршрута

Определения маршрутов

 const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    component: () => import('../views/About.vue')
  },
  {
    path: '/post/:id',
    name: 'Post',
    // Here we enable the passing of props.
    // The boolean mode passes all params defined as props with the same name as the param
    // So :id will be available as prop 'id' in the Component.
    props: true,
    component: () => import('../views/Post.vue')
  }
]
 

Теперь мы должны ввести prop в наш компонент для последующего использования.

ViewBlogPage

 <template>
  <div class="post">
      <h1>{{ blogPost.title }}</h1>
      <p>{{ blogPost.text }}</p>
  </div>
</template>
   
<script>
import { mapGetters } from 'vuex';

export default {
  props: {
    id: {
      required: true,
    }
  },
  computed: {
    ...mapGetters({
      blogById: 'blogById',
    }),
    blogPost() {
      return this.blogById(this.id);
    },
  },
}
</script>
 

И получатель из первого подхода может быть записан как getBlog Getter вместо getBlogText Getter

Получатели VueX

 import { createStore } from 'vuex';

export default createStore({
    state: {
        cards: [{
                title: "Blog 1",
                text: "This is blog 1",
                index: 1,
            },
            {
                title: "Blog 2",
                text: "This is blog 2",
                index: 2,
            },
            {
                title: "Blog 3",
                text: "This is blog 3",
                index: 3,
            },
            {
                title: "Blog 4",
                text: "This is blog 4",
                index: 4,
            },
            {
                title: "Blog 5",
                text: "This is blog 5",
                index: 5,
            },
            {
                title: "Blog 6",
                text: "This is blog 6",
                index: 6,
            },
            {
                title: "Blog 7",
                text: "This is blog 7",
                index: 7,
            },
        ],
    },
    getters: {
      blogById: (state) => (index) => {
         // cast both to Number() to prevent unexpected type-mismatch
         return state.cards.find(c => Number(c.index) === Number(index));
      },
    },
});
 

При таком подходе ваш title и text отображение больше не тесно связано с параметрами маршрута, а реактивно связано с вашим состоянием из vueX.

Я надеюсь, что один из подходов соответствует вашим потребностям.

Комментарии:

1. пока не работает…

2. Я не получаю ошибок, но они просто не отображаются в браузере

3. Я посмотрю на это, дай мне минутку. @EmielVandeVeire

4. большое спасибо, надеюсь, вы найдете решение!

5. @EmielVandeVeire Я включил этот код в существующий проект, и он работает. Не могли бы вы, пожалуйста, попытаться сузить область поиска, какая часть не работает? Попробуйте вызвать средство получения с жестко заданным индексом 1, например. И прежде всего: попробуйте это в функции получения : if (Number(card.index) === Number(index)) . Возможно, это работает так, как ожидалось, но индекс из хранилища или параметры маршрута могут иметь разный тип. Я могу себе представить, что параметр из маршрута в какой-то момент преобразуется в строку, поскольку он происходит из строки.