履歴に追加せずに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ありがとうございます。これにより、[戻る]ボタンが機能し、メインメニューに戻りますが、[進む]ボタンがなくなり、再び進むことができなくなります。
19:10の

プロセスは次のとおりです。メインメニュー、ロード、結果。$router.replaceローディングから電話しています。これで、[結果]-> [メイン]に戻ることができますが、結果に進むことができません。
[Upvote]をクリックします。

別のオプションは、読み込みルートを持たないことです。むしろ、作成時にデータフェッチを実行する結果ルートに直接プッシュし、完了するまで読み込みビューをレンダリングします。その場合、再ルーティングは行われず、履歴とユーザーフローは$ router.replaceなしでそのまま維持されます。
ArrayKnight

@ClickUpvoteあなたはこの問題の解決策を見つけましたか?
chans

回答:


8

この状況を処理するには完璧な方法があります

コンポーネント内ガードを使用して、グラニュールレベルでルートを制御できます

コードに次の変更を加えます

メイン画面コンポーネント

このbeofreRouteLeaveガードをコンポーネントオプションに追加します。「結果画面」に進む前に、ロード画面のみを通るルートを設定します

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、

完全に同意します。このソリューションはまったく好きではあり
ません

2

router.replaceは行く方法だと思います-しかし、まだいくつかの考えの線(テストされていません):


基本的に$routerは、変更時にloading-componentをレンダリングしてからを発行し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 {}

これは、私が話している種類の実装ですが、ルート監視ラッパーを作成するのではなく、ロードコンポーネントをページコンポーネントに直接実装する傾向があります。その理由:私は、コンテンツコンポーネントがセミロード状態にロードされるスケルトンアプローチを使用して、ユーザーに何を期待するかを視覚的にフィードバックしてから、ロードコンポーネントをオーバーレイする(すべてのアクションをブロックする必要がある場合)か、ユーザーはデータの読み込み中にUIを使用できます。それは、スピードに対するユーザーの認識と、ユーザーが望むことを可能な限り迅速に実行する能力のブロックを解除することです。
ArrayKnight

@ArrayKnight単にそのcorespondingルート・コンポーネントとルータ・ビュー・コンポーネントを交換することができ抽象的なものを考え、ローダラッパーのいくつかの種類を取得します-しかし、私は悪いデザインのようなルートフィールにローダーを入れ同意
Estradiaz

2

これは、積載ルートで何が発生しているかについて私たちがほとんど知らないことを考えると、難しい電話です。

だが...

ロードルートを構築する必要はありませんでした。init/ data収集段階で複数のルートにレンダリングされるコンポーネントをロードするだけです。

読み込みルートがないことの1つの引数は、ユーザーがこのURLに(誤って)直接移動する可能性があり、ユーザーの送信先や実行するアクションを知るのに十分なコンテキストがないように思われることです。ただし、この時点でエラールートに陥る可能性があります。全体として、素晴らしい経験ではありません。

もう1つは、ルートを簡略化すると、ルート間のナビゲーションがはるかに簡単になり、を使用しなくても期待どおり/期待どおりに動作することです$router.replace

これで問題が解決しないことを理解しています。しかし、このロードルートを再考することをお勧めします。

アプリ

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

結果コンポーネント

基本的には、既存のまったく同じ結果コンポーネントですが、loadingtrueであるかresultsnullである場合でも、必要に応じて、偽のデータセットを作成して反復処理を行い、スケルトンバージョンを作成できます。そうでなければ、あなたはそれをあなたがそれを持っている方法でそのまま保つことができます。


私が持っているロード画面は画面全体を占めているので、独自のルートがなければそれを表示する方法は考えられません。ライブデモについては、naminator.ioを参照してください。これを行うための他のアイデアを受け入れる。
19:07に

朝はちゃんと見せるつもりですが、固定位置を使って画面を乗っ取れないわけはないと思います。
ArrayKnight、

デザインを見ると、いくつかのオプションがあるようです。結果コンテンツの代わりにローダーをレンダリングし、データがフェッチされたら切り替えることができます。または、結果のスケルトンのローダーをオーバーレイし、結果を更新してデータを入力し、ローダーを削除することもできます。ローダーがヘッダー(サイトシェル)をカバーしていないことを考えると、位置を固定する必要はありません。
ArrayKnight

@ClickUpvoteから、このアプローチについての感想をお聞かせください。
ArrayKnight、

@ArrayKnight、問題はルートがすでに実装されており、期待どおりに機能していることです。この読み込みアプローチはここに収まらない可能性があります。私は同じ状況を経験しています。ロードルートには、支払いゲートウェイなどのフォームポストリクエストや、いくつかのフォームポストアクションを含めることもできます。このシナリオでは、ルートを削除することはできません。しかし、ガードに入る前にまだ持つことができるVueルーターレベルでは、このアプローチを提案した理由は、コンポーネントをロードするVueインスタンスがまだこのフックで作成されておらず、以前のルートに基づいてこのルートに入るかどうかを決定する利点です
chans


0

これbeforeEachは、ルーターのフックを使用して行うことができます。

あなたがする必要があるのは、(loadingコンポーネントにリダイレクトする前に)データが読み込まれたときに、コンポーネント内のグローバルまたはlocalStorageに変数を保存する必要があることですresults

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

そして、beforeEachフックでこの変数を確認し、正しいコンポーネントにスキップする必要があります。

// 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コンポーネントにルーティングする前)に、コンポーネントの値をfalseにリセットすることを忘れないでください。

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

このソリューションは壊れやすく(保守技術の負債が発生する)、この機能が必要になる可能性のあるすべてのルートにエントリが必要です。
ArrayKnight

0

ロード画面は、vue-routerによって制御されるべきではありません。最良のオプションは、JavaScriptで制御される、ローディング画面にモーダル/オーバーレイを使用することです。Vueの周りには多くの例があります。何も見つからない場合は、vue-bootstrapに実装する簡単な例がいくつかあります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.