Skip to content
<

Vue 路由及异步组件

前端路由 router 原理及表现

背景

路由全部都是由服务端控制的, 前端代码和服务端代码过度融合在一起.

客户端/前端发起 http 请求 -> 服务器 -> url 路径去匹配不同的路由/返回不同的数据

优点: 直接返回一个 html, 渲染了页面结构. SEO的效果非常好, 首屏时间特别快.

在浏览器输入一个 url 开始 <-> 页面任意元素加载出来/渲染出来 => 首屏时间

缺点: 前端代码和服务端代码过度融合在一起, 开发协同非常的乱, 服务器压力大, 因为把构建 html 的工作放在服务端.

Ajax

异步请求, 浏览器端异步请求获取所需数据.

单页应用

页 -> html 文件

单页 -> 单个 html 文件 (以前 user/index.html. foods/index.html)

特点:

  1. 页面中的交互是不刷新页面的.

  2. 多个页面间的交互, 不需要刷新页面; 加载过的公共资源, 无需再重复加载.

支撑起单页应用这种特性的是什么?

前端路由

vue -> hash, history

react -> hash, history

  1. 页面间的交互不刷新页面

  2. 不同 url会渲染不同的内容

Hash

特性

  1. url中带有一个 # 符号, 但是 # 只是浏览器端/客户端的状态, 不会传递给服务端.

    www.baidu.com/#/user -> http -> www.baidu.com/

    www.baidu.com/#/list/test -> http -> www.baidu.com/

  2. hash 值的更改, 不会导致页面的刷新.

    location.hash = '#aaa';

    location.hash = '#bbb';

    从 #aaa 到 #bbb, 页面是不会刷新的

  3. hash 值的更改, 会在浏览器的访问历史中添加一条记录. 所以我们才可以通过浏览器的返回、前进按钮来控制 hash 的切换

  4. hash 值的更改, 会触发 hashchange 事件

    js
    window.addEventListener('hashchange', () => {});

如何更改 hash

  1. location.hash

  2. html 标签的方式

    html
    <a href="#user">点击跳转到 user</a>
html
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .container {
            width: 100%;
            height: 60px;
            display: flex;
            justify-content: space-around;
            align-items: center;

            font-size: 18px;
            font-weight: bold;

            background: black;
            color: white;
        }
        a:link,
        a:hover,
        a:active,
        a:visited {
            text-decoration: none;
            color: white;
        }
    </style>
</head>
<body>
    <div class="container">
        <a href="#gray">灰色</a>
        <a href="#green">绿色</a>
        <a href="#">白色</a>
        <button onclick="window.history.go(-1)">返回</button>
    </div>
    <script type="text/javascript" src="./index.js"></script>
</body>
</html>
js
/* index.js */
class BaseRouter {
    constructor() {
        this.routes = {};
        /* 改变 this 指向 */
        const refresh = this.refresh.bind(this);
        window.addEventListener('load', refresh);
        window.addEventListener('hashchange', refresh);
    }
    route(path, callback) {
        if (!callback) {
            throw new Error('callback is required!');
        }
        this.routes[path] = callback;
    }
    refresh() {
        const path = `/${location.hash.slice(1) || ''}`;
        this.routes[path]();
    }
}

const body = document.querySelector('body');
function changeBgColor(color) {
    return function() {
        body.style.backgroundColor = color;
    }
}
const Router = new BaseRouter();
Router.route('/', changeBgColor('white'));
Router.route('/green', changeBgColor('green'));
Router.route('/gray', changeBgColor('gray'));

History

hash 有个 # 符号, 不美观, 服务端无法接收到 hash 路径和参数

html5 history

js
window.history.back(); // 后退
window.history.forward(); // 前进
window.history.go(-3); // 接收 number 参数, 后退三个页面
window.history.pushState(null, null, path);
window.history.replaceState(null, null, path);

pushState/replaceState 的参数

  1. state, 是一个对象, 是一个与指定网址相关的对象, 当 popstate 事件触发的时候, 该对象会传入回调函数

  2. title, 新页面的标题, 浏览器支持不一, null

  3. url, 页面的新地址

pushState, 页面的浏览记录里添加一个历史记录

replaceState, 替换当前历史记录

History 的特性

  1. 没有 #

  2. pushState/replaceState 并不会触发popstate事件, 这时我们需要手动触发页面的重新渲染.

  3. 我们可以使用 popstate来监听 url 的变化.

  4. popstate 到底什么时候才能触发.

    4.1 点击浏览器后退按钮

    4.2 点击浏览器前进按钮

    4.3 js 调用 forward 方法

    4.4 js 调用 back 方法

    4.5 js 调用 go 方法

html
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .container {
            width: 100%;
            height: 60px;
            display: flex;
            justify-content: space-around;
            align-items: center;

            font-size: 18px;
            font-weight: bold;

            background: black;
            color: white;
        }
        a:link,
        a:hover,
        a:active,
        a:visited {
            text-decoration: none;
            color: white;
        }
    </style>
</head>
<body>
    <div class="container">
        <a href="/gray">灰色</a>
        <a href="/green">绿色</a>
        <a href="/">白色</a>
        <button onclick="window.history.go(-1)">返回</button>
    </div>
    <script type="text/javascript" src="./index-history.js"></script>
</body>
</html>
js
/* index-history.js */
class BaseRouter {
    constructor() {
        this.routes = {};
        this.go(location.pathname);
        this._bindPopState();
    }

    route(path, callback) {
        if (!callback) {
            throw new Error('callback is required!');
        }
        this.routes[path] = callback;
    }

    go(path) {
        // 跳转并执行对应的 callback
        window.history.pushState({
            path
        }, null, path);
        const cb = this.routes[path];
        cb && cb();
    }

    _bindPopState() {
        // 演示一个 popstate事件触发后, 会发生什么
        window.addEventListener('popstate', e => {
            const path = e.state && e.state.path;
            const cb = this.routes[path];
            cb && cb();
        });
    }
}

const Router = new BaseRouter();

const body = document.querySelector('body');
const container = document.querySelector('.container');

function changeBgColor(color) {
    return function () {
        body.style.backgroundColor = color;
    }
}

Router.route('/', changeBgColor('white'));
Router.route('/green', changeBgColor('green'));
Router.route('/gray', changeBgColor('gray'));

container.addEventListener('click', e => {
    if (e.target.tagName === 'A') {
        e.preventDefault();
        Router.go(e.target.getAttribute('href'));
    }
});

Hash 和 History

  1. hash 有 #, history 没有

  2. hash 的 # 部分内容不会给服务端, history 的所有内容都会给服务端

  3. hash 通过 hashchange 监听变化, history 通过 popstate 监听变化

vue.js router 使用详解

vue-cli 新建了一个 vue 项目, ts

导航守卫执行顺序

  1. 「组件」前一个组件的 beforeRouteLeave

  2. 「全局」的 router.beforeEach

    (3) 「组件」如果是路由参数变化, 触发 beofreRouteUpdate

  3. 「配置文件」里, 下一个的 beforeEnter

  4. 「组件」内部声明的 beforeRouteEnter

  5. 「全局」的 router.afterEach

vue-router 里面, 怎么记住前一个页面的滚动条的位置.

滚动到了 {top: 100}

list -> detail -> list

scrollBehavior 生效的条件

  1. 浏览器支持 history api

  2. 页面间的交互是通过 go, forward, back 或者浏览器的前进、返回按钮

js
/*
1. 手动点击浏览器返回或者前进按钮, 记住滚动条的位置, 基于 history, go, back, forward
2. 通过 router-link 返回, 并不会记住滚动条的位置
*/
const router = new VueRouter({
	mode: 'history',
	base: process.env.BASE_URL,
	routes,
	scrollBehavior: (to, from, savedPosition) => {
		console.log(savedPosition); // 保存的位置信息
		return savedPosition;
	}
});