上节课我们用Switch组件来解决了路由按顺序持续匹配问题,这样可以提高匹配效率。这节课我们来介绍一下如何解决样式丢失问题。

回顾

在开始这节课内容之前,我们来对上节课来做一个简单的回顾

  • 正常情况下一个路径只对应一个组件
  • 当有多个路由时可以通过Switch来使路由匹配到路径之后就终止继续往下匹配
  • Switch可以提高路由匹配效率。

以上便是上节课的主要要点,接下来我们来介绍如何解决样式丢失问题。

需求

就我们目前的状态来看,其实我们的功能都没有问题,而且也没有出现所谓的样式丢失问题。那么我们来做这么样一个事儿。我们让我们的路径都能有一个前缀。比如原来的/about路径,现在我们要改成/lynchow/about,那么没有什么难的,直接在to属性前面加上前缀就行了,那么相应的,匹配的时候也要加上前缀。

export default class App extends Component {
  render() {
    return (
      <div>
        ...
            <div className="list-group">
              <MyNavLink to="/lynchow/about">About</MyNavLink>
              <MyNavLink to="/lynchow/home">Home</MyNavLink>
            </div>
         ...
              <div className="panel-body">
                <Switch>
                  <Route path="/lynchow/about" component={About} />
                  <Route path="/lynchow/home" component={Home} />
                </Switch>
              </div>
         ...
      </div>
    );
  }
}

那么我们来看一下这样的话会是什么效果:

image-20220114111428767

仿佛也没有什么问题啊,样式都在啊那么我们来点击一下刷新。

image-20220114112314309

这一下出问题了,样式丢了啊,这是为什么呢?这样,我们先把前缀去掉,我们从头捋。

分析

image-20220114112525767

我们来看我们这个时候bootstrap是不是正常加载的?我们看一下这个请求链接是什么?是不是我们public文件夹中的css文件夹里的bootstrap.css文件?那么我们并没有配这个路由啊,为什么这个链接不会报 404 呢?

我们之前是不是说过public文件夹是我们这个脚手架服务的静态资源根目录。所以说当我们我们请求链接的时候,服务都会检查自身有没有这项资源,没有的话再去匹配路由。那么如果我们路由也没匹配到,自身也没有这项资源会怎么样呢?那我们在浏览器中测一下呗:

image-20220114122354709

我们来看,我们的脚手架是不是根本就没有/a/b/c/test.html这个文件?而且我们也没有做这个路由匹配。那么为什么会这样呢?我们拿到的又是什么呢?我们来看一下这个个内容,这是不是index.html啊?这就是react脚手架设计的,如果说出现了网络请求的类似这种的一些错误,我们不报错,但是会把public中的index.html给返回回来。

那么我们这个时候是没有问题的,因为我才刚打开页面,而且成功加载了bootstrap,然后点击了About,那么这个时候我点击刷新的话,我们来看网络请求。

image-20220114122937342

我们来看,这个时候请求的是不是还是localhost:3000/css/bootstrap.css,所以我们没有加前缀的时候点击刷新是没有问题的。

那么接下来我们把路由的路径加上前缀再过一遍这个流程

image-20220114123352811

这次我们可是把前缀也加上了,然后我们点击了About,来看,我们请求的链接是不是没有问题?而且页面的样式也是正常的,那么我们来刷新一下页面:

image-20220114123535166

诶,样式丢了,请求的链接好像有问题啊,他怎么请求了localhost:3000/lynchow/css/bootstrap.css?我们脚手架的public中有lynchow这个目录吗?没有啊,那能匹配到/lynchow/css的路径的路由吗?也匹配不到啊,那这个时候来兜底的人来了,就是public中的index.html,他被展示出来了,但是因为没有成功加载bootstrap所以样式丢了。

结论

那么经过上面的举例,我们能得出结论了吧,我来问大家,什么时候样式会丢失?是不是我们的路由路径是多级的时候,然后一刷新就会丢失。并不是只要路由路径是多级就会丢失,得当我们刷新了才会丢失。而丢失的原因就是浏览器认为我们多级路径中的前缀也是属于host的一部分。

解决

那么问题出来了,原因也分析出来了,那么怎么解决这个问题呢?我们有三种解决方法

第一,我们来看public中的index.html的代码:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="description" content="Web site created using create-react-app"/>
    <title>React Router</title>
    <link rel="stylesheet" href="./css/bootstrap.css">
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

我们是怎么引入的bootstrap?是不是用相对路径引入的?我们现在呢不用相对路径了,我们用之前说过的%PUBLIC_URL%来引入

image-20220114125141575

这次我们再刷新,没有问题,请求的链接也是正常的,因为之前说过%PUBLIC_URL%是脚手架中定义好的一个关键字,就好比是环境变量一般,这样的话刷新请求的永远都是这个绝对路径下的bootstrap

第二种方法和第一种类似我们直接这样:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="description" content="Web site created using create-react-app"/>
    <title>React Router</title>
    <link rel="stylesheet" href="/css/bootstrap.css">
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

这是什么意思?是不是直接从根目录下的css目录来引入?根目录是谁?是不是public?所以说这样也不会导致路径出错。

第三种方法可能有人说,我就想在index.html中写./css我就是要用相对路径引入,那么这么办呢?也没问题,我们来改index.js

import { HashRouter } from 'react-router-dom'

import App from './App';

ReactDOM.render(<HashRouter><App /></HashRouter>, document.getElementById('root'));

我们用HashRouter,我们都知道HashRouterBrowserRouter最直观的一个差别就是一个有#另一个没有,我们是不是说过#后面的东西懂不带给服务器,所以说当我们点击About,路径是不是/lynchow/about?但是这一串是不是都在#后面,所以在刷新的时候直接就从#截断了,根本不可能会出现路径上的问题。

总结

  • 多级路由路径在刷新时会丢失样式
  • 样式丢失是由导入样式的相对路径导致的
  • 解决样式丢失有三种办法
  • %PUBLIC_URL%引入样式
  • 根目录引入样式
  • HashRouter解决

Copyright statement:The articles of this site are all original if there is no special explanation, indicate the source please when you reprint.

Link of this article:https://work.lynchow.com/article/solve_lose_style/