如何使用vue路由到不同的页面并显示它们的标题和文字?

How to use vue to route to different pages and display their title and text?

我正在用 vue 制作一个 post 网站。 这是我的问题:

有 2 个页面,一个用于所有博客,另一个用于查看博客。 我正在使用 vuex。 当我点击其中一个博客时,我被转到查看博客页面,我看到了正确博客的标题,所以没问题。

我想做的事情,看似简单。 我想要一个包含两个项目的 viewblog 页面: 标题和文本 。这两个应该来自store(vuex).

这是目前的查看博客页面:

<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-router:

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

谢谢你的帮助!

方法一:通过路由参数传递

您可以定义一个 getter 函数来 select 给定的 Blogtext。 将 blog-route:id 作为 param 传递,以便您可以将其用作 getter 中的过滤器。 然后在 getter 中定义一个简单的过滤器逻辑。我用 forEach 做到了,但是还有其他选项可以过滤数组,选择一个适合您需要的选项。

component 中,您可以映射 getter,并在计算的 属性 中使用通过路由传递的 id 调用它,瞧,它应该 return 该特定博客的文本 post.

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>

方法二:通过id作为prop,获取整张卡片

此方法需要进行更多更改,其中一些更改会改变您使用路由参数的一般方法。 我们将为您的 viewBlogPage 引入一个新属性,它仅代表要查看的博客的 id || index。然后我们使用该 ID 获取整个卡片项目并显示我们想要显示的所有数据。

在您的 LibraryCardComponent 中,您使用 title 参数重载了路由调用,这在本质上并不是不好的做法,但如果不了解此特定行为,则很难遵循。它还在组件和 $route 对象之间引入了紧密耦合。但是既然你在你的 beforeEach 钩子中使用它,我会让它留在那里。

那我们怎么办?

我们更改 Post 的路由定义以启用 Passing Props to Route Components

路由定义

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 引入到我们的组件中以备后用。

查看博客页面

<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>

方法一中的 getter 可以写成 getBlog Getter 而不是 getBlogText Getter

VueX Getters

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

通过这种方法,您的 titletext 显示不再与路由参数紧密耦合,而是与 vueX 的状态反应耦合。

我希望其中一种方法能满足您的需求。