> 文章列表 > 浏览器状态同步和路由-前端路由和服务端路由原理

浏览器状态同步和路由-前端路由和服务端路由原理

浏览器状态同步和路由-前端路由和服务端路由原理

目录

前端路由和History API

浏览文境(Browser Context)

会话历史(Session History)

History API

history.go()切换当前会话,并不改变会话栈

history.back() === history.go(-1) & history.forward() === history.go(1)

pushState(state,title,url)

replaceState(state, title, url)

 实战服务端路由(做个负载均衡)

实战前端路由(单页面


前端路由和History API

浏览文境(Browser Context)

 

document文档

Browser Context (浏览的网页,网址等---浏览器的上下文)(共享的知识)

程序Present呈现出我们看到的网页效果

会话历史(Session History)

浏览器的会话栈Session Stack

 

History API

提供操作控制浏览器会话历史,维护会话栈(Session stack)的能力

history.go()切换当前会话,并不改变会话栈

 

history.back() === history.go(-1) & history.forward() === history.go(1)

pushState(state,title,url)

压栈,从当前会话栈前往新产生路径

不发生跳转、刷新页面、后退/前进到一个pushState出来的路径也不会渲染,让前端渲染

新增一个状态(State)到会话栈(Session Stack)

  • state:状态数据(自定义),可以通过history.state获取
  • title:预留字段,多数浏览器不使用
  • url:新状态的URL

 

replaceState(state, title, url)

替换当前页面路径

替换会话栈(Session Stack)中当前的状态

  • state:状态数据(自定义),可以通过history.state获取
  • title:预留字段,多数浏览器不使用
  • url:新状态的URL

 实战服务端路由(做个负载均衡)

  • 观察node.js实现服务端路由
  • 观察用Cluster启动多个实例进行负载均衡
const app = require('express')()
const fs = require('fs')
const path = require('path')
const pageDir = path.resolve(__dirname, "pages")//解析绝对路径, __dirname当前文件相对目录,文件夹
const htmls = fs.readdirSync(pageDir)// 读取目录下所有文件s
function displayHtmlFile(name) { // 写一个函数去显示,return (req, res) => {const filePath = path.resolve(pageDir, name + ".html") // 找到文件res.sendFile(filePath)// 返回文件}
}
htmls.forEach(file => { // 路由架构const [name, ext] = file.split('.')// 拿到文件名.扩展名app.get('/' + name, displayHtmlFile(name))
})
app.listen(3000)
// 负载均衡
const cluster = require('cluster');// 集群
const numCPUs = require('os').cpus().length; // 我的机器上有多少CPU核心
const express = require('express')
if (cluster.isMaster) {// 判断进程是在主进程console.log(`Master ${process.pid} is running`);// pid 进程id// Fork workers.for (let i = 0; i < numCPUs - 1; i++) {cluster.fork(); // 再执行一次本文件}cluster.on('exit', (worker, code, signal) => {console.log(`worker ${worker.process.pid} died`);});
} else {// 从进程// Workers can share any TCP connection// In this case it is an HTTP serverconst app = new express() // 此时express提供负载均衡,是操作系统层面提供的;// TCP层面开始分流,流量平均打到8080端口上// 可以加1级缓存,2级缓存,3级缓存app.listen(8080)console.log(`Worker ${process.pid} started`);
}
// 父进程和子进程是平等的/平行的,父进程关了不一定会关子进程
// 组织形式上有父子关系,Master父,Worker子,内部有一定的联系

实战前端路由(单页面)

前端路由配合服务端路由的使用

  • 在node端实现wildcard路由/product/*
  • 前端解析/product/:id和/product/list形成单页面应用
// 服务端node
const app = require('express')()
const path = require('path')
const { reset } = require('nodemon')
const htmlFile = path.resolve(__dirname, "pages/spa.html")// 多路由都指向单页面渲染的同一html
// /proucts /product/123 -> 
app.get(/\\/product(s|\\/\\d+)/, (req, res) => {// /d+数字res.sendFile(htmlFile)// 返回文件
})
app.listen(3000)

 

// html
<!DOCTYPE html>
<html>
<head><style>a {color: skyblue;cursor: pointer;}</style>
</head>
<body><h2>单页面应用示例</h2><div id='content'>渲染内容区</div><ul><li><a onclick='route("/products")'>列表</a></li><li><a onclick='route("/product/123")'>详情</a></li></ul><script>function pageList() {const html = `<ul><li>Apple</li><li>TicTok</li><li>Alibaba</li></ul>`document.getElementById('content').innerHTML = html}function pageDetail() {document.getElementById('content').innerHTML = "DETAIL"}function route(path) { // 单页面跳转history.pushState(null, null, path)matchRoute(pages, window.location.href) // 渲染}const pages = [// 路由列表{match: /\\/products/,// 路由匹配route: pageList // 调用函数},{match: /\\/product\\/\\d+/,route: pageDetail}]function matchRoute(pages, href) { // 路由匹配const page = pages.find(page => page.match.test(href))page.route()}window.onpopstate = function () { // 监听单页面应用浏览器前进后退,增加渲染,和history api是一组的matchRoute(pages, window.location.href)}matchRoute(pages, window.location.href)</script>
</body>
</html>