วิธีการผลักดันไปยัง vue-router โดยไม่ต้องเพิ่มประวัติ


12

ฉันมีลำดับต่อไปนี้เกิดขึ้น:

  • หน้าจอหลัก

  • กำลังโหลดหน้าจอ

  • หน้าจอผลลัพธ์

ในหน้าแรกเมื่อมีคนคลิกปุ่มฉันจะส่งพวกเขาไปยังหน้าจอโหลดผ่าน:

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

และเมื่องานของพวกเขาเสร็จสิ้นพวกเขาจะถูกส่งไปยังหน้าจอผลลัพธ์ผ่านทาง

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

ปัญหาคือพวกเขามักจะต้องการจากผลลัพธ์กลับไปที่หน้าจอหลัก แต่เมื่อพวกเขาคลิกกลับพวกเขาจะถูกส่งไปโหลดอีกครั้งซึ่งจะส่งพวกเขากลับไปที่ผลลัพธ์ดังนั้นพวกเขาจึงติดอยู่ในวงวนไม่สิ้นสุดและไม่สามารถไปได้ กลับไปที่หน้าจอหลัก

แนวคิดใดที่จะแก้ไขปัญหานี้ ฉันชอบถ้ามีตัวเลือกเช่น:

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

ซึ่งจะส่งไปยังเส้นทางโดยไม่ต้องเพิ่มลงในประวัติ


5
this.$router.replace({path: "/results/xxxx"})
Roland Starke

@RolandStarke ขอบคุณ - สิ่งนี้ทำให้ปุ่มย้อนกลับใช้งานได้ & กลับไปที่เมนูหลัก แต่ฉันสูญเสียปุ่มไปข้างหน้า & ไม่สามารถไปข้างหน้าอีกครั้ง - ความคิดใด ๆ
คลิก

กระบวนการคือ: เมนูหลักกำลังโหลดผลลัพธ์ ฉันกำลังโทร$router.replaceจากกำลังโหลด ตอนนี้ให้ฉันย้อนกลับจากผลลัพธ์ -> หลัก แต่ฉันก็ไม่สามารถไปข้างหน้ากับผลลัพธ์ได้
คลิก

ตัวเลือกอื่นจะไม่มีเส้นทางการโหลด ค่อนข้างตรงไปยังเส้นทางผลลัพธ์ที่ดึงข้อมูลจากการสร้างแล้วแสดงผลมุมมองการโหลดจนกว่าจะเสร็จสมบูรณ์ จากนั้นจะไม่มีการกำหนดเส้นทางใหม่และประวัติและการไหลของผู้ใช้ควรยังคงอยู่โดยไม่ต้องใช้ $ router.replace
ArrayKnight

@ClickUpvote คุณไม่หาทางแก้ปัญหาใด ๆ กับเรื่องนี้ ...
Chans

คำตอบ:


8

มีวิธีที่สมบูรณ์แบบในการจัดการสถานการณ์นี้

คุณสามารถใช้ตัวป้องกันแบบคอมโพเนนต์เพื่อควบคุมเส้นทางในระดับแกรนูล

ทำการเปลี่ยนแปลงต่อไปนี้ในรหัสของคุณ

ในองค์ประกอบหน้าจอหลัก

เพิ่ม beofreRouteLeave Guard ในตัวเลือกส่วนประกอบก่อนออกจาก 'หน้าจอผลลัพธ์' คุณกำลังตั้งค่าเส้นทางเพื่อไปยังหน้าจอการโหลดเท่านั้น

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

ในการโหลดองค์ประกอบหน้าจอ

หากเส้นทางย้อนกลับจากผลการโหลดแล้วมันไม่ควรลงจอดที่นี่และข้ามไปยังหน้าจอหลักโดยตรง

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

ในหน้าจอการโหลดป้องกัน beforeRouteEnter ไม่สามารถเข้าถึงสิ่งนี้ได้เนื่องจากมีการเรียกตัวป้องกันก่อนที่การนำทางจะได้รับการยืนยันดังนั้นส่วนประกอบการป้อนใหม่ยังไม่ได้ถูกสร้างขึ้น ดังนั้นการใช้ประโยชน์จากสิ่งนี้คุณจะไม่ได้รับสายที่ไม่สิ้นสุดเมื่อกำหนดเส้นทางจากหน้าจอผลลัพธ์

ในองค์ประกอบหน้าจอผล

หากคุณใช้ย้อนกลับไม่ควรลงจอดและข้ามไปที่หน้าจอหลักโดยตรง

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

ฉันเพิ่งสร้างแอปพลิเคชัน vue ขนาดเล็กเพื่อทำซ้ำปัญหาเดียวกัน มันทำงานในท้องถิ่นของฉันตามคำถามของคุณ หวังว่ามันจะช่วยแก้ปัญหาของคุณเช่นกัน


2
โซลูชันนี้มีความเปราะ (แนะนำหนี้ด้านเทคโนโลยีการบำรุงรักษา) และต้องมีรายการสำหรับเส้นทางที่เป็นไปได้ทุกเส้นทางที่คุณอาจต้องใช้ฟังก์ชันนี้
ArrayKnight

เห็นด้วยอย่างสมบูรณ์ฉันไม่ชอบวิธีแก้ปัญหานี้เลย
omarjebari

2

ฉันคิดว่าrouter.replaceเป็นวิธีที่จะไป - แต่ยังคงมีบางบรรทัดของความคิด (ยังไม่ทดลอง):


โดยทั่วไปเมื่อมี$routerการเปลี่ยนแปลงจะทำให้ส่วนประกอบโหลดจนกว่าจะปล่อยออกload:stopมาrouter-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 {}

นี่เป็นการเรียงลำดับของการนำไปใช้ที่ฉันกำลังพูดถึงแม้ว่าฉันมักจะนำส่วนประกอบการโหลดของฉันไปใช้กับส่วนประกอบหน้าของฉันโดยตรง เหตุผล: ฉันชอบใช้วิธี Skeleton ซึ่งองค์ประกอบเนื้อหาโหลดเข้าสู่สถานะกึ่งโหลดเพื่อให้ผู้ใช้เห็นภาพสะท้อนสิ่งที่คาดหวังจากนั้นซ้อนทับองค์ประกอบโหลด (หากจำเป็นต้องปิดกั้นการกระทำทั้งหมด) หรืออินไลน์หาก ผู้ใช้สามารถใช้ UI ในขณะที่โหลดข้อมูล มันคือทั้งหมดที่เกี่ยวกับการรับรู้ของผู้ใช้เกี่ยวกับความเร็วและการยกเลิกการปิดกั้นความสามารถในการทำสิ่งที่ต้องการโดยเร็วที่สุด
ArrayKnight

@ArrayKnight คิดนามธรรมเพียงแค่สามารถแลกเปลี่ยนส่วนประกอบเราเตอร์ดูกับส่วนประกอบเส้นทางที่สอดคล้องกันและได้รับ wrapper ชนิดบาง - แต่ฉันตกลงวางโหลดลงในเส้นทางรู้สึกเหมือนการออกแบบที่ไม่ดี
5159

2

นี่เป็นการโทรที่ยากลำบากเมื่อพิจารณาว่าเรารู้เพียงเล็กน้อยเกี่ยวกับสิ่งที่เกิดขึ้นในเส้นทางการโหลดของคุณ

แต่...

ฉันไม่เคยต้องการสร้างเส้นทางการโหลดเพียง แต่เคยโหลดส่วนประกอบที่สร้างการแสดงผลหลายเส้นทางในช่วงเริ่มต้น / ขั้นตอนการรวบรวมข้อมูล

อาร์กิวเมนต์หนึ่งที่ไม่มีเส้นทางการโหลดอาจเป็นได้ว่าผู้ใช้สามารถนำทางไปยัง URL นี้ได้โดยตรง (โดยไม่ตั้งใจ) และดูเหมือนว่าจะไม่มีบริบทเพียงพอที่จะรู้ว่าจะส่งผู้ใช้ไปยังที่ใดหรือดำเนินการอย่างไร แม้ว่านี่อาจหมายความว่ามันผ่านไปยังเส้นทางที่ผิดพลาด ณ จุดนี้ โดยรวมแล้วไม่ใช่ประสบการณ์ที่ดี

ก็คือว่าถ้าคุณลดความซับซ้อนของเส้นทางของคุณนำทางระหว่างเส้นทางที่จะกลายเป็นง่ายมากและจะทำงานตามที่คาดไว้ / $router.replaceต้องการโดยไม่ต้องใช้ของ

ฉันเข้าใจว่าสิ่งนี้ไม่ได้แก้ปัญหาในแบบที่คุณถาม แต่ฉันขอแนะนำให้คิดใหม่เส้นทางการโหลดนี้

App

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

เปลือก

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

หน้าหลัก

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

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

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

หน้าผลลัพธ์

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

องค์ประกอบผลลัพธ์

โดยทั่วไปส่วนประกอบของผลลัพธ์ที่แน่นอนที่คุณมีอยู่แล้ว แต่ถ้าloadingเป็นจริงหรือถ้าresultsเป็นโมฆะ แต่คุณต้องการคุณสามารถสร้างชุดข้อมูลปลอมเพื่อทำซ้ำและสร้างโครงกระดูกหากคุณต้องการ มิฉะนั้นคุณสามารถเก็บสิ่งต่าง ๆ ในแบบที่คุณมี


หน้าจอการโหลดที่ฉันใช้เต็มหน้าจอดังนั้นฉันไม่สามารถคิดวิธีที่จะแสดงมันได้โดยไม่ต้องมีเส้นทางของตัวเอง ดูnaminator.ioสำหรับการสาธิตสด เปิดรับแนวคิดอื่นใดเพื่อทำสิ่งนี้
คลิก

ฉันจะให้มันดูเหมาะสมในตอนเช้า แต่ความคิดเริ่มต้นของฉันคือไม่มีเหตุผลที่คุณไม่สามารถใช้ตำแหน่งที่คงที่เพื่อควบคุมหน้าจอ
ArrayKnight

ดูที่การออกแบบของคุณดูเหมือนว่ามีตัวเลือกอยู่สองสามตัว: คุณสามารถแสดงตัวโหลดแทนเนื้อหาผลลัพธ์แล้วสลับเมื่อดึงข้อมูลแล้ว หรือคุณสามารถวางซ้อนตัวโหลดโครงกระดูกของผลลัพธ์ของคุณจากนั้นอัปเดตและเติมผลลัพธ์และลบตัวโหลดออก เมื่อพิจารณาว่าโหลดเดอร์ไม่ครอบคลุมส่วนหัว (เชลล์ไซต์) คุณไม่จำเป็นต้องแก้ไขตำแหน่ง
ArrayKnight

@ClickUpvote แจ้งให้เราทราบว่าคุณรู้สึกอย่างไรเกี่ยวกับวิธีการนี้
ArrayKnight

@ ArrayKnight คำถามคือเส้นทางถูกนำไปใช้งานแล้วและทำงานตามที่คาดไว้วิธีการโหลดนี้อาจไม่เหมาะกับที่นี่ฉันผ่านสถานการณ์แบบนี้มาแล้ว เส้นทางการโหลดสามารถมีคำขอโพสต์ฟอร์มเช่นเกตเวย์การชำระเงินหรือการดำเนินการโพสต์แบบฟอร์มบางอย่าง ในสถานการณ์นี้เราไม่สามารถทำการลบเส้นทางได้ แต่ยังคงอยู่ในระดับที่เราเตอร์ vue เราสามารถมีก่อนที่จะเข้าสู่ยามทำไมผมแนะนำวิธีการนี้คือตัวอย่าง vue องค์ประกอบโหลดยังไม่ได้สร้างขึ้นในเบ็ดนี้และความได้เปรียบในการตัดสินใจว่าจะเข้าสู่เส้นทางนี้หรือไม่ขึ้นอยู่กับเส้นทางก่อนหน้า
chans


0

สิ่งนี้สามารถทำได้ด้วยbeforeEachเบ็ดของเราเตอร์

สิ่งที่คุณต้องทำคือคุณต้องบันทึกตัวแปรทั่วโลกหรือใน localStorage ในloadingองค์ประกอบเมื่อข้อมูลถูกโหลด (ก่อนที่จะเปลี่ยนเส้นทางไปยังresultsองค์ประกอบ):

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

จากนั้นคุณควรตรวจสอบตัวแปรนี้ในตะขอก่อนและข้ามไปยังองค์ประกอบที่ถูกต้อง:

// 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();
});

นอกจากนี้อย่าลืมรีเซ็ตค่าเป็นเท็จในmainองค์ประกอบของคุณเมื่อคลิกปุ่มแบบสอบถาม (ก่อนที่จะกำหนดเส้นทางไปยังloadingส่วนประกอบ):

export default {
  name: "main",
  ...
  queryButtonClicked() {
    localStorage.setItem('DATA_LOADED', JSON.stringify(false));
    this.$router.push({path: "/loading"});
  }
}

โซลูชันนี้มีความเปราะ (แนะนำหนี้ด้านเทคโนโลยีการบำรุงรักษา) และต้องมีรายการสำหรับเส้นทางที่เป็นไปได้ทุกเส้นทางที่คุณอาจต้องใช้ฟังก์ชันนี้
ArrayKnight

0

หน้าจอการโหลดของคุณไม่ควรถูกควบคุมโดย vue-router เลย ตัวเลือกที่ดีที่สุดคือการใช้ modal / overlay สำหรับหน้าจอการโหลดของคุณควบคุมโดย javascript มีตัวอย่างมากมายสำหรับ vue หากคุณไม่พบอะไรเลย vue-bootstrap มีตัวอย่างง่ายๆที่จะนำไปใช้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.