Як натиснути на 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"})
Роланд Старк

@RolandStarke Спасибі - це змушує роботу кнопки "назад" повертатися до головного меню, але я втрачаю кнопку вперед і знову не можу йти вперед - будь-які ідеї?
Клацніть Upvote

Процес: головне меню, завантаження, результати. Я дзвоню $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

Повністю згоден, мені це рішення взагалі не подобається
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 {}

Це такий варіант реалізації, про який я говорю, хоча я схильний реалізувати свій компонент завантаження безпосередньо в компоненти моєї сторінки, а не створювати обгортку для перегляду маршруту. Причина полягає в тому, що я люблю використовувати скелетний підхід, коли компонент вмісту завантажується в напівнавантажений стан, щоб дати користувачеві візуальний відгук про те, що очікувати, а потім накладати компонент завантаження (якщо йому потрібно блокувати всі дії) або вкласти його, якщо користувач може використовувати інтерфейс користувача під час завантаження даних. Вся справа в сприйнятті користувачем швидкості та розблокуванні їх здатності робити те, що вони хочуть, якнайшвидше.
ArrayKnight

@ArrayKnight думаючи абстрактно, можна просто обміняти компонент перегляду маршрутизатора з відповідним компонентом маршруту і отримує якусь обгортку навантажувача - але я погоджуюся, що введення завантажувача в маршрут відчуває себе поганим дизайном
Estradiaz

2

Це важкий виклик, враховуючи, як мало ми знаємо про те, що відбувається у вашому маршруті завантаження.

Але ...

У мене ніколи не виникало необхідності будувати маршрут завантаження, тільки коли-небудь завантажувальний компонент (и), який отримується на декількох маршрутах під час етапу збору даних / init.

Одним із аргументів для відсутності маршруту завантаження є те, що користувач може потенційно перейти безпосередньо до цієї URL-адреси (випадково), і тоді здається, що у нього не буде достатньо контексту, щоб знати, куди надіслати користувача або які дії вжити. Хоча це може означати, що в цей момент він потрапляє на маршрут помилки. Загалом, не великий досвід.

Інша полягає в тому, що якщо ви спростите свої маршрути, навігація між маршрутами стає набагато простішою і поводиться так, як очікувалося / бажано, без використання $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
                }
            })
        }
    }
}

Компонент результатів

В основному такий самий компонент результатів у вас вже є, але якщо loadingце правда, або якщо resultsце нуль, однак ви віддаєте перевагу, ви можете створити підроблений набір даних, щоб повторити і створити версії скелетів, якщо хочете. В іншому випадку ви можете просто зберігати речі так, як у вас є.


Екран завантаження, який я маю, займає весь екран, тому я не можу придумати спосіб його відображення без власного маршруту. Дивіться naminator.io для демонстрації в прямому ефірі. Відкриті для будь-яких інших ідей для цього.
Клацніть Upvote

Я вранці придивляюся до цього, але я вважаю, що немає жодної причини, щоб ти не зміг використовувати фіксовану позицію, щоб перейняти екран.
ArrayKnight

З огляду на ваш дизайн, схоже, є кілька варіантів: ви можете візуалізувати завантажувач замість вмісту результатів, а потім переключитися після отримання даних; або ви можете накладати завантажувач скелета результатів, а потім оновити та заповнити результати та видалити завантажувач. Враховуючи, що завантажувач не покриває заголовок (оболонку сайту), вам не потрібно фіксувати положення.
ArrayKnight

@ClickUpvote дайте мені знати, як ви ставитесь до цього підходу.
ArrayKnight

@ ArrayKnight, питання в тому, що маршрути вже реалізовані і як працює, як очікувалося, цей підхід до завантаження може не підійти тут, я пережив цю ж ситуацію. Маршрут завантаження також може мати запит на відправлення форми, наприклад, шлюз платежів або деякі дії після публікації форми. У цьому випадку ми не можемо передбачати видалення маршруту. Але все-таки на рівні маршрутизатора vue, який ми можемо мати перед тим, як ввести охоронців, чому я запропонував такий підхід, vue екземпляр завантажувального компонента ще не створений на цьому гачку, і його перевага вирішити, чи вводити цей маршрут чи не на основі попереднього маршруту
чани

1

Іншим варіантом є використання API історії .

Коли ви перейдете на екран результатів, ви можете використовувати ReplaceState для заміни URL-адреси в історії веб-переглядача.


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

І тоді ви повинні перевірити цю змінну в гачку передEach і перейти до потрібного компонента:

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

Крім того, не забудьте відновити значення до значення false у вашому mainкомпоненті після натискання кнопки запиту (перед маршрутизацією до loadingкомпонента):

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 має кілька простих прикладів для реалізації.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.