1.什么是单页面应用(SPA)
- 概念:单页面应用是指,一次性加载完所需要的资源,如HTML,css,js,不需要动态的加载资源,对SPA来说页面的切换就是指组件的切换。简单来说,SPA只有一个html页面,可以局部的刷新页面,而非加载整个页面。
2.SPA最终实现的效果
- url的地址发生变化,但不会向后端发送请求。
- 根据配置的路由信息,每次点击切换路由,会改变url地址栏,并切换到不同的组件显示。
3.SPA两种模式
- hash路由
- browser路由
4.SPA实现原理
- hash模式
- 浏览器的hashchange事件
- 发布订阅者模式
// 手写一个HashRouter
class HashRouter {
constructor() {
this.routes = {};
this.currentUrl = null;
}
// 一个hash值对应一个方法,发布订阅者模式
route(hash, callBack) {
this.routes[hash] = callBack || function () {};
}
updateCurrentUrl() {
this.currentUrl = window.location.hash || "/";
this.routes[this.currentUrl] && this.routes[this.currentUrl]();
}
init() {
// 注册监听事件
window.addEventListener("load", this.updateCurrentUrl.bind(this), false);
window.addEventListener(
"hashchange",
this.updateCurrentUrl.bind(this),
false
);
}
}
export default HashRouter;
// 案例
import React, { Component } from "react";
import HashRouter from "./handleRouter/HashRouter";
class App extends Component {
hashRouter = new HashRouter();
render() {
this.hashRouter.init();
this.hashRouter.route("#page", () => {
console.log("hashRouter");
});
return (
<>
<a href="/#page">page</a>
</>
);
}
}
export default App;
2. history模式
- 浏览器的popstate事件
- popstate事件触发依赖history.pushState()和history.replaceState(),但是只执行这两个方法并不会触发popState事件,只有在做出浏览器动作时,才会触发该事件,如浏览器的后退history.back()或者history.forward()加载历史列表中的下一个url
- 其他的原理和hashRouter基本一样,都采用了发布订阅者模式
// 手写一个BrowserRoter
class BrowserRouter {
constructor() {
this.routes = {};
this.currentUrl = null;
}
// 一个值对应一个方法,发布订阅者模式
route(hash, callBack) {
this.routes[hash] = callBack || function () {};
}
updateCurrentUrl(url) {
this.currentUrl = url;
this.routes[this.currentUrl] && this.routes[this.currentUrl]();
}
init() {
this.linkBind();
window.addEventListener(
"load",
this.updateCurrentUrl.bind(this, "/"),
false
);
window.addEventListener(
"popstate",
this.updateCurrentUrl.bind(this, window.location.pathname),
false
);
}
}
export default BrowserRouter;
import React, { Component } from "react";
import { Button } from "antd";
import BrowserRouter from "./handleRouter/BrowserRouter";
class App extends Component {
browserRouter = new BrowserRouter();
handleClickBrowser = (e) => {
e.preventDefault();
window.history.pushState({}, null, "/page1");
window.history.back();
};
handleClickBrowser2 = (e) => {
e.preventDefault();
window.history.pushState({}, null, "/page2");
window.history.back();
};
render() {
this.browserRouter.init();
this.browserRouter.route("/page1", () => {
console.log("browserRouter1");
});
this.browserRouter.route("/page2", () => {
console.log("browserRouter2");
});
return (
<>
<a href="#" onClick={this.handleClickBrowser}>
page1
</a>
<a href="#" onClick={this.handleClickBrowser2}>
page2
</a>
<div id="content"></div>
</>
);
}
}
export default App;