history与hash详解

原始阶段

早期浏览器就是根据浏览器网址的变化,跳转请求资源的。但是在单页应用盛行的今天又是怎么做到网址改变不刷新页面的呢。浏览器的 hash和 history 都能够做到。

  function
  return () {
      window.location.href = 'https://www.baidu.com'
  }

浏览器有两大api: location 和 history

location 有 href、reload、search、pathname、hash

history 有 pushState、replaceState、go、back、forward 等功能

hash

网址中的#称为位置的标识符,代表网页中的一个位置,浏览器的hash,将资源路径伪装成锚点,通过 onhashchange 事件来改变状态,同时又不会刷新浏览器。

当#值发生变化时,就会触发 hashchange 事件

    // 显示当前时间
    setInterval(function() {
        window.location.hash = 'J'
    }, 2000);
    window.onhashchange = funcRef;

    //以上操作将覆盖现有的事件处理程序。
    //为了添加一个新的事件处理程序,而不覆盖掉已有的其他事件处理程序,可以使用函数 "addEventListener"。

    // 监听onhashchange事件
    window.addEventListener("hashchange", function(e) {
        console.log('e: ', e);
        // 获取hash值判断页面状态
        var flag = location.hash && location.hash.substring(1);
        console.log('flag: ', flag);

    }, false);
  window.location.href = 'www.baidu.com/#'

  window.location.href = 'www.baidu.com/#/foo'

history

html5 的 history api

  • history.pushState

  • history.replaceState

  • history.back()

  • history.forward()

  • history.go()

可以在不刷新页面的前提下动态改变浏览器地址栏中的 url 地址,动态修改页面上所显示资源。

pushState(state, title, url); //添加一条历史记录,不刷新页面

state: 一个于执行网址相关的状态对象,popstate事件触发时,该对象会传回调函数, 如果不需要这个对象,此处可以填 null

title: 新页面的标题,所有浏览器可以忽略这个值,可以填 null

url: 新的网址,必须与前页面处在同一个域。浏览器的地址将显示这个网址。

replaceState 和 pushState的区别

  pushState()、 replaceState() 方法接收三个参数: stateObj、 title、 url。

  // 设置状态
  history.pushState({
      color: null
  }, null, "newHistory");

  // 监听状态
  window.onpopstate = function(event) {
      console.log(event.state);
      if (event.state && event.state.color === "newHistory") {
          document.body.style.color = "newHistory";
      }
  }

  // 改变状态
  history.back();
  history.forward();

replaceState(state, title, url); 替换当前的历史记录,历史栈没有变化,而 pushState 有。

hashchange 和 popstate

这两个都能监听url hash的改变

window.addEventListener('hashchange', function(e) {
    console.log(e)
})

window.addEventListener('popstate', function(e) {
    console.log(e)
})

1.popstate 事件: 历史记录发生改变时触发,调用 history.pushState() 或者 history.replaceState() 时,不会触发 popState 事件。

2.hashchange 事件:当页面的 hash 值改变的时候触发,history.pushState() 或者 history.replaceState() 也不会触发。

hash 和 history 两者之间区别

  • hash前端路由,无刷新 #

  • history 会去请求接口 /

  • hash 改变hash不会加载页面

  • history 修改页面url时,浏览器不会向后端发送请求。

hash模式下,# 之前的内容包含在 http 请求中,对后端来说,即使没有对路由做到全面覆盖,也不会报 404;

history 在刷新页面时,如果服务器中没有相应的响应资源,就会出现 404,如果 url 匹配不到任何静态资源,则应该返回同一个 index.html 页面。

vue-router 提供两种模式的原因

前端路由的核心:改变视图的同时不会向后端发出请求。

1.hash: hash 虽然出现在 url 中,但不会被包含 http 请求,

2.history 能够修改同源下面的任意url,hash只能修改 # 后面的部分,因为只能设置当前url同文档的url。

pushState 设置的新 url,能与当前一样,这样也会记录添加到栈中,hash设置的新值能不能与原来一样,一样的值不会触发动作将记录添加到栈中。pushstate通过stateObject参数将

react router v5

两个基础组件 BrowserRouter, Route 它的组件形式是 react-router 提供了一些router的核心api: router, route,switch 和 react-router-dom 提供了dom操作进行跳转的API,browserrouter,routerlink

例如

< Route path = "/help"
component = {
    Help
}
/>

当我们在浏览器地址栏中输入 http://localhost:3000/help 的时候,React Router 会匹配到 这一条记录,然后就会在当前位置渲染对应的component。

还可以导入 Link 组件进行链接的跳转

 < li > < Link to = "/help" > Help < /Link></li >

配置方式

如果没有指定 path 属性, 这时只要你打开项目, 无论访问什么, 都是匹配到这个 Route.

跟上面一样那个, 我们访问的路径, 都包含"/"的路径. 无论访问什么, 都是匹配到这个 Route.

  • 我们可以设置 exact , 匹配规则就是匹配上才会走这个路由.

  • { return

    我是匹配到的路由

    } } />

render属性。该属性是一个函数,当匹配到这个Route的时候,页面将会渲染出这个函数返回的内容。

匹配到的 Route 则会渲染出来, 没有匹配到的 Route, 则会替换成 null

import warning from "warning";
import React from "react";
import PropTypes from "prop-types";
import { createBrowserHistory as createHistory } from "history";//这里的history就是上面第二个例子中的historyModule
import Router from "./Router"; //对应第二个例子中的Router对象

/**
 * The public API for a <Router> that uses HTML5 history. //这里是重点
 */
class BrowserRouter extends React.Component {
  history = createHistory(this.props);
  render() {
    return <Router history={this.history} children={this.props.children} />;
  }
}

export default BrowserRouter;




//定义一个接口
export interface History {
    length: number;
    action: Action;
    location: Location;
    push(path: Path, state?: LocationState): void;
    push(location: LocationDescriptorObject): void;
    replace(path: Path, state?: LocationState): void;
    replace(location: LocationDescriptorObject): void;
    go(n: number): void;
    goBack(): void;
    goForward(): void;
    block(prompt?: boolean): UnregisterCallback;
    listen(listener: LocationListener): UnregisterCallback;
    createHref(location: LocationDescriptorObject): Href;
}





/**
 * The public API for putting history on context. //这里的道理类似于例子二中第二步
 */
class Router extends React.Component {

  static childContextTypes = {
    router: PropTypes.object.isRequired
  };

  getChildContext() {
    return {
      router: {
        ...this.context.router,
        history: this.props.history,
        route: {
          location: this.props.history.location,
          match: this.state.match
        }
      }
    };
  }

  state = {
    match: this.computeMatch(this.props.history.location.pathname)
  };

  computeMatch(pathname) {
    return {
      path: "/",
      url: "/",
      params: {},
      isExact: pathname === "/"
    };
  }

  componentWillMount() {
    const { children, history } = this.props;
    // Do this here so we can setState when a <Redirect> changes the
    // location in componentWillMount. This happens e.g. when doing
    // server rendering using a <StaticRouter>.
    this.unlisten = history.listen(() => {
      this.setState({
        match: this.computeMatch(history.location.pathname)
      });
    });
  }

  componentWillReceiveProps(nextProps) {
    warning(
      this.props.history === nextProps.history,
      "You cannot change <Router history>"
    );
  }

  componentWillUnmount() {
    this.unlisten();
  }

  render() {
    const { children } = this.props;
    return children ? React.Children.only(children) : null;
  }
}

export default Router;

最后更新于