专业编程教程与实战项目分享平台

网站首页 > 技术文章 正文

前端架构师之吾见-单页应用路由原理及实现

ins518 2024-09-30 21:19:33 技术文章 13 ℃ 0 评论

今日的内容比较简单,但却是单页应用(Single Page Application)不可或缺的组成部分。数据层与视图层相关技术点在前面篇幅中已经介绍完。本篇重点介绍单页应用前端路由原理及实现。在单页面应用中,路由跳转逻辑均由前端进行控制,服务器端作用可有可无。

目前有两种主流的前端路由实现技术,即基于 History API 的前端路由和基于 Hash 的路由。其中基于 History API 的前端路由需要配置好相应的 HTTP 服务器。配置方式将在实现环节进行讲解。

能力(特性)检测

两种前端路由各有利弊,具体到某个单页应用会进行指定或采用路由模块默认的路由类型。在这里面需要大伙脑海中涌现出一种思想:能力(特性)检测,即优先识别浏览器的能力。能力检测很好地实现了“渐进增强,优雅降级”的愿景。采用能力检测后,开发人员无需拘泥于纷繁的浏览器自身,只需确定浏览器支持的能力或特性,依据能力,给出解决方案。

具体到前端路由解决方案中,可以针对浏览器进行能力检测,如支持 History API 能力,将默认采用基于 History API 的前端路由实现方案,反之采用基于 Hash 的前端路由方案。

/**
*	History API 能力检测示例
*	@return boolean
**/
function isHistoryAvailable(){
  if (window.history) {
  	return true;
  } else {
  	return false;
  }
}

原理

  • History API

back

返回前一页

forward

在浏览器记录中前往下一页

go

在当前页面的相对位置从浏览器历史记录加载页面

pushState

按指定的名称和 URL 将数据 push 进会话历史栈

replaceState

按指定的数据、名称和 URL 更新历史栈上最新的入口

由于 History API 是基于真实 URL 进行操作的,如果未配置服务端,那么在进行页面刷新的时候可能会出现 404 报错的情况,这点需要格外注意。配置服务端也非常简单,以 nodejs server 配置为例:

// server.js
...
app.all('*', path.resolve('./index.html'));
...

当然也可以通过 nginx 配置来实现,将所有的请求默认反向代理至单页应用入口 html 地址。

# nginx config
...
rewirte ^/(.*) /index.html permanent;
...
  • 基于 Hash

由于浏览器窗口的 Hash 发生改变时会触发 hashchange 事件,因而通过监听 hashchange 事件,即可绑定路由与函数的映射关系,从而实现前端路由。

window.addEventListener('hashchange',({oldURL, newURL}) => {
// TODO
}, false);

实现

import { isHistoryAvailable } from '/utils/browserDetect.js';

export default class Router {
	constructor(props) {
    const { routers } = props;
  	this.routers = routers || {}; // 存储路由配置
    this.currentURL = '';
    this.historyRoute = isHistoryAvailable() ? true : fasle;
  };
  
  /**
  * 添加路由
  * @params path string;
  * @params callback function;
  * @return void;
  **/
  add(path, callback){
  	this.routers[path] = callback || () => {};
  }
  
  // 刷新
  refresh(){
  	this.currentURL = location.hash.replace(/^#*/gi, '');
    this.routes[this.currentURL]();
  }
  
  /**
  * 导航
  * @params path string
  **/
  nav(path){
  	const { historyRoute, refresh } = this;
    if (historyRoute) {
    	window.history.pushState(path);
    } else {
			window.addEventListener('hashchange', refresh.bind(this), false);  	
    }
  }
  
 ...
  
}
 

const appRoutes = [
  {path: '/', component: Home},
  {path: '/dash', component: Dashboard},
];

window.Router = new Router(appRoutes);

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表