摘要:而應用便是基于前端路由實現的所以便有了前端路由。因為兩種模式都需要調用一個方法來實現不同路由內容的刷新前端路由路由列表匹配當前的路由匹配不到則使用配置內容并渲染下面我們來實現兩種模式。
什么是路由?
路由這概念最開始是在后端出現的,在以前前后端不分離的時候,由后端來控制路由,服務器接收客戶端的請求,解析對應的url路徑,并返回對應的頁面/資源。前端路由的來源簡單的說 路由就是根據不同的url地址來展示不同的內容或頁面.
在很久很久以前~ 用戶的每次更新操作都需要重新刷新頁面,非常的影響交互體驗,后來,為了解決這個問題,便有了Ajax(異步加載方案),Ajax給體驗帶來了極大的提升。前端路由的兩種實現原理 1.Hash模式雖然Ajax解決了用戶交互時體驗的痛點,但是多頁面之間的跳轉一樣會有不好的體驗,所以便有了spa(single-page application)使用的誕生。而spa應用便是基于前端路由實現的,所以便有了前端路由。
如今比較火的vue-router/react-router 也是基于前端路由的原理實現的~
window對象提供了onhashchange事件來監聽hash值的改變,一旦url中的hash值發生改變,便會觸發該事件。
window.onhashchange = function(){ // hash 值改變 // do you want }2.History 模式
HTML5的History API 為瀏覽器的全局history對象增加的擴展方法。簡單來說,history其實就是瀏覽器歷史棧的一個接口。這里不細說history的每個API啦。具體可查閱 傳送門
window對象提供了onpopstate事件來監聽歷史棧的改變,一旦歷史棧信息發生改變,便會觸發該事件。
需要特別注意的是,調用history.pushState()或history.replaceState()不會觸發popstate事件。只有在做出瀏覽器動作時,才會觸發該事件。
window.onpopstate = function(){ // 歷史棧 信息改變 // do you want }
history提供了兩個操作歷史棧的API:history.pushState 和 history.replaceState
history.pushState(data[,title][,url]);//向歷史記錄中追加一條記錄
history.replaceState(data[,title][,url]);//替換當前頁在歷史記錄中的信息。
// data: 一個JavaScript對象,與用pushState()方法創建的新歷史記錄條目關聯。無論何時用戶導航到新創建的狀態,popstate事件都會被觸發,并且事件對象的state屬性都包含歷史記錄條目的狀態對象的拷貝。 //title: FireFox瀏覽器目前會忽略該參數,雖然以后可能會用上。考慮到未來可能會對該方法進行修改,傳一個空字符串會比較安全。或者,你也可以傳入一個簡短的標題,標明將要進入的狀態。 //url: 新的歷史記錄條目的地址。瀏覽器不會在調用pushState()方法后加載該地址,但之后,可能會試圖加載,例如用戶重啟瀏覽器。新的URL不一定是絕對路徑;如果是相對路徑,它將以當前URL為基準;傳入的URL與當前URL應該是同源的,否則,pushState()會拋出異常。該參數是可選的;不指定的話則為文檔當前URL。兩種模式優劣對比
對比 | Hash | History |
---|---|---|
觀賞性 | 丑 | 美 |
兼容性 | >ie8 | >ie10 |
實用性 | 直接使用 | 需后端配合 |
命名空間 | 同一document | 同源 |
本demo只是想說幫助我們通過實踐更進一步的理解前端路由這個概念,所以只做了簡單的實現~history模式404
當我們使用history模式時,如果沒有進行配置,刷新頁面會出現404。文件結構原因是因為history模式的url是真實的url,服務器會對url的文件路徑進行資源查找,找不到資源就會返回404。
這個問題的解決方案這里就不細說了,google一下,你就知道~ 我們在以下demo使用webpack-dev-server的里的historyApiFallback屬性來支持HTML5 History Mode。
|-- package.json |-- webpack.config.js |-- index.html |-- src |-- index.js |-- routeList.js |-- base.js |-- hash.js |-- history.js1.搭建環境
廢話不多說,直接上代碼~
package.json
{ "name": "web_router", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "dev": "webpack-dev-server --config ./webpack.config.js" }, "author": "webfansplz", "license": "MIT", "devDependencies": { "html-webpack-plugin": "^3.2.0", "webpack": "^4.28.1", "webpack-cli": "^3.2.1", "webpack-dev-server": "^3.1.14" } }
webpack.config.js
"use strict"; const path = require("path"); const webpack = require("webpack"); const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = { mode: "development", entry: "./src/index.js", output: { filename: "[name].js" }, devServer: { clientLogLevel: "warning", hot: true, inline: true, open: true, //在開發單頁應用時非常有用,它依賴于HTML5 history API,如果設置為true,所有的跳轉將指向index.html (解決histroy mode 404) historyApiFallback: true, host: "localhost", port: "6789", compress: true }, plugins: [ new webpack.HotModuleReplacementPlugin(), new HtmlWebpackPlugin({ filename: "index.html", template: "index.html", inject: true }) ] };2.開擼
首先我們先初始化定義我們需要實現的功能及配置參數。
前端路由 | 參數 | 方法 |
---|---|---|
x | 模式(mode) | push(壓入) |
x | 路由列表(routeList) | replace(替換) |
x | x | go(前進/后退) |
const MODE=""; const ROUTELIST=[]; class WebRouter { constructor() { } push(path) { ... } replace(path) { ... } go(num) { ... } } new WebRouter({ mode: MODE, routeList: ROUTELIST });
前面我們說了前端路由有兩種實現方式。
1.定義路由列表
2.我們分別為這兩種方式創建對應的類,并根據不同的mode參數進行實例化,完成webRouter類的實現。
src/routeList.jsexport const ROUTELIST = [ { path: "/", name: "index", component: "This is index page" }, { path: "/hash", name: "hash", component: "This is hash page" }, { path: "/history", name: "history", component: "This is history page" }, { path: "*", name: "notFound", component: "404 NOT FOUND" } ];src/hash.js
export class HashRouter{ }src/history.js
export class HistoryRouter{ }src/index.js
import { HashRouter } from "./hash"; import { HistoryRouter } from "./history"; import { ROUTELIST } from "./routeList"; //路由模式 const MODE = "hash"; class WebRouter { constructor({ mode = "hash", routeList }) { this.router = mode === "hash" ? new HashRouter(routeList) : new HistoryRouter(routeList); } push(path) { this.router.push(path); } replace(path) { this.router.replace(path); } go(num) { this.router.go(num); } } const webRouter = new WebRouter({ mode: MODE, routeList: ROUTELIST });
前面我們已經實現了webRouter的功能,接下來我們來實現兩種方式。
因為兩種模式都需要調用一個方法來實現不同路由內容的刷新,so~
index.htmljs/base.js前端路由
const ELEMENT = document.querySelector("#page"); export class BaseRouter { //list = 路由列表 constructor(list) { this.list = list; } render(state) { //匹配當前的路由,匹配不到則使用404配置內容 并渲染~ let ele = this.list.find(ele => ele.path === state); ele = ele ? ele : this.list.find(ele => ele.path === "*"); ELEMENT.innerText = ele.component; } }
ok,下面我們來實現兩種模式。
Hash模式
src/hash.jsimport { BaseRouter } from "./base.js"; export class HashRouter extends BaseRouter { constructor(list) { super(list); this.handler(); //監聽hash變化事件,hash變化重新渲染 window.addEventListener("hashchange", e => { this.handler(); }); } //渲染 handler() { this.render(this.getState()); } //獲取當前hash getState() { const hash = window.location.hash; return hash ? hash.slice(1) : "/"; } //獲取完整url getUrl(path) { const href = window.location.href; const i = href.indexOf("#"); const base = i >= 0 ? href.slice(0, i) : href; return `${base}#${path}`; } //改變hash值 實現壓入 功能 push(path) { window.location.hash = path; } //使用location.replace實現替換 功能 replace(path) { window.location.replace(this.getUrl(path)); } //這里使用history模式的go方法進行模擬 前進/后退 功能 go(n) { window.history.go(n); } }
History模式
src/history.jsimport { BaseRouter } from "./base.js"; export class HistoryRouter extends BaseRouter { constructor(list) { super(list); this.handler(); //監聽歷史棧信息變化,變化時重新渲染 window.addEventListener("popstate", e => { this.handler(); }); } //渲染 handler() { this.render(this.getState()); } //獲取路由路徑 getState() { const path = window.location.pathname; return path ? path : "/"; } //使用pushState方法實現壓入功能 //PushState不會觸發popstate事件,所以需要手動調用渲染函數 push(path) { history.pushState(null, null, path); this.handler(); } //使用replaceState實現替換功能 //replaceState不會觸發popstate事件,所以需要手動調用渲染函數 replace(path) { history.replaceState(null, null, path); this.handler(); } go(n) { window.history.go(n); } }3.小功告成
就這樣,一個簡單的前端路由就完成拉。
源碼地址
如果覺得有幫助到你的話,給個star哈~
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/100920.html
摘要:延伸閱讀學習與實踐資料索引與前端工程化實踐前端每周清單半年盤點之篇前端每周清單半年盤點之與篇前端每周清單半年盤點之篇 前端每周清單專注前端領域內容,以對外文資料的搜集為主,幫助開發者了解一周前端熱點;分為新聞熱點、開發教程、工程實踐、深度閱讀、開源項目、巔峰人生等欄目。歡迎關注【前端之巔】微信公眾號(ID:frontshow),及時獲取前端每周清單;本文則是對于半年來發布的前端每周清單...
摘要:前端每周清單專注前端領域內容,以對外文資料的搜集為主,幫助開發者了解一周前端熱點分為新聞熱點開發教程工程實踐深度閱讀開源項目巔峰人生等欄目。 前端每周清單專注前端領域內容,以對外文資料的搜集為主,幫助開發者了解一周前端熱點;分為新聞熱點、開發教程、工程實踐、深度閱讀、開源項目、巔峰人生等欄目。歡迎關注【前端之巔】微信公眾號(ID:frontshow),及時獲取前端每周清單;本文則是對于...
摘要:發布是由團隊開源的,操作接口庫,已成為事實上的瀏覽器操作標準。本周正式發布,為我們帶來了,,支持自定義頭部與腳部,支持增強,兼容原生協議等特性變化。新特性介紹日前發布了大版本更新,引入了一系列的新特性與提升,本文即是對這些變化進行深入解讀。 showImg(https://segmentfault.com/img/remote/1460000012940044); 前端每周清單專注前端...
摘要:的最后一個大招就是替換一些傳統的服務端語言,例如,,等,在業務層上面使用來開發服務端完全不成問題。更多的的使用細節和技巧建議關注美團博客大搜車論壇下一篇我們開啟如何結合和搭建一個開發環境和項目目錄 往期回顧 前面2期都講得是瀏覽器端的東西比較多,包括Webpack,雖然是Node處理的,但是還是瀏覽器端用的多,對于現在的前端開發來說,不懂一點服務端的東西,簡直沒辦法活,一般的招聘要求都...
閱讀 803·2021-09-06 15:02
閱讀 2446·2019-08-30 15:43
閱讀 2169·2019-08-30 11:26
閱讀 2376·2019-08-26 12:12
閱讀 3546·2019-08-23 18:24
閱讀 3262·2019-08-23 18:16
閱讀 700·2019-08-23 17:02
閱讀 2248·2019-08-23 15:34