在技術的世界,沒有奇蹟,只有精妙的,使人咂舌的技術運用。 ---- 南方小菜語
看到一句話,前端的革命性事件:ajax實現主動請求局部刷新,路由控制權的掌控;前者很好理解,後者越以爲很讓人驚喜,以往本身開發項目的固態思惟:javascript
一如前端路由深似海,今後再無進度條css
首先先不聊怎麼實現,先思考html
即點擊連接後url發生變化,每一個變化對應一個掛載點的內容(很天然的,咱們須要一個路由表,即路徑與掛載點內容的k-v,可用經過json實現)前端
傳統後端路由每次跳轉都刷新頁面,另發起一個新的請求,會給用戶帶來的白屏、耗時等較差體驗。所以前端路由採用的是當即加載的方式,再也不向服務器請求,而是加載路由對應的組件;而這種思路的實現主要採用兩種方案:hashchange 以及 historyvue
htmljava
<div>
<a href="javascript:;" data-href="/">home</a>
<a href="javascript:;" data-href="/book">book</a>
<a href="javascript:;" data-href="/movie">movie</a>
<div id="content"></div>
</div>
複製代碼
jsajax
//路由類
class Router {
constructor(opts) {
//路由表
this.routes = {},
this.init();
this.bindEvent();
opts.forEach(item => {
this.route(item.path, () => {
document.getElementById('content').innerHTML = item.component;
})
})
}
init() {
//頁面初始化時渲染路由
window.addEventListener('load', this.updateView.bind(this));
// 當活動歷史記錄條目更改時, 將觸發popstate事件。
// 若是被激活的歷史記錄條目是經過對history.pushState() 的調用建立的,
// 或者受到對history.replaceState() 的調用的影響,
// popstate事件的state屬性包含歷史條目的狀態對象的副本。
window.addEventListener('popstate', this.updateView.bind(this));
}
// 路由渲染
updateView() {
const currentUrl = window.location.pathname || '/';
this.routes[currentUrl] && this.routes[currentUrl]();
}
// 配置路由
route(path, fn) {
this.routes[path] = fn;
}
push(url){
window.history.pushState({},null,url);
this.updateView()
}
// 爲超連接綁定事件
bindEvent() {
const _this = this;
const links = document.getElementsByTagName('a');
[].forEach.call(links, link => {
link.addEventListener('click', function () {
const url = this.getAttribute('data-href');
console.log(url);
_this.push(url);
})
})
}
}
//實例化路由
const router = new Router([{
path: '/',
component: 'home'
}, {
path: '/movie',
component: 'movie'
}, {
path: '/book',
component: 'book'
}])
複製代碼
hash也存在下面幾個特性:數據庫
URL中hash值只是客戶端的一種狀態,也就是說當向服務器端發出請求時,hash部分不會被髮送。 hash值的改變,都會在瀏覽器的訪問歷史中增長一個記錄。所以咱們能經過瀏覽器的回退、前進按鈕控制hash的切換。 咱們能夠使用hashchange事件來監聽hash的變化。json
出發hsah變化的方式也有兩種,一種是經過a標籤,並設置href屬性,當用戶點擊這個標籤後,URL就會發生改變,也就會觸發hashchange事件了後端
<!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>Document</title>
</head>
<body>
<a href="#/" data-href="/">home</a>
<a href="#/book" data-href="/">book</a>
<a href="#/movie" data-href="/">movie</a>
<div id="content">
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
// window.onload = function (params) {
// window.location.href += '#/'
// }
const Home = {
template: '<div>home</div>'
}
const Book = {
template: '<div>book</div>'
}
const Movie = {
template: '<div>movie</div>'
}
class Router {
constructor(opts) {
// this.path = opts.path;
// this.component = opts.component;
// this.routes = opts.routes;
this.routes = {
}
// console.log(opts);
opts.forEach(item => {
this.route(item.path,()=>{
document.getElementById('content').innerHTML = item.component;
})
})
console.log(this.routes);
this.init()
}
bindEvent() { }
init() {
window.addEventListener('load',this.updateView.bind(this))
window.addEventListener('hashchange', this.updateView.bind(this))
}
updateView(e) {
// console.log(e,'updated');
// console.log(e.newURL.indexOf(e.oldURL));
// console.log(e.newURL.substring(e.newURL.indexOf(e.oldURL)));
const hashTag = window.location.hash.slice(1) || '/'
console.log(window.location.hash.slice(1));
this.routes[hashTag] && this.routes[hashTag]()
}
route(path,cb){
this.routes[path] = cb;
}
}
new Router([
{
path: '/',
component: 'home',
},
{
path: '/book',
component: 'book'
},
{
path: '/movie',
component: 'movie'
}
])
</script>
</body>
</html>
複製代碼