Pir*_*App 5 master-detail vue.js vue-router vuejs2
我在 Nuxt SSR 中使用 Vue Router,我想实现如上所示的结构。
在桌面上,我想在单个页面上渲染嵌套子路由,其中左侧有项目列表,右侧有项目的详细信息,可以像这样完成
export function createRouter() {
return new Router({
mode: "history",
routes: [
{
path: "/news/:tag?",
name: "TaggedNews",
component: Index,
children: [
{
path: "/news/:tag/:id/:title",
name: "TaggedNewsItem",
component: Detail,
props: true
}
]
}
]
});
}
Run Code Online (Sandbox Code Playgroud)
在移动设备上,我想要第 1 页上的列表和第 2 页上的详细信息。
路由器需要看起来像这样
export function createRouter() {
return new Router({
mode: "history",
routes: [
{
path: "/news/:tag?",
name: "TaggedNews",
component: Index
},
{
path: "/news/:tag/:id/:title",
name: "TaggedNewsItem",
component: Detail,
props: true
}
]
});
}
Run Code Online (Sandbox Code Playgroud)
我如何使用 vue 路由器做到这一点?
这是我的 Codesandbox 正如您在我的沙箱上看到的,它在桌面上完美运行,但在移动设备上,详细信息页面不会显示
桌面
我终于想出了如何做到这一点。方法不止一种,让我在这里分享每种方法
方法 1 使用编程式路线导航,所有内容均在同一页面上
这种方法的想法是只有一个页面可以处理列表视图和详细信息视图。路由器定义了两条单独的路由,它们都指向同一页面。每当路由发生变化时,我们都会对其进行监控,以便找到新路由的名称,并根据该名称动态加载列表或详细信息组件
路由器.js
import Vue from "vue";
import Router from "vue-router";
import Index from "~/pages/index";
Vue.use(Router);
export function createRouter() {
return new Router({
mode: "history",
routes: [
{
path: "/news/:tag?",
alias: "/",
name: "NewsList",
component: Index,
props: true
},
{
path: "/news/:tag?/:id([a-fA-F\\d]{32})/:title",
name: "NewsDetail",
component: Index,
props: true
}
]
});
}
Run Code Online (Sandbox Code Playgroud)
索引.vue
<template>
<div class="news__container">
<template v-if="isMobile">
<component :is="current"></component>
</template>
<template v-else>
<div class="left">
<news-list></news-list>
</div>
<div class="right">
<news-detail></news-detail>
</div>
</template>
</div>
</template>
<script>
import NewsList from "~/components/NewsList";
import NewsDetail from "~/components/NewsDetail";
export default {
name: "root",
components: { NewsList, NewsDetail },
data: () => ({
isMobile: false,
}),
beforeDestroy() {
if (typeof window !== "undefined") {
window.removeEventListener("resize", this.onResize, { passive: true });
}
},
computed: {
current() {
return this.$route.name === "NewsList" ? NewsList : NewsDetail;
},
},
mounted() {
this.onResize();
window.addEventListener("resize", this.onResize, { passive: true });
},
watch: {
$route: {
immediate: true,
handler(newRoute) {
// Set name of the current route inside a variable
// Use this variable as a computed property to dynamically load the <component> on mobile view
this.current = newRoute.name;
},
},
},
methods: {
onResize() {
this.isMobile = window.innerWidth < 768;
},
},
};
</script>
<style lang="scss" scoped>
.news__container {
display: flex;
}
.left {
flex: 1;
}
.right {
flex: 1;
}
</style>
Run Code Online (Sandbox Code Playgroud)
方法 2 创建列表作为父项,创建详细信息作为在移动设备上隐藏的 nuxt 子项,并创建仅在移动设备上显示的单独详细信息页面(重复)
这里创建的 router.js 使得新闻列表页面是父页面,新闻详细信息页面是子页面。在桌面上,列表和详细信息页面并排显示。在移动设备上,详细信息页面被隐藏,并显示另一个仅移动设备的详细信息页面。因此,在此方法中详细信息页面被复制两次。除了重复之外,这种方法的另一个问题是移动端的 NewsDetail 页面可以在桌面上直接访问
路由器.js
import Vue from "vue";
import Router from "vue-router";
import Index from "~/pages/index";
import Detail from "~/pages/detail";
Vue.use(Router);
export function createRouter() {
return new Router({
mode: "history",
routes: [
{
path: "/news/:tag?",
name: "TaggedNews",
component: Index,
alias: "/",
children: [
{
path: "/news/:tag?/:id([a-fA-F\\d]{32})/:title",
name: "TaggedNewsItemDesktop",
component: Detail,
props: true
}
]
},
{
path: "/news/:tag?/:id([a-fA-F\\d]{32})/:title",
name: "TaggedNewsItemMobile",
component: Detail,
props: true
}
]
});
}
Run Code Online (Sandbox Code Playgroud)
Index.vue 页面使用窗口调整大小侦听器来设置变量 isMobile,该变量对于宽度 < 768 为 true
索引.vue
<template>
<div class="news__container">
<div class="left">
<news-list :is-mobile="isMobile" />
</div>
<div v-if="!isMobile" class="right">
<nuxt-child></nuxt-child>
</div>
</div>
</template>
<script>
import NewsList from "~/components/NewsList";
export default {
name: "root",
components: { NewsList },
data: () => ({
isMobile: false,
}),
beforeDestroy() {
if (typeof window !== "undefined") {
window.removeEventListener("resize", this.onResize, { passive: true });
}
},
mounted() {
this.onResize();
window.addEventListener("resize", this.onResize, { passive: true });
},
methods: {
onResize() {
this.isMobile = window.innerWidth < 768;
},
},
};
</script>
<style lang="scss" scoped>
.news__container {
display: flex;
height: 100%;
}
.left {
flex: 1;
}
.right {
flex: 1;
}
</style>
Run Code Online (Sandbox Code Playgroud)
新闻列表始终以这种方式显示。详细信息仅显示在桌面上。手机版NewsDetail页面如下图
新闻详情.vue
<template>
<news-detail :tag="tag" :id="id" :title="title" />
</template>
<script>
import NewsDetail from "~/components/NewsDetail";
export default {
components: { NewsDetail },
props: {
tag: {
type: String,
required: true,
default: "",
},
id: {
type: String,
required: true,
default: "",
},
title: {
type: String,
required: true,
default: "",
},
},
};
</script>
Run Code Online (Sandbox Code Playgroud)
方法 3 作为子项创建列表和详细信息
创建 router.js,其中新闻页面将 NewsList 和 NewsDetail 页面作为子页面
路由器.js
import Vue from "vue";
import Router from "vue-router";
import Index from "~/pages/index";
import NewsList from "~/pages/NewsList";
import NewsDetail from "~/pages/NewsDetail";
Vue.use(Router);
export function createRouter() {
return new Router({
mode: "history",
routes: [
{
path: "/news/:tag?",
alias: "/",
component: Index,
children: [
{
path: "",
name: "NewsList",
component: NewsList,
props: true
},
{
path: "/news/:tag?/:id([a-fA-F\\d]{32})/:title",
name: "NewsDetail",
component: NewsDetail,
props: true
}
]
}
]
});
}
Run Code Online (Sandbox Code Playgroud)
Index.vue 文件添加了一个窗口调整大小侦听器,每次屏幕尺寸发生变化时都会调用 onResize 方法。在此方法中,如果屏幕宽度 < 768,我们将变量 isMobile 设置为 true。现在,只要 isMobile 为 true,我们就会根据路由显示适当的 nuxt 子项,并在桌面上并排显示列表和详细信息组件,而不使用 nuxt 子项。简单的想法是,页面 NewsList 和 NewsDetail 根据移动视图上的路线显示,而桌面视图并排加载这两个组件
索引.vue
<template>
<div class="news__container">
<template v-if="isMobile">
<div class="left">
<nuxt-child></nuxt-child>
</div>
</template>
<template v-else>
<div class="left">
<app-news-list />
</div>
<div class="right">
<app-news-detail
:tag="$route.params.tag"
:id="$route.params.id"
:title="$route.params.title"
></app-news-detail>
</div>
</template>
</div>
</template>
<script>
import AppNewsList from "~/components/AppNewsList";
import AppNewsDetail from "~/components/AppNewsDetail";
export default {
name: "root",
components: { AppNewsList, AppNewsDetail },
data: () => ({
isMobile: false,
}),
beforeDestroy() {
if (typeof window !== "undefined") {
window.removeEventListener("resize", this.onResize, { passive: true });
}
},
computed: {
current() {
return this.$route.name === "Index" || this.$route.name === "AppNewsList"
? AppNewsList
: AppNewsDetail;
},
},
mounted() {
this.onResize();
window.addEventListener("resize", this.onResize, { passive: true });
},
methods: {
onResize() {
this.isMobile = window.innerWidth < 768;
},
},
};
</script>
<style lang="scss" scoped>
.news__container {
display: flex;
}
.left {
flex: 1;
}
.right {
flex: 1;
}
</style>
Run Code Online (Sandbox Code Playgroud)
NewsList 页面仅加载显示新闻项列表的组件,而 NewsDetail 页面仅加载详细信息组件
新闻列表.vue
<template>
<app-news-list />
</template>
<script>
import AppNewsList from "~/components/AppNewsList";
export default {
name: "NewsList",
components: { AppNewsList },
};
</script>
Run Code Online (Sandbox Code Playgroud)
新闻详情.vue
<template>
<app-news-detail :tag="tag" :id="id" :title="title" />
</template>
<script>
import AppNewsDetail from "~/components/AppNewsDetail";
export default {
name: "NewsDetail",
components: { AppNewsDetail },
props: {
tag: { type: String, required: true, default: "" },
id: { type: String, required: true, default: "" },
title: { type: String, required: true, default: "" },
},
};
</script>
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1517 次 |
最近记录: |