Bagaimana cara mendorong vue-router tanpa menambah histori?

12

Saya memiliki urutan berikut yang terjadi:

  • Layar utama

  • Memuat layar

  • Layar hasil

Di beranda, ketika seseorang mengklik tombol, saya mengirimnya ke layar pemuatan, melalui:

this.$router.push({path: "/loading"});

Dan begitu tugas mereka selesai, mereka dikirim ke layar hasil melalui

this.$router.push({path: "/results/xxxx"});

Masalahnya adalah, biasanya mereka ingin beralih dari hasil kembali ke layar utama, tetapi ketika mereka mengklik kembali, mereka dikirim ke memuat lagi yang mengirim mereka kembali ke hasil, sehingga mereka terjebak dalam loop tak terbatas & tidak dapat pergi kembali ke layar utama.

Ada ide bagaimana cara memperbaikinya? Idealnya saya suka jika ada opsi seperti:

this.$router.push({path: "/loading", addToHistory: false});

yang akan mengirim mereka ke rute tanpa menambahkannya ke riwayat.

Klik Upvote
sumber
5
this.$router.replace({path: "/results/xxxx"})
Roland Starke
@RolandStarke Terima kasih - ini membuat tombol kembali bekerja & kembali ke menu utama, tapi saya kehilangan tombol maju & tidak bisa maju lagi - ada ide?
Klik Upvote
Prosesnya adalah: Menu utama, Memuat, Hasil. Saya menelepon $router.replacedari Memuat. Ini sekarang memungkinkan saya kembali dari Hasil -> Utama tetapi saya kemudian tidak dapat melanjutkan ke hasil.
Klik Upvote
Pilihan lain adalah tidak memiliki rute pemuatan. Alih-alih mendorong langsung ke rute hasil yang diambil data pada pembuatan, dan kemudian membuat tampilan pemuatan sampai selesai. Maka tidak ada perutean ulang dan riwayat dan aliran pengguna harus tetap utuh tanpa $ router.replace.
ArrayKnight
@ClickUpvote apakah Anda menemukan solusi untuk masalah ini ...
chans

Jawaban:

8

Ada cara sempurna untuk menangani situasi ini

Anda dapat menggunakan pelindung dalam-komponen untuk mengontrol rute di tingkat granul

Buat perubahan berikut dalam kode Anda

Di komponen layar utama

Tambahkan penjaga beofreRouteLeave ini dalam opsi komponen, sebelum pergi ke 'layar hasil' Anda mengatur rute untuk pergi hanya melalui layar memuat

beforeRouteLeave(to, from, next) {
   if (to.path == "/result") {
      next('/loading')
    }
    next();
  }, 

Dalam memuat komponen layar

Jika rute kembali dari hasil ke pemuatan, rute tersebut seharusnya tidak mendarat di sini dan langsung melompat ke layar utama

beforeRouteEnter(to, from, next) {
    if (from.path == "/result") {
      next('/main')
    }
     next();
  },

Di layar memuat, Penjaga beforeRouteEnter TIDAK memiliki akses ke ini, karena penjaga dipanggil sebelum navigasi dikonfirmasi, sehingga komponen yang masuk baru bahkan belum dibuat. Jadi, manfaatkan ini, Anda tidak akan mendapatkan panggilan tak terbatas yang diputuskan saat merutekan dari layar hasil

Dalam komponen layar hasil

jika Anda menggunakan kembali maka seharusnya tidak mendarat di pemuatan dan langsung melompat ke layar utama

beforeRouteLeave(to, from, next) {
    if (to.path == "/loading") {
      next('/')
    }
    next();
  },

Saya baru saja membuat aplikasi vue kecil untuk mereproduksi masalah yang sama. Ini berfungsi di lokal saya sesuai pertanyaan Anda. Semoga ini bisa menyelesaikan masalah Anda juga.

nyanyian
sumber
2
Solusi ini rapuh (memperkenalkan utang teknologi pemeliharaan) dan membutuhkan entri untuk setiap rute yang mungkin Anda perlukan untuk fungsi ini.
ArrayKnight
Sepenuhnya setuju, saya tidak suka solusi ini sama sekali
omarjebari
2

Saya kira router.replaceadalah cara untuk pergi - tetapi masih beberapa garis pemikiran (belum diuji):


Pada dasarnya pada $routerperubahan itu membuat komponen pemuatan sampai memancarkan load:stop, kemudian ia membuatrouter-view


import { Vue, Component, Watch, Prop } from "vue-property-decorator";

@Component<RouteLoader>({
    render(h){
        const rv = (this.$slots.default || [])
        .find(
            child => child.componentOptions
            //@ts-ignore 
            && child.componentOptions.Ctor.extendedOptions.name === "RouterView"
        )
        if(rv === undefined) 
            throw new Error("RouterView is missing - add <router-view> to default slot")

        const loader = (this.$slots.default || [])
        .find(
            child => child.componentOptions
            //@ts-ignore 
            && child.componentOptions.Ctor.extendedOptions.name === this.loader
        )
        if(loader === undefined) 
            throw new Error("LoaderView is missing - add <loader-view> to default slot")
        const _vm = this 
        const loaderNode = loader.componentOptions && h(
            loader.componentOptions.Ctor,
            {
                on: {
                    // "load:start": () => this.loading = true,
                    "load:stop": () => _vm.loading = false
                },
                props: loader.componentOptions.propsData,
                //@ts-ignore
                attrs: loader.data.attrs
            }
        )
        return this.loading && loaderNode || rv
    }
})
export default class RouteLoader extends Vue {
    loading: boolean = false
    @Prop({default: "LoaderView"}) readonly loader!: string
    @Watch("$route")
    loads(nRoute: string, oRoute: string){
        this.loading = true
    }
}

@Component<Loader>({
    name: "LoaderView",
    async mounted(){

        await console.log("async call")
        this.$emit("load:stop")
        // this.$destroy()
    }
})
export class Loader extends Vue {}
Estradiaz
sumber
Ini adalah jenis implementasi yang saya bicarakan, meskipun saya cenderung mengimplementasikan komponen pemuatan saya langsung ke komponen halaman saya daripada membuat bungkus menonton rute. Alasannya adalah: Saya suka menggunakan pendekatan kerangka di mana komponen konten dimuat ke dalam kondisi semi-load untuk memberikan umpan balik visual kepada pengguna tentang apa yang diharapkan dan kemudian melapisi komponen pemuatan (jika perlu memblokir semua tindakan) atau sebariskan jika pengguna dapat menggunakan UI saat memuat data. Ini semua tentang persepsi pengguna tentang kecepatan dan membuka blokir kemampuan mereka untuk melakukan apa yang mereka inginkan secepat mungkin.
ArrayKnight
@ArrayKnight berpikir abstrak satu hanya dapat bertukar komponen router-view dengan komponen rute yang sesuai dan mendapat semacam pembungkus loader - tapi saya setuju menempatkan loader ke rute terasa seperti desain yang buruk
Estradiaz
2

Ini adalah panggilan yang sulit mengingat sedikitnya yang kita ketahui tentang apa yang terjadi dalam rute pemuatan Anda.

Tapi...

Saya tidak pernah memiliki kebutuhan untuk membangun rute pemuatan, hanya komponen pemuatan yang akan diberikan pada beberapa rute selama tahap pengumpulan data.

Satu argumen untuk tidak memiliki rute pemuatan adalah bahwa pengguna berpotensi menavigasi langsung ke URL ini (secara tidak sengaja) dan kemudian sepertinya tidak akan memiliki konteks yang cukup untuk mengetahui ke mana harus mengirim pengguna atau tindakan apa yang harus diambil. Meskipun ini bisa berarti bahwa ia jatuh ke rute kesalahan pada titik ini. Secara keseluruhan, bukan pengalaman hebat.

Lain adalah bahwa jika Anda menyederhanakan rute Anda, navigasi antar rute menjadi lebih sederhana dan berperilaku seperti yang diharapkan / diinginkan tanpa menggunakan $router.replace .

Saya mengerti ini tidak menyelesaikan pertanyaan dengan cara Anda bertanya. Tapi saya sarankan memikirkan kembali rute pemuatan ini.

Aplikasi

<shell>
    <router-view></router-view>
</shell>

const routes = [
  { path: '/', component: Main },
  { path: '/results', component: Results }
]

const router = new VueRouter({
  routes,
})

const app = new Vue({
  router
}).$mount('#app')

Kulit

<div>
    <header>
        <nav>...</nav>
    </header>
    <main>
        <slot></slot>
    </main>
</div>

Halaman Utama

<section>
    <form>...</form>
</section>

{
    methods: {
        onSubmit() {
            // ...

            this.$router.push('/results')
        }
    }
}

Halaman Hasil

<section>
    <error v-if="error" :error="error" />
    <results v-if="!error" :loading="loading" :results="results" />
    <loading v-if="loading" :percentage="loadingPercentage" />
</section>

{
    components: {
        error: Error,
        results: Results,
    },
    data() {
        return {
            loading: false,
            error: null,
            results: null,
        }
    },
    created () {
        this.fetchData()
    },
    watch: {
        '$route': 'fetchData'
    },
    methods: {
        fetchData () {
            this.error = this.results = null
            this.loading = true

            getResults((err, results) => {
                this.loading = false

                if (err) {
                    this.error = err.toString()
                } else {
                    this.results = results
                }
            })
        }
    }
}

Komponen Hasil

Pada dasarnya komponen hasil yang sama persis sudah Anda miliki, tetapi jika loadingitu benar, atau jika resultsadalah nol, bagaimanapun Anda suka, Anda dapat membuat dataset palsu untuk beralih dan membuat versi kerangka, jika Anda mau. Kalau tidak, Anda bisa menyimpan barang-barang seperti yang Anda miliki.

ArrayKnight
sumber
Layar memuat yang saya miliki memakan seluruh layar, jadi saya tidak bisa memikirkan cara untuk menampilkannya tanpa memiliki rute sendiri. Lihat naminator.io untuk demo langsung. Terbuka untuk ide lain untuk melakukan ini.
Klik Upvote
Saya akan memberikan tampilan yang tepat di pagi hari, tetapi pemikiran awal saya adalah tidak ada alasan Anda tidak dapat menggunakan posisi tetap untuk mengambil alih layar.
ArrayKnight
Melihat desain Anda, sepertinya ada beberapa opsi: Anda dapat membuat loader sebagai pengganti konten hasil dan kemudian beralih setelah data diambil; atau Anda dapat overlay loader kerangka hasil Anda dan kemudian memperbarui dan mengisi hasilnya dan menghapus loader. Menimbang bahwa pemuat tidak menutupi tajuk (shell situs) Anda tidak perlu posisi diperbaiki.
ArrayKnight
@ClickUpvote beri tahu saya bagaimana perasaan Anda tentang pendekatan ini.
ArrayKnight
@ ArrayKnight, pertanyaannya adalah rute sudah diterapkan dan berfungsi seperti yang diharapkan, pendekatan pemuatan ini mungkin tidak cocok di sini, saya telah melalui situasi yang sama. Rute pemuatan juga dapat memiliki permintaan pos formulir seperti gateway pembayaran atau beberapa tindakan pos formulir. Dalam skenario ini kita tidak bisa terdiri dari menghapus rute. Namun masih dalam level router vue yang dapat kita miliki sebelum masuk penjaga, Mengapa saya menyarankan pendekatan ini adalah, contoh komponen loading vue belum dibuat di hook ini dan keuntungannya untuk memutuskan apakah akan memasuki rute ini atau tidak berdasarkan rute sebelumnya
chans
1

Pilihan lain adalah menggunakan API Sejarah .

Setelah Anda berada di layar Hasil, Anda dapat menggunakan ReplaceState untuk mengganti URL dalam riwayat browser.

Angelo
sumber
0

Ini bisa dilakukan dengan beforeEachkait dari router.

Yang perlu Anda lakukan adalah Anda harus menyimpan variabel secara global atau di localStorage di loadingkomponen saat data dimuat (sebelum mengarahkan ke resultskomponen):

export default {
  name: "results",
  ...
  importantTask() {
    // do the important work...
    localStorage.setItem('DATA_LOADED', JSON.stringify(true));
    this.$router.push({path: "/results/xxxx"});
  }
}

Dan kemudian Anda harus memeriksa variabel ini di hook sebelumEach dan lompat ke komponen yang benar:

// router.js...
router.beforeEach((to, from, next) => {
  const dataLoaded = JSON.parse(localStorage.getItem('DATA_LOADED'));
  if (to.name === "loading" && dataLoaded)
  {
    if (from.name === "results")
    {
      next({ name: "main"} );
    }
    if (from.name === "main")
    {
      next({ name: "results"} );
    }
  }
  next();
});

Juga, jangan lupa untuk mereset nilai ke false di mainkomponen Anda ketika tombol kueri diklik (sebelum merutekan ke loadingkomponen):

export default {
  name: "main",
  ...
  queryButtonClicked() {
    localStorage.setItem('DATA_LOADED', JSON.stringify(false));
    this.$router.push({path: "/loading"});
  }
}
Yogesh
sumber
Solusi ini rapuh (memperkenalkan utang teknologi pemeliharaan) dan membutuhkan entri untuk setiap rute yang mungkin Anda perlukan untuk fungsi ini.
ArrayKnight
0

Layar pemuatan Anda seharusnya tidak dikontrol oleh vue-router sama sekali. Pilihan terbaik adalah menggunakan modal / hamparan untuk layar pemuatan Anda, dikendalikan oleh javascript. Ada banyak contoh untuk vue. Jika Anda tidak dapat menemukan apa pun maka vue-bootstrap memiliki beberapa contoh mudah untuk diterapkan.

omarjebari
sumber