# √hash路由和history路由原理

# √hash路由

原理: 使用load和hashchange监听路由的变化,做出相对应的反应

思路

  1. 创建一个路由class,包括构造函数constructor + 监听触发函数notice + 路由注册函数 route
  2. 在constructor中创建一个变量routes作为存储路由表的集合,同时用addEventListener()去监听hashchange和load,同时触发notice,注意notice的指向
  3. 创建router实例后,调用route方法将hash路由名称和callback传入,通过route函数操作,将hash路由和callback当做key和value存入routes
  4. 当路由变化时,通过监听load和hashchange,触发notice函数,notice会拿到location中的hash,然后调用this.routesx触发
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>hash路由demo</title>
</head>
<body>
  <ul>
    <li><a href="#/">我是主页</a></li>
    <li><a href="#/a">我是a页面</a></li>
    <li><a href="#/b">我是b页面</a></li>
  </ul>
</body>
</html>
<script type="text/javascript">
class HashRouter {
  constructor() {
    // 存储hash与callback键值对
    this.routes = {};
    // 保存当前的hash
    this.currentHash = '';
	
    // 绑定事件
    // const hashChangeUrl = this.hashChangeUrl.bind(this);
	const hashChangeUrl = this.hashChangeUrl.bind(this);
    // 页面加载事件
    window.addEventListener('load', hashChangeUrl, false);
    // 监听hashchange事件
    window.addEventListener('hashchange', hashChangeUrl, false);
  }
  // path路径和callback函数对应起来,并且使用 上面的this.routes存储起来
  route(path, callback) {
    this.routes[path] = callback || function() {};
  }
  hashChangeUrl() {
	  // console.log(this)
    /*
     获取当前的hash值
     location.hash 获取的值为:"#/a, 因此 location.hash.slice(1) = '/a' 这样的
    */
    this.currentHash = location.hash.slice(1) || '/';
    // 执行当前hash对应的callback函数
    this.routes[this.currentHash]();
  }
}
// 初始化
const Router = new HashRouter();
const body = document.querySelector('body');
const changeColor = function(color) {
  body.style.backgroundColor = color;
};
// 注册函数
Router.route('/', () => {
  changeColor('red');
});
Router.route('/a', () => {
  changeColor('green');
}); 
Router.route('/b', () => {
  changeColor('#CDDC39');
}); 
</script>

# √history路由

在html5中,History API 新增了操作会话浏览历史记录的功能。如下新增的几个方法

  • window.history.state 该参数是只读的,表示与会话浏览历史的当前记录相关联的状态对象
  • window.history.pushState(data, title, ?url): 在会话浏览历史记录中添加一条记录。
  • window.history.replaceState(data, title, ?url): 该方法用法和history.pushState方法类似,但是该方法的含义是将修改会话浏览历史的当前记录,而不是新增一条记录。也就是说把当前的浏览地址换成 replaceState之后的地址,但是浏览历史记录的总长度并没有新增。
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<title>history模式路由</title>
	</head>
	<body>
		<div>
			<a href="/">/</a><br/>
			<a href="/a">/a</a><br/>
			<a href="/b">/b</a><br/>
			<div id='view'>/</div>
			<button onclick="go()">go</button>
		</div>
	</body>
</html>
<script type="text/javascript">
	class HistoryRouter{
		constructor() {
		    this.routes={}
			const getState =this.getState.bind(this)
			window.addEventListener("popstate",getState)
		}
		route(path, callback) {
		    this.routes[path] = callback || function() {};
		}
		notice(path){
			history.pushState(null, null, path);
			this.routes[path] && this.routes[path]();
		}
		  getState() {
			  console.log(1)
			const path = window.location.pathname||"/";
			this.routes[path] && this.routes[path]();
		  }
	}
	
	let router =new HistoryRouter()
	let view = document.querySelector("#view")
	router.route("/",()=>{
		view.innerHTML='现在是首页页面'
	})
	router.route("/a",()=>{
		view.innerHTML='现在是a页面'
	})
	router.route("/b",()=>{
		view.innerHTML='现在是b页面'
	})
	
	window.addEventListener("click",function(e){
		console.log(e.target.tagName)
		
		e.preventDefault()
		if(e.target.tagName === 'A'){
			router.notice(e.target.getAttribute('href'))
		}
	})
	function go(){
		console.log(111)
		history.go(-1)
	}
	// 1. 创建一个路由class,包括构造函数constructor + 触发点击事件函数notice + 路由注册函数 route + 监听popstate变化触发函数getState
	// 2. 在constructor中创建一个变量routes作为存储路由表的集合,同时用addEventListener()去监听popstate,同时触发getState,注意getState的指向
	// 3. 创建router实例后,调用route方法将hash路由名称和callback传入,通过route函数操作,将hash路由和callback当做key和value存入routes
	// 4. 当路由点击a标签变化时,通过注册的监听事件触发notice,然后调用this.routes[x]()触发路由
	// 5. 当点击浏览器的返回按钮或者调用history.go等方法,会触发监听的popstate,获取url上的当前路由,触发
</script>
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>hash路由demo</title>
</head>
<body>
  <ul>
    <li><a href="/">我是主页</a></li>
    <li><a href="/a">我是a页面</a></li>
    <li><a href="/b">我是b页面</a></li>
  </ul>
</body>
</html>
<script type="text/javascript">
	
class HistoryRoutes {
  constructor() {
    // 保存对应键和函数
    this.routes = {};
    
    // 监听popstate事件
    window.addEventListener('popstate', (e) => {
      const path = this.getState();
      this.routes[path] && this.routes[path]();
    });
  }
  // 获取路由路径
  getState() {
    const path = window.location.pathname;
    return path ? path : '/';
  }
  route(path, callback) {
    this.routes[path] = callback || function() {};
  }
  init(path) {
    history.replaceState(null, null, path);
    this.routes[path] && this.routes[path]();
  }
  go(path) {
    history.pushState(null, null, path);
    this.routes[path] && this.routes[path]();
  }
}

window.Router = new HistoryRoutes();
console.log(location.pathname);
Router.init(location.pathname);

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

const changeColor = function(color) {
  body.style.backgroundColor = color;
};
// 注册函数
Router.route('/', () => {
  changeColor('red');
});
Router.route('/a', () => {
  changeColor('green');
}); 
Router.route('/b', () => {
  changeColor('#CDDC39');
}); 

const ul = document.querySelector('ul');
ul.addEventListener('click', e => {
  console.log(e.target);
  if (e.target.tagName === 'A') {
    e.preventDefault();
    Router.go(e.target.getAttribute('href'));
  }
});

</script>

# √hash&&history模式路由

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>原生实现hash和browser两种路由模式</title>
</head>
<body>
    <div class="router_box">
        <a href="/home" class="router" replace="true">主页</a>
        <a href="/news" class="router">新闻</a>
        <a href="/team" class="router">团队</a>
        <a href="/about" class="router">关于</a>
        <a href="/abcd" class="router">随便什么</a>
    </div>
    <div id="router-view"></div>
    <script>
        function Router(params){
            // 记录routes配置
            this.routes = params.routes || [];
            // 记录路由模式
            this.mode = params.mode || 'hash';
            console.log('this.mode', this.mode);
            // 初始化
            this.init = function(){
                // 绑定路由响应事件
                var that = this;
                document.querySelectorAll(".router").forEach((item,index)=>{
                    item.addEventListener("click",function(e){
                        // 阻止a标签的默认行为
                        if ( e && e.preventDefault ){
                            e.preventDefault(); 
                        }else{
                            window.event.returnValue = false;  
                        }  
                        if (that.mode == 'hash'){
                            // 判断是replace方法还是push方法
                            if (this.getAttribute("replace")){
                                var i = window.location.href.indexOf('#')
                                // 通过replace方法直接替换url
                                window.location.replace(
                                    window.location.href.slice(0, i >= 0 ? i : 0) + '#' + this.getAttribute("href")
                                )
                            }else{
                                // 通过赋值追加
                                window.location.hash = this.getAttribute("href");
                            }
                        }else{
                            if (this.getAttribute("replace")){
                                window.history.replaceState({}, '', window.location.origin+this.getAttribute("href"))
                                that.routerChange();
                            }else{
                                window.history.pushState({}, '', window.location.origin+this.getAttribute("href"))
                                that.routerChange();
                            }
                        }
                        
                    }, false);
                });
                // 监听路由改变
                if (this.mode == 'hash'){//hash模式时监听hashchange
                    window.addEventListener("hashchange",()=>{
                        this.routerChange();
                    });
                }else{//history模式时监听popstate事件
                    window.addEventListener('popstate', e => {
                        console.log(123);
                        this.routerChange();
                    })
                }
                this.routerChange();
            },
            // 路由改变监听事件
            this.routerChange = function(){
                if (this.mode == 'hash'){
                    let nowHash=window.location.hash;
                    let index=this.routes.findIndex((item,index)=>{
                        return nowHash == ('#'+item.path);
                    });
                    if(index>=0){
                        document.querySelector("#router-view").innerHTML=this.routes[index].component;
                    }else {
                        let defaultIndex=this.routes.findIndex((item,index)=>{
                            return item.path=='*';
                        });
                        if(defaultIndex>=0){
                            const i = window.location.href.indexOf('#')
                            window.location.replace(
                                window.location.href.slice(0, i >= 0 ? i : 0) + '#' + this.routes[defaultIndex].redirect
                            )
                        }
                    }
                }else{
                    let path = window.location.href.replace(window.location.origin, '');
                    let index=this.routes.findIndex((item,index)=>{
                        console.log('path...', path, 'item.path...', item.path);
                        return path == item.path;
                    });
                    if(index>=0){
                        document.querySelector("#router-view").innerHTML=this.routes[index].component;
                    }else {
                        let defaultIndex=this.routes.findIndex((item,index)=>{
                            return item.path=='*';
                        });
                        if(defaultIndex>=0){
                            console.log(window.location.origin+this.routes[defaultIndex].redirect)
                            window.history.pushState({}, '', window.location.origin+this.routes[defaultIndex].redirect)
                            this.routerChange();
                            
                        }
                    }
                }
            }
            // 调用初始化
            this.init();
        }
        new Router({
            mode: 'history',
            routes:[
                { path: '/home', component: '<h1>主页</h1><h4>新一代前端工程师:我们啥都会</h4>' },
                { path: '/news', component: '<h1>新闻</h1><h4>今天2019-6-11</h4>' },
                { path: '/team', component: '<h1>团队</h1><h4>WEB前端工程师</h4>' },
                { path: '/about', component: '<h1>关于</h1><h4>我们都要加油</h4>' },
                { path:'*', redirect:'/home'}
            ]
        });
    </script>
</body>
</html>
最后更新: 8/22/2021, 4:53:34 PM