首先我们了解一下资源请求:
1.什么是资源
在浏览器需要某一个数据或文件进行解析或者浏览器在解析某个脚本的时候需要数据进行DOM渲染等工作,那么这个数据或文件就是浏览器的资源
2.资源怎么获取
浏览器的资源都必须通过资源请求的方式或从缓存中调出的方式进行获取
3.资源请求的过程是什么
- 浏览器HTTP的三次握手
- 在第三次握手的同时进行通信
- 通信就是向某个主机请求资源
- 本地的计算机(文件系统)
- 本地或远程的服务器(文件系统)
- 资源从对象主机响应回客户端(浏览器)
- 浏览器对资源进行加载解析
网络请求:
前提:
1. 一定要有一个服务器
2. 文件一定是保存在服务器文件系统的
服务器程序(HTTP服务)
4.下面是我们写的一个服务器文件,通过浏览器可以请求该服务器资源
const express = require('express');
const {resolve} = require('path');
const app = express();
// 服务器 如果你想通过 http://localhost:8080/home.html
// app.use(express.static(resolve(__dirname, 'public')));
// GET http://localhost:8080/data => {}
//
app.get('/data', (req, res) => {
// application/json
res.json({
name: 'hello lucky',
age: 18
})
})
app.get('/', (req, res) => {
// 响应回home.html
// res.sendFile()
res.sendFile(resolve(__dirname, 'public/home.html'));
});
app.get('/list', (req, res) => {
// 响应回list.html
res.sendFile(resolve(__dirname, 'public/list.html'));
})
app.listen(8080, () => {
console.log('Server is running on PORT 8080');
})
资源目录 home和list资源存放在public文件夹下(普通的html文件)
以上就是浏览器通过地址访问服务器资源的过程,下面我们继续说下什么是单页面应用,什么是多页面应用
单页面应用/多页面应用
1.单页面应用(spa)
指只有一个页面的web应用,进入页面只需要加载一次相关资源(css、js等),所有内容都包含在此页面中,对每一个功能内容做组件化。单页面应用跳转,就是切换相关组件,仅仅只是刷新局部资源。
2.多页面应用(mpa)
指有多个独立页面的web应用,且进入每个页面都必须重复加载相关资源(js、css等)。多页面应用的跳转,需要整页资源刷新。
3.单页面应用和多页面应用区别?
- 页面数量和跳转方式不同。单页面应用通常只有一个主页,而多页面应用包含多个页面。单页面应用通过前端路由实现页面间的切换,仅刷新局部资源;多页面应用在页面跳转时需要整页刷新。
- 用户体验和性能不同。单页面应用提供快速流畅的页面切换和局部更新效果,对服务器压力较小,但初次加载时耗时较多;多页面应用有利于搜索引擎优化,每个页面都有自己的独立性,用户体验可能相对较差。
- 开发和维护难度不同。单页面应用的开发和维护成本较高,因为它需要处理更多的逻辑和数据;多页面应用虽然易于扩展和数据分析,但每个页面都需要单独设计和实现,维护起来较为复杂。
了解了单页面应用和多页面应用,那么我们知道单页面应用是通过路由控制页面切换,那么路由又有两种形式
hash
和history
,我们继续了解一下两种形式的工作原理
路由模式(hash/history)
1.hash 模式
hash模式,url如何控制页面切换
使用#来对URL进行截断,#在url中是锚点
#及后面的内容是浏览器端识别的内容
#前面的内容是向服务器请求的URL
下面我们通过代码来理解hash模式是如何工作的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app"></div>
<script src="./index.js" type="module"></script>
</body>
</html>
export default function Home () {
return `
<h1>Home</h1>
<button id="toListBtn">To List</button>
`;
}
import Home from './Home';
// 获取根节点下的容器app,也是单页面应用的入口文件
const oApp = document.querySelector('#app');
const init = () => {
// 监听 hashchange 事件,hash改变后触发
window.addEventListener('hashchange', handleRouter, false);
handleRouter();
bindEvent();
};
// hash 改变后的处理逻辑
function handleRouter () {
const hash = window.location.hash.substring(1);
switch (hash) {
case '/':
case '/home':
oApp.innerHTML = Home();
bindEvent();
break;
case '/list':
oApp.innerHTML = `
<h1>List</h1>
<button id="toHomeBtn">To Home</button>
`;
bindEvent();
break;
default:
oApp.innerHTML = `
<h1>404 Not Found</h1>
`;
break;
}
}
// 页面渲染后,给页面元素添加点击事件
function bindEvent () {
const oToListBtn = document.getElementById('toListBtn');
const oToHomeBtn = document.getElementById('toHomeBtn');
oToListBtn && oToListBtn.addEventListener('click', handleToListBtnClick, false);
oToHomeBtn && oToHomeBtn.addEventListener('click', handleToHomeBtnClick, false);
}
// 跳转页面
function handleToListBtnClick () {
window.location.hash = '#/list';
}
// 跳转页面
function handleToHomeBtnClick () {
window.location.hash = '#/';
}
init();
2.history 模式
了解了hash模式之后,history理解起来也比较简单,hash是通过监听hashchange事件来切换不同组件显示,而history则是监听state的改变来切换组件显示,如下代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app"></div>
// 请求服务器文件
<script src="http://localhost:8080/app.js"></script>
</body>
</html>
const oApp = document.getElementById('app');
const init = () => {
handleRouter();
bindEvent();
}
function handleRouter () {
const { state } = window.history;
console.log(state);
switch (state) {
case '':
case '/':
case 'home':
case null:
oApp.innerHTML = `
<h1>Home</h1>
<button id="toListBtn">To List</button>
`;
bindEvent();
break;
case 'list':
oApp.innerHTML = `
<h1>List</h1>
<button id="toHomeBtn">To Home</button>
` ;
bindEvent();
break;
default:
oApp.innerHTML = `
<h1>404 Not Found</h1>
`;
bindEvent();
break;
}
}
function bindEvent () {
const oToListBtn = document.getElementById('toListBtn');
const oToHomeBtn = document.getElementById('toHomeBtn');
oToListBtn && oToListBtn.addEventListener('click', handleToListBtnClick, false);
oToHomeBtn && oToHomeBtn.addEventListener('click', handleToHomeBtnClick, false);
}
function handleToListBtnClick () {
window.history.pushState('list', '', '/list');
handleRouter();
}
function handleToHomeBtnClick () {
window.history.pushState('home', '', '/');
handleRouter();
}
init();
const express = require('express');
const { resolve } = require('path');
const app = express();
app.use(express.static(resolve(__dirname, 'public')));
app.use((req, res) => {
res.sendFile(resolve(__dirname, 'public/index.html'));
});
app.listen(8080, () => {
console.log('Server is running on PORT 8080');
})