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 事件来改变状态,同时又不会刷新浏览器。
当 # 值发生变化时,就会触发 onhashchange 事件
// 显示当前时间
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。
export class HashHistory extends History{
  constructor(router: Router, base: ?string, fallback: boolean){
    super(router, base){
      if(fallback && checkFallback(this.base)){
        return
      }
      ensureSlash()
    }
  }
}
接着 history.transitionTo 做路由过渡
transitionTo (
    location: RawLocation,
    onComplete?: Function,
    onAbort?: Function
  ) {
    let route
    // catch redirect option https://github.com/vuejs/vue-router/issues/3201
    try {
      route = this.router.match(location, this.current)
    } catch (e) {
      this.errorCbs.forEach(cb => {
        cb(e)
      })
      // Exception should still be thrown
      throw e
    }
    const prev = this.current
    this.confirmTransition(
      route,
      () => {
        this.updateRoute(route)
        onComplete && onComplete(route)
        this.ensureURL()
        this.router.afterHooks.forEach(hook => {
          hook && hook(route, prev)
        })
        // fire ready cbs once
        if (!this.ready) {
          this.ready = true
          this.readyCbs.forEach(cb => {
            cb(route)
          })
        }
      },
      err => {
        if (onAbort) {
          onAbort(err)
        }
      }
    )
  }
遍历路由表,将我们的路由信息加入到其中,创建路由映射表
router.forEach((route) => {
  addRouteRecord(pathList, pathMap, nameMap, route);
});进行深度遍历
function addRouteRecord (
  pathList,
  pathMap,
  nameMap,
  route,
  parent,
  matchAs
) {
    route.children.forEach(function (child) {
      var childMatchAs = matchAs
        ? cleanPath((matchAs + "/" + (child.path)))
        : undefined;
      addRouteRecord(pathList, pathMap, nameMap, child, record, childMatchAs);
    });
  }
}pushState 设置的新 url,能与当前一样,这样也会记录添加到栈中,hash 设置的新值能不能与原来一样,一样的值不会触发动作将记录添加到栈中。pushstate 通过 stateObject 参数将
导航守卫
const {
  updated,
  deactivated,
  activated
} = resloveQueue(this.current.matched, route.matched){
  const queue = [].concat()
}
1、业务 2、性能、 3、技术栈 、vue、 java
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 , 匹配规则就是匹配上才会走这个路由.
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;最后更新于
这有帮助吗?