刷新时指示 vue-router 重新渲染正确的页面 [英] Instruct vue-router to re-render correct page when refresh

查看:110
本文介绍了刷新时指示 vue-router 重新渲染正确的页面的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的应用程序是由 vuejs + rails 构建的,它具有以下路由器组件:

My application is built by vuejs + rails, it has these router components:

 {
    path: '/',
    name: 'home',
    component: Home,
    children: [
      {
        path: '/',
        name: 'welcome',
        component: Welcome,
      },
      {
        path: '/classes/:id',
        name: 'class',
        component: Class,
      },
      {
        path: '/classes/:id/members',
        name: 'member',
        component: Member,
      },
      {
        path: '/account',
        name: 'account',
        component: Account,
      },
      ...
    ]
  },

当我导航链接时,路由器链接部分工作正常,但是当我站在 /classes/1 path 并刷新 url 时,它导致未找到错误:

The router-link parts work fine when I navigate the link, but when I'm standing in /classes/1 path and refresh the url, it result a not found error:

没有路由匹配[GET]/classes/1"

我明白刷新的时候,我向服务器发出了一个新的请求,所以我添加了这个Rails 路由器中的路由:

I understand that when I refresh, I make a new request to the server, so I added this routing in rails router:

get '/*path', to: 'home#index'

因此,它不会引发错误,而是使主页成功,但这不是我想要的.我希望它仍然保留或重新加载我所在的当前页面.如果我在 /classes/1 并点击刷新,它应该保持/刷新 /classes/1 页面.我怎样才能做到这一点?

So instead of raising an error, it render home page successful but that is not what I want. I want it still keep or reload the current page where I 'm in. If I'm in /classes/1 and hit refresh, it should keep/refresh /classes/1 page. How can I achieve this?

推荐答案

这里是我在工作中所做的应用程序关键部分的修改版本.

Here are modified versions of key parts of an app I did at work.

# config/routes.rb
# frozen_string_literal: true

Rails.application.routes.draw do
  root('application#index')

  # I use a scope because in the real app I also have other routes such as
  # browserconfig.xml and site.webmanifest which are both procedurally
  # generated.
  scope('/', format: false) do
    # Catch all for HTML 5 history routing. This must be the last route.
    get('*path', to: 'application#index')
  end
  # equivalent, if scope is unnecessary
  # get('/*path', to: 'application#index', format: false)
end

# app/controllers/application_controller.rb
# frozen_string_literal: true

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception

  def index
    # Avoid having an empty view file.
    render(inline: '', layout: 'application')
  end
end

/ app/views/layouts/application.html.slim
doctype html
html
  head
    / ...

  body
    main

    / Webpacker 4.3.0
    = javascript_packs_with_chunks_tag('main', defer: true)

在我的 JS 代码中,我在 main 元素上挂载了一个 Vue 实例.所有客户端/Vue 路由都通过我的 Vue 路由器处理.我有一个JS目录树结构如下:

In my JS code, I mount a Vue instance on the main element. All client-side/Vue routes are handled through my Vue router. I have a JS directory tree structure as follows:

project_root/
|-- src/
    |-- api/ <- external REST API handling code
    |-- channels/ <- Action Cable stuff
    |-- components/ <- Vue SFC's and functional components
    |-- images/ <- image files
    |-- lib/ <- various local library code
    |-- mixins/ <- Vue component mixins
    |-- packs/ <- all files in here are picked up by Webpacker; these are the entry-points
        |-- main.mjs <- imports pages/main.mjs (my app has multiple packs and pages, and there's "some" logic behind this structure)
    |-- pages/
        |-- main.mjs <- does the heavy lifting of building the Vue instance
    |-- plugins/ <- Vue plugins
    |-- scss/ <- SCSS files
    |-- store/ <- Vuex stuff
    |-- vendor/ <- third-party vendor stuff, like poly-fills
    |-- main.mjs

// src/pages/main.mjs
import '@/scss/main.scss';

// this should be first to ensure `vendors` loads first
import build from '@/main';

import VuexRouterSync from 'vuex-router-sync';
import App from '@/components/app.vue';
import router from '@/lib/router';
import store from '@/store';

build(
  App,
  {
    router,
    store,
  },
  (Vue) => {
    Vue.use(BootstrapVueUtils);

    VuexRouterSync.sync(store, router);
  },
);

// src/main.mjs
import '@/vendor';
import isFunction from 'lodash-es/isFunction';
import Vue from 'vue';

export default function build(appComponent, config = {}, preInit = null) {
  // call Vue.use here for any plugins that are common to all packs/pages
  // Vue.use(Vuelidate);

  if (preInit == null && isFunction(config)) {
    preInit = config;
    config = {};
  }

  if (isFunction(preInit)) {
    preInit(Vue);
  }

  function init() {
    new Vue({
      beforeCreate() {
        Vue.$rootVm = this;
      },

      render(h) {
        return h(appComponent);
      },

      ...config,
    }).$mount('main');
  }

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', init);
  } else {
    init();
  }
}

// src/lib/router.mjs
import Vue from 'vue';
import VueRouter from 'vue-router';
import Folder from '@/components/folders/show.vue';
import Folders from '@/components/folders'; // index.vue
import Home from '@/components/home.vue';
import LogIn from '@/components/log-in.vue';
import NotFound from '@/components/not-found.vue';
import Settings from '@/components/settings'; // index.vue

Vue.use(VueRouter);

const router = new VueRouter({
  mode: 'history',
  saveScrollPosition: true,
  routes: [
    {
      path: '/',
      redirect: '/folders',
      component: Home,
      children: [
        {
          name: 'folders',
          path: 'folders',
          component: Folders,
        },
        {
          name: 'folder',
          path: 'folders/:id',
          component: Folder,
        },
        {
          name: 'settings',
          path: 'settings',
          component: Settings,
        },
      ],
    },
    {
      name: 'log-in',
      path: '/log-in',
      component: LogIn,
    },
    // This isn't necessary, and I question it now. Has no effect on the functioning of the router.
    {
      name: 'not-found',
      path: '*',
      component: NotFound,
    }
  ],
});

// There's some additional code after this to setup event handling on the
// `router`, specifically `router.beforeEach` to check the logged-in status
// and if not logged in redirect to the log-in route.

这是很多代码,但我想尝试涵盖从 Rails 路由器到 Vue 路由器的所有内容,还包括一些可能有帮助的额外内容.

This is a lot of code, but I wanted to try and cover everything from Rails router to Vue router, and also include some extra stuff that might help.

我确实注意到您在 'home' Vue 路由的 children 数组中包含了前导 /.这是不必要的,因为它是基于父级隐含的.

I did notice that you included the leading / in the children array of your 'home' Vue route. That's unnecessary as it's implied based on the parent.

来自 Vue 路由器文档:

请注意,以/开头的嵌套路径将被视为根路径.这允许您利用组件嵌套而不必使用嵌套 URL.

Note that nested paths that start with / will be treated as a root path. This allows you to leverage the component nesting without having to use a nested URL.

所以,虽然没有必要,但我认为这不是您问题的原因.

So, while unnecessary, I don't think it's the cause of your issue.

我唯一能想到的就是绝对确定 Rails 包罗万象的路线是最后一条路线.Rails 路由按照在 config/routes.rb 文件中指定路由的顺序从上到下匹配.

The only thing I can think of, is to make absolutely certain the Rails catch-all route is the very last route. Rails routing matches from top-bottom in order of how the routes are specified in the config/routes.rb file.

这篇关于刷新时指示 vue-router 重新渲染正确的页面的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆