原始阶段
早期浏览器就是根据浏览器网址的变化,跳转请求资源的。但是在单页应用盛行的今天又是怎么做到网址改变不刷新页面的呢。浏览器的 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.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 两者之间区别
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 , 匹配规则就是匹配上才会走这个路由.
<Route path="/render" render={ () => { 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;