Làm thế nào để đẩy vào vue-router mà không cần thêm vào lịch sử?


12

Tôi có trình tự sau đây xảy ra:

  • Màn hình chính

  • Đang tải màn hình

  • Màn hình kết quả

Trên trang chủ, khi ai đó nhấp vào nút, tôi gửi chúng đến màn hình đang tải, thông qua:

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

Và một khi nhiệm vụ của họ kết thúc, họ được gửi đến màn hình kết quả thông qua

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

Vấn đề là, thông thường họ muốn đi từ kết quả trở lại màn hình chính, nhưng khi họ nhấp lại, họ sẽ được gửi lại để tải lại kết quả, vì vậy họ bị mắc kẹt trong một vòng lặp vô hạn và không thể đi trở về màn hình chính.

Bất kỳ ý tưởng làm thế nào để sửa lỗi này? Tôi lý tưởng như nếu có một tùy chọn như:

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

mà sẽ gửi chúng đến tuyến đường mà không cần thêm nó vào lịch sử.


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

@RolandStarke Cảm ơn - điều này làm cho nút quay lại hoạt động và quay trở lại menu chính, nhưng tôi mất nút chuyển tiếp và không thể tiếp tục lại - có ý tưởng nào không?
Nhấp vào Upvote

Quá trình là: Menu chính, Đang tải, Kết quả. Tôi đang gọi $router.replacetừ Đang tải. Điều này bây giờ cho phép tôi quay lại từ Kết quả -> Chính nhưng sau đó tôi không thể chuyển tiếp kết quả.
Nhấp vào Upvote

Một lựa chọn khác là không có tuyến đường tải. Thay vào đó đẩy trực tiếp đến tuyến kết quả thực hiện tìm nạp dữ liệu khi tạo và sau đó hiển thị chế độ xem tải cho đến khi hoàn tất. Sau đó, không có định tuyến lại và lịch sử và luồng người dùng sẽ vẫn còn nguyên mà không có $ router.replace.
ArrayKnight

@ClickUpvote bạn có tìm thấy bất kỳ giải pháp nào cho vấn đề này không ...
chans

Câu trả lời:


8

Có một cách hoàn hảo để xử lý tình huống này

Bạn có thể sử dụng bảo vệ trong thành phần để kiểm soát tuyến đường ở cấp độ hạt

Thực hiện các thay đổi sau trong mã của bạn

Trong thành phần màn hình chính

Thêm bảo vệ beofreRouteLeave này trong các tùy chọn thành phần, trước khi rời khỏi 'màn hình kết quả', bạn đang đặt tuyến đường chỉ đi qua màn hình tải

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

Đang tải thành phần màn hình

Nếu tuyến đường quay trở lại từ kết quả đến tải sau đó, nó không nên hạ cánh ở đây và trực tiếp nhảy đến màn hình chính

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

Trong màn hình tải, bộ bảo vệ beforeRoute Entry KHÔNG có quyền truy cập vào điều này, bởi vì bộ bảo vệ được gọi trước khi điều hướng được xác nhận, do đó thành phần nhập mới thậm chí chưa được tạo. Vì vậy, tận dụng lợi thế này, bạn sẽ không nhận được các cuộc gọi vô hạn khi định tuyến từ màn hình kết quả

Trong thành phần màn hình kết quả

Nếu bạn sử dụng quay lại thì nó không nên tải và trực tiếp nhảy lên màn hình chính

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

Tôi vừa tạo ứng dụng vue nhỏ để tái tạo vấn đề tương tự. Nó hoạt động tại địa phương của tôi theo câu hỏi của bạn. Hy vọng nó cũng giải quyết vấn đề của bạn .


2
Giải pháp này rất dễ vỡ (giới thiệu nợ công nghệ bảo trì) và yêu cầu các mục nhập cho mọi tuyến đường có thể mà bạn có thể cần chức năng này.
ArrayKnight

Hoàn toàn đồng ý, tôi hoàn toàn không thích giải pháp này
omarjebari

2

Tôi đoán router.replacelà con đường để đi - nhưng vẫn còn một số dòng suy nghĩ (chưa được kiểm tra):


Về cơ bản khi $routerthay đổi, nó kết xuất thành phần tải cho đến khi nó phát ra load:stop, sau đó nó kết xuất lạirouter-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 {}

Đây là loại triển khai mà tôi đang nói đến, mặc dù tôi có xu hướng triển khai thành phần tải trực tiếp vào các thành phần trang của mình thay vì tạo một trình bao bọc xem tuyến đường. Lý do là: Tôi thích sử dụng phương pháp bộ xương trong đó thành phần nội dung tải vào trạng thái bán tải để cung cấp cho người dùng phản hồi trực quan về những gì mong đợi và sau đó phủ lên thành phần tải (nếu cần chặn tất cả các hành động) hoặc nội tuyến nó nếu người dùng có thể sử dụng UI trong khi tải dữ liệu. Đó là tất cả về nhận thức của người dùng về tốc độ và bỏ chặn khả năng của họ để làm những gì họ muốn càng nhanh càng tốt.
ArrayKnight

@ArrayKnight suy nghĩ trừu tượng người ta có thể chỉ cần trao đổi thành phần xem bộ định tuyến với thành phần tuyến đường lõi của nó và nhận được một số loại trình bao bọc trình tải - nhưng tôi đồng ý đưa trình tải vào tuyến có cảm giác như thiết kế tồi
Estradiaz

2

Đây là một cuộc gọi khó khăn xem xét chúng tôi biết rất ít về những gì xảy ra trong tuyến đường tải của bạn.

Nhưng...

Tôi chưa bao giờ có nhu cầu xây dựng tuyến tải, chỉ bao giờ tải (các) thành phần được hiển thị trên nhiều tuyến trong giai đoạn thu thập dữ liệu / init.

Một lập luận cho việc không có lộ trình tải là người dùng có khả năng điều hướng trực tiếp tới URL này (vô tình) và sau đó có vẻ như nó sẽ không có đủ ngữ cảnh để biết nơi gửi người dùng hoặc phải thực hiện hành động nào. Mặc dù điều này có thể có nghĩa là nó rơi vào một tuyến lỗi vào thời điểm này. Nhìn chung, không phải là một kinh nghiệm tuyệt vời.

Một điều nữa là nếu bạn đơn giản hóa các tuyến đường của mình, việc điều hướng giữa các tuyến đường trở nên đơn giản hơn nhiều và hoạt động như mong đợi / mong muốn mà không cần sử dụng $router.replace.

Tôi hiểu điều này không giải quyết được câu hỏi theo cách bạn hỏi. Nhưng tôi khuyên bạn nên xem xét lại tuyến đường tải này.

Ứng dụng

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

Vỏ

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

Trang chính

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

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

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

Trang kết quả

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

Thành phần kết quả

Về cơ bản chính là thành phần kết quả chính xác mà bạn đã có, nhưng nếu loadinglà đúng hoặc nếu resultskhông, tuy nhiên bạn thích, bạn có thể tạo một tập dữ liệu giả để lặp lại và tạo các phiên bản khung xương, nếu bạn muốn. Nếu không, bạn chỉ có thể giữ mọi thứ theo cách bạn có nó.


Màn hình tải tôi đã chiếm toàn bộ màn hình, vì vậy tôi không thể nghĩ ra cách nào để hiển thị màn hình mà không có lộ trình riêng. Xem naminator.io cho một bản demo trực tiếp. Mở cho bất kỳ ý tưởng khác để làm điều này.
Nhấp vào Upvote

Tôi sẽ cho nó một cái nhìn đúng đắn vào buổi sáng, nhưng suy nghĩ ban đầu của tôi là không có lý do gì bạn không thể sử dụng vị trí cố định để chiếm lấy màn hình.
ArrayKnight

Nhìn vào thiết kế của bạn, có vẻ như có một vài tùy chọn: bạn có thể kết xuất trình tải thay cho nội dung kết quả và sau đó chuyển đổi khi dữ liệu đã được tìm nạp; hoặc bạn có thể chồng lên bộ tải của bộ khung kết quả của bạn, sau đó cập nhật và điền vào kết quả và xóa bộ tải. Xem xét rằng trình tải không bao gồm tiêu đề (vỏ trang web), bạn không cần cố định vị trí.
ArrayKnight

@ClickUpvote cho tôi biết cảm nhận của bạn về phương pháp này.
ArrayKnight

@ ArrayKnight, câu hỏi là các tuyến đã được triển khai và nó hoạt động như mong đợi, phương pháp tải này có thể không phù hợp ở đây, tôi đã trải qua tình huống tương tự. Lộ trình tải cũng có thể có một yêu cầu bài mẫu như cổng thanh toán hoặc một số hành động bài mẫu. Trong kịch bản này, chúng tôi không thể bao gồm việc loại bỏ một tuyến đường. Nhưng vẫn ở cấp bộ định tuyến vue mà chúng ta có thể có trước khi vào bộ bảo vệ, Tại sao tôi lại đề xuất cách tiếp cận này, ví dụ vue của thành phần tải chưa được tạo tại hook này và lợi thế của nó là quyết định có nên vào tuyến này hay không dựa trên tuyến trước
trò chuyện

1

Một tùy chọn khác là sử dụng API Lịch sử .

Khi bạn đang ở trong màn hình Kết quả, bạn có thể sử dụng Thay thếState để thay thế URL trong lịch sử của trình duyệt.


0

Điều này có thể được thực hiện với beforeEachhook của bộ định tuyến.

Những gì bạn cần làm là bạn phải lưu một biến trên toàn cầu hoặc trong localStorage trong loadingthành phần khi dữ liệu được tải (trước khi chuyển hướng đến resultsthành phần):

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

Và sau đó bạn nên kiểm tra biến này trong hook beforeEach và bỏ qua thành phần chính xác:

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

Ngoài ra, hãy nhớ đặt lại giá trị thành false trong mainthành phần của bạn khi nhấp vào nút truy vấn (trước khi định tuyến đến loadingthành phần):

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

Giải pháp này rất dễ vỡ (giới thiệu nợ công nghệ bảo trì) và yêu cầu các mục nhập cho mọi tuyến đường có thể mà bạn có thể cần chức năng này.
ArrayKnight

0

Màn hình tải của bạn không nên được kiểm soát bởi vue-router. Tùy chọn tốt nhất là sử dụng phương thức / lớp phủ cho màn hình tải của bạn, được điều khiển bởi javascript. Có rất nhiều ví dụ xung quanh cho vue. Nếu bạn không thể tìm thấy bất cứ điều gì thì vue-bootstrap có một số ví dụ dễ thực hiện.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.