簡體   English   中英

如何使用Vue Router在桌面上嵌套子頁面但在移動設備上單獨頁面?

[英]How to do nested child on Desktop but separate pages on mobile using Vue Router?

在此處輸入圖片說明

我在 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
          }
        ]
      }
    ]
  });
}

在移動設備上,我想要第 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
      }
    ]
  });
}

我如何使用 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
      }
    ]
  });
}

索引.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>

方法 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
      }
    ]
  });
}

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>

NewsList 總是以這種方式顯示。 詳細信息僅顯示在桌面上。 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>

方法 3 創建列表和細節作為孩子

創建 router.js,其中 News 頁面同時包含 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
          }
        ]
      }
    ]
  });
}

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>

頁面 NewsList 只是加載一個顯示新聞列表的組件,而 NewsDetail 只是加載一個詳細信息組件

新聞列表.vue

<template>
  <app-news-list />
</template>

<script>
import AppNewsList from "~/components/AppNewsList";
export default {
  name: "NewsList",
  components: { AppNewsList },
};
</script>

新聞詳情.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>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM