前端路由 router 原理及表现和实现

218 阅读3分钟

历史

前后端未分离时路由都由服务端控制。 前端/客⼾端 -> http 请求 -> 服务端 -> 根据 url 路径的不同,返回不同的 html/数据

这种⽅式的缺点和优点都⾮常明显

优点:SEO 效果好,直接返回一个HTML,⾸屏的耗时⼩(浏览器地址输入一个UR离开时--->页面上任意元素展示)

缺点:服务器压⼒⼤,构建HTML过程放在服务器上,前后端代码融合在⼀起不好维护,协作麻烦

单页应用

前后端未分离时路由都由服务端控制。 前端/客⼾端 -> http 请求 -> 服务端 -> 根据 url 路径的不同,返回不同的 html/数据

这种⽅式的缺点和优点都⾮常明显

优点:SEO 效果好,直接返回一个HTML,⾸屏的耗时⼩(浏览器地址输入一个UR离开时--->页面上任意元素展示)

缺点:服务器压⼒⼤,构建HTML过程放在服务器上,前后端代码融合在⼀起不好维护,协作麻烦

前端路由 router 原理及表现

vue -> hash, history react -> hash, history

要求:页面间的交互不刷新页面;不同 Url 会渲染不同的内容

Hash 和 History有什么区别

  1. hash 有#,history 没有
  2. hash 的#部分内容不会给服务端, history 的所有内容都会给服务端
  3. hash 通过 hashchange 监听变化,history 通过 popstate 监听变化
  4. histudy路由在应用部署的时候需要注意html文件访问(nginx指向)

1、Hash

特性

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

www.baidu.com/#/user -> http -> www.baidu.com/
www.baidu.com/#/list/deta… -> http -> www.baidu.com/

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

location.hash = '#aaa';
location.hash = '#bbb';
从#aaa 到#bbb,页面是不会刷新的

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

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

location.hash = '#aaa';
location.hash = '#bbb';
window.addEventLisenter('hashchange', () => {})

  1. 如何更改 hash

①. location.hash

location.hash = '#aaa'

②. html 标签的方式

<a href="#user"> 点击跳转到 user </a>

2、History

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

html5 history api

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, 替换当前历史记录

相关问题

问:pushState 时,会触发 popstate 吗?

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

问:popstate 到底什么时候才能触发。

答:点击浏览器后退按钮;点击浏览器前进按钮;js 调用 back 方法;js 调用 forward 方法;js 调用 go 方法

nginx配置

1、index.html存在服务器本地(nginx部署不在A服务器,index文件也在A服务器)

www.baidu.com/index
www.baidu.com/index/a

location /index/ {
    tey_files $uri $uri /home/dist/index.html
}

2、index.html存在远程服务器,oss/cdn

www.baidu.com/index
www.baidu-cdn.com/file/index.html 文件位置

location /index/ {
    rewrite ^ /file/index/html break;
    proxy_pass https://www.baidu-cdn.com;
}

3、实现基本的hash和histudy路由

//hash路由
class BaseRouter {
    constructor() {
        this.routes = {}; // 存储path以及callback的对应关系
        this.refresh = this.refresh.bind(this);
        window.addEventListener('load', this.refresh); // 处理页面首次加载
        window.addEventListener('hashchange', this.refresh); // 处理页面hash的变化
    }

    /**
     * route
     * @param {string} path 路由路径
     * @param {function} callback 回调函数
     */
    route(path, callback) {
        // 向this.routes存储path以及callback的对应关系
        this.routes[path] = callback || function () {};
    }

    refresh() {
        // 刷新页面
        const path = `/${location.hash.slice(1) || ''}`;
        console.log(location.hash);
        this.routes[path]();
    }

}

// 期望看到的是,点击三个不同的a标签,页面的背景色会随之改变

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

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


const Router = new BaseRouter();

Router.route('/', function () {
    changeBgColor('white');
});
Router.route('/green', function () {
    changeBgColor('green');
});
Router.route('/gray', function () {
    changeBgColor('gray');
});
//histudy路由
class BaseRouter {
    constructor() {
        this.routes = {};
        // location.hash; hash的方式
        this.init(location.pathname);
        this._bindPopState();
    }

    init(path) {
        window.history.replaceState({
            path
        }, null, path);
        const cb = this.routes[path];
        // cb && cb();
        if (cb) {
            cb();
        }
    }

    route(path, callback) {
        this.routes[path] = callback || function () {};
    }

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

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

const Router = new BaseRouter();

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

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

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