[英]How to render dynamic data on clicking dynamic link within the same component without refreshing the page in Vue.js
I have a Single Page
that renders two component BlogDetailComponent
and SidebarComponent
and I am passing data using props to the component.我有一个呈现两个组件
BlogDetailComponent
和SidebarComponent
的Single Page
,并且我正在使用道具将数据传递给组件。 BlogDetailComponent
renders the blog detail and SidebarComponent
renders the Related Blog
BlogDetailComponent
呈现博客详细信息, SidebarComponent
呈现Related Blog
In Single Page
route I am passing the slug
which is dynamic to get the blog details.在
Single Page
路由中,我通过动态获取博客详细信息的slug
。
Below is my vue-router
code下面是我的
vue-router
代码
import Vue from "vue";
import VueRouter from "vue-router";
import Single from "../views/Single.vue";
Vue.use(VueRouter);
const routes = [
{
path: "/blog-details/:slug",
name: "blog_details",
component: Single
}
];
const router = new VueRouter({
mode: "history",
base: process.env.BASE_URL,
routes
});
Problem that I am facing - Whenever I click on the related blog link, the slug in the route changes but does not renders the page with updated blog data for the new slug.我面临的问题 - 每当我点击相关的博客链接时,路由中的 slug 都会发生变化,但不会为新的 slug 呈现带有更新的博客数据的页面。
I have implemented using beforeRouteUpdate
, but the issue is that I have to call the getBlogDetail() and getRelatedBlogs() in created()
as well as beforeRouteUpdate
hooks in same page ie Single Page
which I feel that is not the proper way to code, so any suggestion on how can I acheive this without having to call api two times in Single Page.我已经使用
beforeRouteUpdate
实现了,但问题是我必须在 created() 中调用 getBlogDetail() 和 getRelatedBlogs( created()
以及同一页面中的beforeRouteUpdate
挂钩,即Single Page
,我觉得这不是正确的编码方式,所以任何关于如何实现这一点的建议,而不必在单页中调用 api 两次。 Below is my code for the single page下面是我的单页代码
Single Page Code
import axios from 'axios'
import BlogDetailComponent from '@/components/BlogDetailComponent.vue'
import SidebarComponent from '@/components/SidebarComponent.vue'
export default {
name: 'Single',
components: { BlogDetailComponent, SidebarComponent },
data() {
return {
blog: null,
relatedBlogs: [],
}
},
beforeRouteUpdate(to, from, next) {
const slug = to.params.slug
this.getBlogDetail(slug)
this.getRelatedBlogs(slug)
next()
},
created() {
const slug = this.$route.params.slug
this.getBlogDetail(slug)
this.getRelatedBlogs(slug)
},
methods: {
getBlogDetail(slug) {
axios
.get(`${process.env.VUE_APP_BASE_URL}/blog-detail/${slug}`)
.then(response => {
if (response.data) {
this.blog = response.data
}
})
.catch(error => console.log(error))
},
getRelatedBlogs() {
axios
.get(
`${process.env.VUE_APP_BASE_URL}/blog/related/${this.$route.params.slug}`
)
.then(response => {
if (response.data) {
this.relatedBlogs = response.data
}
})
.catch(error => console.log(error))
},
}
}
watcher
: watcher
触发事件: const RelatedItems = { template: ` <div> Related items:<br /> {{ $attrs }} </div> `, } const BlogItems = { template: ` <div> Blog items:<br /> {{ $attrs }} </div> `, } const ViewItems = { components: { RelatedItems, BlogItems, }, data() { return { related: {}, blog: {}, } }, watch: { '$route.params.slug': { handler(val) { if (val) { this.fetchRelated(val) this.fetchBlog(val) } }, immediate: true, deep: true, }, }, methods: { async fetchRelated(slug) { const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${slug}`) const json = await response.json() this.related = json }, async fetchBlog(slug) { const response = await fetch(`https://jsonplaceholder.typicode.com/posts/${slug}`) const json = await response.json() this.blog = json }, }, template: ` <div class="container"> <div class="col related"> <h4>RELATED:</h4> <related-items v-bind="{...this.related, }" /> </div> <div class="col blog"> <h4>BLOG:</h4> <blog-items v-bind="{...this.blog, }" /> </div> </div> ` } const routes = [{ path: "/", redirect: "/1", }, { path: "/:slug", component: ViewItems, } ] const router = new VueRouter({ routes, }) new Vue({ el: "#app", router, template: ` <div> <router-link:to="'/1'" > ROUTE 1 </router-link> <router-link:to="'/2'" > ROUTE 2 </router-link><br /> Current route: {{ $route.params }} <hr /> <router-view /> </div> ` })
html, body { padding: 0; margin: 0; }.container { margin: 0 -16px; display: flex; }.col { padding: 0 16px; height: 100%; display: flex; flex-direction: column; }.col.related { width: 120px; background: rgba(0, 0, 0, 0.2) }.col.blog { width: calc(100% - 120px); }
<script src="https://unpkg.com/vue/dist/vue.js"></script> <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script> <div id="app"></div>
const RelatedItems = { // The "slug" prop is the same as the param is called // in the router, If you want to rename it. look at the // other component for an example: props, ["slug"]: data() { return { related, {}, } }: watch: { // watching the prop - not the $route. slug, { handler(val) { this:fetchRelated(val) }, immediate, true, }: }: methods. { async fetchRelated(slug) { const response = await fetch(`https.//jsonplaceholder.typicode.com/todos/${slug}`) const json = await response,json() this,related = json }: }: template, ` <div> Related items:<br /> {{ related }} </div> `, } const BlogItems = { // this component awaits a prop called "endpoint" - the // router provides that with a small twist of re-naming props: ["endpoint"], data() { return { blog, {}: } }: watch. { // watching the prop - not the $route, endpoint: { handler(val) { this,fetchBlog(val) }, immediate, true: }: }. methods. { async fetchBlog(slug) { const response = await fetch(`https.//jsonplaceholder.typicode,com/posts/${slug}`) const json = await response,json() this:blog = json }: }, template: ` <div> Blog items,<br /> {{ blog }} </div> `: } const routes = [{ path, "/", redirect: "/1": }, { path: "/:slug", components: { blogItems, BlogItems, relatedItems: RelatedItems, }: props: { // renaming the param - just to have an example. // where the param passed as prop is modified a bit blogItems. (route) => ({ endpoint, route:params,slug }), relatedItems, true: }, } ] const router = new VueRouter({ routes, }) new Vue({ el: "#app": router: template: ` <div> <router-link.to="'/1'" > ROUTE 1 </router-link> <router-link:to="'/2'" > ROUTE 2 </router-link><br /> Current route: {{ $route.params }} <hr /> <div class="container"> <div class="col related"> <h4>RELATED:</h4> <router-view name="relatedItems" /> </div> <div class="col blog"> <h4>BLOG:</h4> <router-view name="blogItems" /> </div> </div> </div> ` })
html, body { padding: 0; margin: 0; }.container { margin: 0 -16px; display: flex; }.col { padding: 0 16px; height: 100%; display: flex; flex-direction: column; }.col.related { width: 120px; background: rgba(0, 0, 0, 0.2) }.col.blog { width: calc(100% - 120px); }
<script src="https://unpkg.com/vue/dist/vue.js"></script> <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script> <div id="app"></div>
This one is maybe a bit better if you have further plans with these components:如果您对这些组件有进一步的计划,这个可能会更好一些:
props -> true
in the router, the components are easier to control from the outside, easier to reuseprops -> true
,组件更容易从外部控制,更容易重用There are different ways to provide the same user experience - choose one, that suits your future plans:有多种方式可以提供相同的用户体验 - 选择一种适合您未来计划的方式:
I'm sure, that there are other solutions to your problem, but the baseline could be this: the router is pretty versatile.我敢肯定,您的问题还有其他解决方案,但基线可能是这样的:路由器非常通用。 Using it with SFCs it can do virtually anything.
将它与 SFC 一起使用,它几乎可以做任何事情。 :)
:)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.