浅析单页应用中路由的histroy模式

基于 Vue 、React 的应用多数被称为 单页应用,就是打开一个页面,点击页面上的链接,浏览器地址栏的 URI 改变了,页面局部内容跟着改变,但整个页面并没有刷新,没有跳转。

# 例如

开始 http://h2o.xin/home

变成 http://h2o.xin/about

变成 http://h2o.xin/help

# 地址变,内容变,整个页面不刷新

下面基于我个人理解,分享一下大致原理。

我们要处理的内容有:
  • URL地址: 改变浏览器 url 地址,通常是 /xxxx 部分,即路由地址
  • 历史记录: 把改变后的 url 地址保存为历史记录,以便之后来回切换
  • 渲染组件: 新的路由地址,渲染相应新的组件
切换内容的场景包括:
  1. 超链接: 点超链接切换显示不同的组件内容
  2. 前进/后退按钮: 点击按钮切换显示不同的组件内容
  3. 浏览器刷新: 浏览器刷新后决定显示哪个组件

接下来我就以上面3个场景为主线来进行说明。

一. <a> 链接

框架中 <router-link> 之类的自定义标签,其最终实质都是基于 <a> 标签,然后绑定单击事件

在单击事件处理函数中,对 3 个内容操作处理:

1-URL 地址 & 2-历史记录

history.pushState(state, title[, url])

功能:

  • 改变浏览器地址栏中地址,只能修改 URI,相当于 window.location.pathname
  • 添加到浏览器历史记录
  • 不会实际请求. 就是说没有实际跳转刷新动作

参数:

  • state 状态对象,将来浏览器切换到相应的 url 地址时,可以拿到这个自定义的对象。它有640k个字符的限制
  • title 大多数浏览器忽略此参数。一般给空字符串
  • url 改变后的 URI 地址. 可以是当前网站根目录开始, 也可以是相对路径, 但不能是其它域名.
const state = { 'page_id': 1, 'user_id': 5 }
const title = ''
const url = 'abcd.html'

history.pushState(state, title, url)

history.replaceState(state, title[, url])

和 pushState() 几乎一样,只是 replace 是替换当前url,而非添加

3-渲染组件

在 <a> 事件处理函数中:

  1. 通过 window.location.pathname 获取当前路由地址
  2. Vue、React 等框架根据路由地址加载不同的组件内容,框架中封装好的方法,本文暂不讨论。

二.前进/后退 按钮

1-URL 地址 & 2-历史记录

浏览器的前进/后退按钮,就是借助“历史记录”切换 url 地址,比如从 /home , 变成 /about , 变成 /help ,但浏览器并没有缓存相应内容,因为那些内容不是浏览器向服务器请求而来的,而是 JS 代码加载组件得来。

3-渲染组件

通过 JS 代码监听 「popstate 事件」,来获知 前进/后退 按钮的按下。

在对应的事件处理函数中:

  1. 通过 window.location.pathname 获取当前路由地址
  2. Vue、React 等框架根据路由地址加载不同的组件内容,框架中封装好的方法,本文暂不讨论。

三.浏览器刷新

原理

假设当前浏览器路由地址为 http://h2o.xin/about ,此时刷新浏览器会向远程服务器发送请求。对于单页应用,路由地址是由客户端负责解析,远程服务器没有相应的处理。为了防止报错,服务器端把不能正确处理的请求响应为网站首页,例如 http://h2o.xin/index.html ,此时,浏览器上面地址还会保持为 http://h2o.xin/about。浏览器接收影响后,重新获取控制权,页面中的 JS 代码根据当前路由地址再次渲染相应的组件内容。

实现

不同的服务端有不同的实现方式。假设打包后的项目代码在 web 目录中

node.js + express 的方式

# 创建空项目, 安装 express
npm init -y
npm i express -D

创建 app.js

const path = require('path')
// 导入处理 history 模式的模块
const history = require('connect-history-api-fallback')
// 导入 express
const express = require('express')

const app = express()

// 注册处理 history 模式的中间件
app.use( history() )

// 处理静态资源的中间件,网站根目录 ./web
app.use(express.static(path.join(__dirname, './web')))

// 开启服务器,端口是 3000
app.listen(3000, () => {
  console.log('服务器开启,端口:3000')
})

执行

node app.js

nginx 方式

将 web 目录中内容复制到 nginx 的 html 目录中 (网站根目录, 不同版本名称不同)

编辑 nginx.conf

server {
  listen       80;
  server_name  localhost;
    
  location / {
    root   html;
    index  index.html index.htm;
    # 添加下面这一行就好
    try_files  $uri  $uri/   /index.html;
  }
    
  error_page  500  502  503  504  /50x.html;
  location = /50x.html {
    root    html;
  }
}

对前端感兴趣的朋友可以加Q群沟通(929330787)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水哥澎湃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值