讲清楚说明白 - 路由

120 阅读2分钟

服务端路由

解析不同路径,决定不同的资源返回

客户端路由

SPA 页面跳转不会刷新

步骤

监听 url 的变化 获取 url 地址 匹配路由规则

方法

hash

<a href="#kaola">to KAOLA</a>
location.hash="#kaola"
  • url 中 hash 值的变化并不会重新加载页面
  • hash值的改变,都会在浏览器的访问历史中增加一个记录
  • hashchange 事件,监听到 hash 值的变化
  • 检测到变化后,通过替换 DOM 的方式来实现页面的更换

hash.jpg

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>hash</title>
</head>

<body>
    <a href="#">/</a>
    <a href="#/hash">hash</a>
    <div class="main" id="main"></div>
    <script>
        var mainbox = document.getElementById('main');
        function Router() {
            this.routes = {};
            this.currentUrl = '';
        }
        Router.prototype.route = function (path, callback) {
            this.routes[path] = callback || function () { };
        };
        Router.prototype.refresh = function () {
            this.currentUrl = location.hash.slice(1) || '/';
            this.routes[this.currentUrl]();
        };
        Router.prototype.init = function () {
            window.addEventListener('load', this.refresh.bind(this), false);
            window.addEventListener('hashchange', this.refresh.bind(this), false);
        }
        window.Router = new Router();
        window.Router.init();
        // 添加路由规则
        Router.route('/', function () {
            // 设置响应内容
            mainbox.innerHTML = 'current page is /'
        });
        Router.route('/hash', function () {
            // 设置响应内容
            mainbox.innerHTML = 'current page is hash'
        });
    </script>
</body>

</html>

history api

pushState.jpg

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>pushState</title>
</head>

<body>
    <p id="menu">
        <a href="/profile" title="profile">profile</a>
        <a href="/account" title="account">account</a>
    </p>
    <div class="main" id="main"></div>
    <script>
        (function () {
            var menubox = document.getElementById('menu');
            var mainbox = document.getElementById('main');

            menubox.addEventListener('click', function (e) {
                e.preventDefault();
                var elm = e.target;
                var uri = elm.href;
                var tlt = elm.title;
                history.pushState({ path: uri, title: tlt }, null, uri);
                mainbox.innerHTML = 'current page is ' + tlt;
            })
            window.addEventListener('popstate', function (e) {
                var state = e.state;
                if (state) {
                    mainbox.innerHTML = 'current page is ' + state.title; // 还原UI
                } else {
                    mainbox.innerHTML = ''
                }
            })
        })()
    </script>

</body>

</html>
  • 拦截 a 标签 的点击事件,阻止它的默认跳转行为

  • 使用 H5 的 history API 更新 URL

  • History.pushState(state, title [, url])

    • 往历史堆栈的顶部添加一个状态
    • 方法接收三个参数:一个状态对象, 一个标题, 和一个(可选的) URL
    • pushState 能更新当前 url,并且不引起页面刷新
  • History.replaceState(stateObj, title[, url]); 修改当前历史记录实体

  • window.onpopstate

    • 当活动历史记录条目更改时,将触发 popstate 事件
    • pushState 和 replaceState 对 url 的修改都不会触发 onpopstate
    • 它只会在浏览器某些行为下触发, 比如点击后退、前进按钮、a 标签点击等
  • 解决 load 发送请求 404 问题

    服务器需要把所有路由都重定向到根页面

  • 监听和匹配路由改变以更新页面

  • url 必须与当前的 url 同源

react-router

路由器组件

<BrowserRouter> 和 <HashRouter>

  • 路由器组件的作为根容器组件,<Route> 等路由组件必须被包裹在内才能够使用

  • <Router> 通过 context 把父组件透传给子孙组件

  • history.listen 就完成了对几种模式路由的监听

    路由组件都能感知路由变化,并拿到相应路由信息

BrowserRouter 对应 addEventListener

路由匹配组件

<Route> 和 <Switch>

  • 路由匹配组件通过匹配 path,渲染对应组件
  • 不同的匹配模式、path写法以及渲染方式
    • 匹配模式
      • 精确匹配
      • 严格匹配
      • 大小写敏感
    • 路径 path 写法
      • 字符串形式
      • 命名参数
      • 数组形式
    • 渲染方式
      • 通过子组件渲染
      • 通过 props.component 渲染
      • 通过 props.render 渲染
  • Route 对应 onpopstate

导航组件

<Link> 和 <NavLink>

  • 导航组件起到类似 a 标签跳转页面的作用
  • Link 对应 a

history 库

屏蔽几种模式之间操作会话历史的差别

matchPath

通过 path-to-regexp 实现路由匹配