2026前端面经
前端实现页面无刷新的方法 AJAX技术:通过异步请求获取数据并局部更新DOM,避免整页刷新 WebSocket:建立持久连接实现实时数据推送 History API:使用pushState/replaceState方法修改URL而不刷新页面 前端路由:通过hashchange或History API实现单页应用(SPA)路由切换 Server-Sent Events(SSE):服务器单向推送数据更
2026前端面经
1、前端怎么做到页面无刷新
前端无刷新更新页面,核心就是不重新加载整个 HTML 页面,只局部更新数据和视图,这也是现代 Web 应用(SPA)的核心能力。
- 原生 AJAX (XMLHttpRequest) —— 基础方案
最经典的无刷新方式,通过异步请求后端数据,局部更新 DOM。
适用:简单页面、局部数据刷新。
// 1. 创建请求对象
const xhr = new XMLHttpRequest();
// 2. 配置请求
xhr.open('GET', '/api/data', true);
// 3. 监听响应
xhr.onload = function() {
if (xhr.status === 200) {
const res = JSON.parse(xhr.responseText);
// 无刷新更新页面DOM
document.getElementById('content').innerHTML = res.msg;
}
};
// 4. 发送请求
xhr.send();
- Fetch API —— 现代原生方案
比 AJAX 更简洁,基于 Promise,现代浏览器默认支持,无需第三方库。
// 异步请求 + 无刷新渲染
fetch('/api/data')
.then(res => res.json())
.then(data => {
// 只更新需要修改的部分,不刷新页面
document.querySelector('#user-info').innerText = data.name;
})
.catch(err => console.error(err));
- Axios —— 企业最常用
封装好的 HTTP 库,用得最多,支持拦截器、异常处理。
axios.get('/api/user')
.then(res => {
// 无刷新渲染
document.getElementById('list').innerHTML = res.data.list.map(item => `<div>${item.name}</div>`).join('');
});
- 前端路由 (SPA 核心) —— 无刷新切换页面
真正实现 “无刷新跳转页面”,通过改变 URL 但不请求新 HTML,只切换组件。
核心技术
- hash 路由:url#/home(# 后面变化不会请求服务器)
- history 路由:url/home(H5 API,更美观)
原生简单实现(hash 路由)
// 监听 URL 哈希变化
window.addEventListener('hashchange', () => {
const hash = location.hash.slice(1);
// 根据hash无刷新切换内容
if(hash === 'home') {
document.getElementById('view').innerHTML = '<h1>首页</h1>';
} else if(hash === 'about') {
document.getElementById('view').innerHTML = '<h1>关于页</h1>';
}
});
框架路由(Vue/React)
这是现代无刷新页面的标准方案:
- Vue:vue-router
- React:react-router-dom
路由切换 → 组件更新 → 页面无刷新。
- WebSocket —— 实时无刷新推送
适合聊天、通知、数据实时更新,服务端主动推送给前端。
const ws = new WebSocket('ws://localhost:8080');
ws.onmessage = function(e) {
// 实时无刷新更新页面
document.getElementById('msg').innerText = e.data;
};
- 异步更新 DOM(纯前端无刷新)
不请求后端,仅前端交互无刷新:
- 点击切换内容
- 表单实时验证
- 折叠 / 展开面板
// 点击按钮无刷新修改文字
document.getElementById('btn').onclick = () => {
document.getElementById('text').innerText = '已更新';
};
总结
- 无刷新核心:异步请求 / 路由切换 + 局部 DOM 更新
- 现代开发:优先用 Axios + 前端路由(框架标配)
- 实时场景:用 WebSocket
- 所有方案都不会重新加载整个页面,体验更流畅
2、react实现一个钩子函数能够记录上次的state值
在 React 中,官方没有直接提供专门记录上一次 state 值的钩子,但最常用、最标准的方案是 useRef + useEffect 组合,手动封装一个自定义 Hook 来实现。
一、最简实现:封装 usePrevious 钩子
import { useRef, useEffect } from 'react';
// 自定义 Hook:获取上一次的值
function usePrevious(prevValue) {
// 创建 ref 存储上一次值
const prevRef = useRef();
// 每次值变化后,更新 ref 存储当前值(作为下一次的“上一次值”)
useEffect(() => {
prevRef.current = prevValue;
});
// 返回的是**更新前**的值
return prevRef.current;
}
export default usePrevious;
使用示例
import { useState } from 'react';
import usePrevious from './usePrevious';
function Demo() {
const [count, setCount] = useState(0);
// 获取上一次的 count
const prevCount = usePrevious(count);
return (
<div>
<p>当前值:{count}</p>
<p>上一次值:{prevCount}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}
二、原理说明(非常重要)
1)useRef
- 有一个稳定不变的 .current 属性
- 不会因为组件重新渲染而重置
- 非常适合用来持久存储数据
2)useEffect
- 会在组件渲染完成后执行
- 所以先渲染新值 → 再更新 ref
- 保证返回的永远是上一次渲染的值
3、Hash 路由 和 History 路由的核心区别
(1)Hash 路由
一、 什么是 Hash 路由
URL 中 # 号 后面的部分就是 hash 值。
https://xxx.com/#/home
# 后面的 /home 就是路由 hash
浏览器规定:
URL 里 # 及后面的哈希部分,改变时不会向服务器发请求,只会触发前端监听事件
前端路由就是利用这个特性,实现无刷新页面跳转。
二、核心底层原理
- hash 变化不会触发页面重载
- hash 改变不会请求后端接口
- 前端通过监听 hashchange 事件,捕获 hash 变化,匹配对应组件渲染
涉及两个核心浏览器事件
- hashchange
后面路径变化时触发(路由切换、前进后退) - load
页面首次加载时,读取初始 hash 进行首次路由匹配
三、Hash 路由的 URL 规则
http://domain/#/xxx/yyy
- 浏览器只会把 http://domain/ 发给后端
- #/xxx/yyy 完全留在浏览器本地,后端接收不到
- 刷新页面,服务器只返回根路径的 index.html,前端再根据当前 hash 渲染对应页面,永远不会 404
四、原生 JS 简易手写 Hash 路由(看懂就是吃透原理)
// 监听 hash 变化
window.addEventListener('hashchange', () => {
render()
})
// 首次进入页面执行
window.addEventListener('load', () => {
render()
})
// 根据 hash 渲染对应页面
function render() {
// 拿到 # 后面的路径
const path = location.hash.slice(1) || '/'
if(path === '/' || path === '/home') {
document.body.innerHTML = '首页'
} else if(path === '/about') {
document.body.innerHTML = '关于页'
}
}
核心 API:
- location.hash:获取当前哈希值
- hashchange:监听哈希变化
五、Hash 路由的特点
- 无需后端配置
不管部署在 Nginx、Apache、静态服务器,直接丢文件就能跑,刷新不 404。 - 兼容性极强
支持 IE 低版本,是老式项目、静态站点首选。 - URL 自带 # 号
地址栏有 #,不够优雅,不符合正规产品 URL 审美。 - #原生是页面锚点
hash 路由会和原生页面锚点功能冲突。 - 路由跳转不刷新、不请求后端
纯前端控制视图切换,性能快。 - 可以传参
/#/detail?id=1001
可以通过 location.search 或手动解析获取参数。
六、优点
- 部署简单,零后端配置
- 刷新永远不会 404
- 兼容性极好,兼容老旧浏览器
- 实现简单,底层原理易懂
- 本地调试、打包部署都不用额外处理
七、缺点
- URL 带 #,不美观、不正式
- 与页面原生锚点(跳到页面某位置)冲突
- 部分第三方平台、微信分享、支付、登录对带 # 路径兼容不好
- SEO 略差(搜索引擎对带 # 路径抓取不如 History 友好)
- 无法使用浏览器 History 完整路径语义
八、适用场景
- 静态网站、个人博客、Demo 项目
- 后端无法配合配置 rewrite 规则
- 需要兼容 IE 老旧浏览器
- 临时快速上线、简单活动页
九、Vue / React 中 Hash 路由怎么开启
Vue Router
import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
history: createWebHashHistory(),
routes: [...]
})
React Router
import { HashRouter } from 'react-router-dom'
<HashRouter>
<Routes>...</Routes>
</HashRouter>
十、一句话总结核心
Hash 路由就是利用 URL # 号不请求服务器、只在本地变化的特性,监听 hashchange 事件,由前端自己匹配路径、渲染组件,不用后端配置、永不 404,但地址带 # 不好看。
(2)history 路由
一、概述
History 路由是前端 SPA(单页应用)的主流路由模式,基于 HTML5 History API 实现,URL 形如 /user/123(无#),更优雅、利于 SEO,但需后端配合解决刷新 404 问题。
二、核心原理
1、底层 API(HTML5 新增)
history.pushState(state, title, url)
新增历史记录,修改 URL 但不刷新页面。
- state:关联的状态对象(popstate时可获取)
- title:浏览器忽略,传’’
- url:新路径(必须同源)
history.replaceState(state, title, url)
替换当前历史记录,不新增栈(无法后退到上一页)
popstate事件
点击前进 / 后退或调用history.back() / forward() / go(n)时触发,用于更新视图
2、前端实现流程
- 拦截链接点击:阻止默认跳转,改用pushState
- 监听popstate:前进 / 后退时更新视图
- 初始渲染:页面加载时读取location.pathname渲染对应组件
极简伪代码:
// 跳转
function goto(path) {
history.pushState({}, '', path);
render(path); // 手动渲染(pushState不触发popstate)
}
// 监听前进/后退
window.addEventListener('popstate', () => render(location.pathname));
// 拦截站内链接
document.addEventListener('click', e => {
const a = e.target.closest('a[href^="/"]');
if (a) { e.preventDefault(); goto(a.href); }
});
三、优缺点
优点
- ✅ URL 美观:无#,与传统页面一致
- ✅ SEO 友好:可配合 SSR / 预渲染提升收录
- ✅ 状态管理强:pushState可携带state对象传参
- ✅ 无锚点冲突:路径与页面内锚点(#id)互不干扰
缺点
- ❌ 需后端配置:刷新 / 直接访问子路径会 404,需服务器兜底到index.html
- ❌ 兼容性有限:不支持 IE9 及以下
四、后端配置(解决 404)
Nginx
location / {
try_files $uri $uri/ /index.html; # 所有请求指向index.html
}
Apache(.htaccess)
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.html [L]
五、框架中使用
Vue Router
import { createRouter, createWebHistory } from 'vue-router';
const router = createRouter({
history: createWebHistory(), // History模式
routes: [...]
});
React(React Router)
import { createBrowserRouter } from 'react-router-dom';
const router = createBrowserRouter([...]);
六、常见问题
- 刷新 404:后端未配置兜底,按上述 Nginx/Apache 配置解决
- 静态资源 404:资源路径用绝对路径(如/js/main.js)
- popstate不触发:pushState/replaceState不会触发,需手动渲染
History 路由是现代 SPA 的首选,URL 优雅、SEO 友好,核心依赖pushState/replaceState与popstate。使用时必须配置后端,否则刷新会 404。
4、路由守卫
一、什么是路由守卫?
路由守卫 = 路由跳转前后,提前设置的「拦截关卡」
在页面跳转前 / 跳转中 / 跳转后,拦截路由,做判断、放行、拦截、跳转。
就像过安检:要去某个页面,先过守卫检查,合格放行,不合格拦下跳登录页。
二、核心作用
- 登录鉴权:没登录不让进首页、个人中心
- 权限控制:普通用户不能进管理员页面
- 页面标题动态修改:进哪个页改对应 title
- 路由拦截、阻止跳转
- 页面跳转记录、埋点
三、路由守卫分类(Vue 全套,面试常问)
- 全局守卫(整个项目所有路由都生效)
① 全局前置守卫 beforeEach
跳转「之前」拦截,最常用
router.beforeEach((to, from, next) => {
// to:要去的页面
// from:从哪来
// next:放行/拦截/强制跳转
// 举例:没登录拦截去登录页
const token = localStorage.getItem('token');
if (!token && to.path !== '/login') {
next('/login'); // 拦截,跳登录
} else {
next(); // 放行
}
});
② 全局后置守卫 afterEach
跳转完成之后执行,没有 next
适合:改页面标题、页面访问埋点
router.afterEach((to, from) => {
document.title = to.meta.title || '默认标题';
});
- 路由独享守卫(只给某一个路由单独设置)
只针对当前这条路由生效,写在路由配置里:
const routes = [
{
path: '/admin',
component: Admin,
// 独享前置守卫
beforeEnter: (to, from, next) => {
// 判断是否是管理员
if (isAdmin) next();
else next('/404');
}
}
];
-
组件内守卫(写在组件页面内部)
进入组件、离开组件时拦截:beforeRouteEnter 进入组件前
beforeRouteUpdate 路由复用、参数变化时
beforeRouteLeave 离开组件前
<script>
export default {
// 进入前
beforeRouteEnter(to, from, next) {
next();
},
// 路由更新(同页面换参数)
beforeRouteUpdate(to, from, next) {},
// 离开当前页面前
beforeRouteLeave(to, from, next) {
// 比如未保存提示是否离开
}
}
</script>
四、三个核心参数必懂
-
to:目标路由(你要去哪里)
-
from:当前路由(你从哪里来)
-
next():必须调用,控制路由走向
next() 正常放行
next(‘/login’) 强制跳转到指定页
next(false) 阻止本次跳转
五、执行顺序(面试常考)
- 全局前置 beforeEach
- 路由独享 beforeEnter
- 组件内 beforeRouteEnter
- 组件渲染
- 全局后置 afterEach
路由守卫就是路由跳转过程中的拦截关卡,可以在跳转前后做登录鉴权、权限控制、页面标题修改、跳转拦截等逻辑。
分为三类:全局守卫、路由独享守卫、组件内守卫;核心参数 to 目标路由、from 来源路由、next 控制放行或跳转,其中 beforeEach 全局前置守卫项目最常用。
5、单页面应用
一、什么是 SPA
SPA(Single Page Application)单页面应用:
整个网站只有一个 HTML 页面,所有页面跳转不刷新浏览器,通过前端路由切换组件、动态替换页面内容。
传统多页 MPA:
点链接 → 浏览器整页刷新 → 重新请求服务器 → 返回新 HTML
SPA 单页:
打开只加载一次 index.html,之后跳转页面不刷新、不请求新 HTML,只通过 JS 动态渲染不同组件。
二、核心原理
- 项目只有一个入口 index.html
- 页面跳转靠 前端路由(Hash / History)
- 切换页面只替换局部组件内容,不重载整个页面
- 页面数据通过 AJAX/Fetch 异步请求后端接口
- 依靠 Vue / React / Angular 开发
三、SPA 怎么实现无刷新跳转
靠两个核心:
- 前端路由:Hash 模式 / History 模式
- 组件动态渲染:根据路径匹配对应组件,替换 区域
四、SPA 优点
- 页面无刷新,体验流畅,像原生 APP
- 前后端分离:前端负责页面,后端只给接口
- 减少服务器压力:不用每次跳转都渲染整页 HTML
- 组件复用性高,开发效率高
- 适合做中后台管理系统、移动端 H5
五、SPA 缺点(面试必问)
-
首屏加载慢
一次性要加载 JS、CSS、路由、状态库,白屏时间长 -
SEO 天生不友好
默认是客户端渲染 CSR,返回空 HTML,爬虫拿不到真实内容
(解决:用 SSR / SSG / 预渲染) -
路由刷新 404(History 模式)
需要后端 Nginx 配置兜底 -
项目打包后体积大,需要做分包、懒加载优化
-
不能用浏览器原生前进后退简单逻辑,要自己处理路由
SPA 单页面应用是整个项目只有一个 index.html,页面跳转依靠前端路由实现无刷新局部组件替换,不用每次请求服务端新页面;优点是页面跳转流畅、前后端分离、组件可复用;缺点是首屏加载慢、SEO 不友好、History 路由刷新会 404,常用 Vue、React 开发,可通过路由懒加载、SSR 解决缺点。
6、SPA和MPA的区别
SPA
- 首次请求:返回唯一 index.html + 打包 js/css
- 后续跳转:不再请求 html,只发 Ajax 接口拿数据,前端渲染组件
MPA
- 每点一个页面
- 浏览器重新请求服务器
- 服务器重新渲染、返回全新 html
- 整页刷新展示
SPA 优点
- 页面跳转无刷新、体验丝滑
- 前后端彻底分离,分工明确
- 组件复用率高,开发效率高
- 减少服务器页面渲染压力
SPA 缺点
- 首屏加载慢,容易白屏
- SEO 不友好(客户端渲染)
- History 路由刷新 404,需要后端配置
- 打包体积大,需要懒加载、分包优化
MPA 优点
- 首屏打开快
- 天然 SEO 友好
- 架构简单,部署不用特殊配置,无 404 问题
- 单页面资源独立,互相影响小
MPA 缺点
- 每次跳转整页刷新,体验差
- 前后端耦合,维护麻烦
- 服务器压力大
- 组件复用困难
SPA 单页面应用只有一个 HTML 入口,依靠前端路由实现无刷新局部组件切换,跳转体验好、前后端分离;缺点是首屏慢、SEO 不友好、History 路由刷新 404 需要后端配置。
MPA 多页面应用每个路由对应独立 HTML,跳转整页刷新、由后端返回页面;优点首屏快、天然 SEO 友好,缺点跳转体验差、前后端耦合、服务器压力大。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)