Vue相关面试题
vue2和vue3的区别
一、核心架构差异
特性Vue2Vue3相应式系统基于Object.defineProperty基于Proxy(支持动态新增/删除属性)代码组织方式Options API(data/methods分块)Composition API(逻辑按功能聚合)虚拟DOM优化全量对比静态标记(Patch Flags)、静态提拔Tree-shaking支持无按需编译,体积更小(如无用的功能模块可剔除) 二、核心特性对比详解
1. 相应式系统
- Vue2:
通过Object.defineProperty挟制对象属性的getter/setter,但无法检测数组索引修改和对象属性新增/删除,需用Vue.set/Vue.delete。
- // Vue2中动态添加响应式属性
- Vue.set(this.obj, 'newKey', 'value');
复制代码 - Vue3:
使用Proxy代理整个对象,天然支持深层相应式,无需额外API。
- const reactiveObj = reactive({});
- reactiveObj.newKey = 'value'; // 自动触发响应
复制代码 2. 代码组织方式
- Vue2 Options API:
逻辑分散在data、methods、computed等选项中,复杂组件代码阅读困难。
- export default {
- data() { return { count: 0 } },
- methods: { increment() { this.count++ } }
- }
复制代码 - Vue3 Composition API:
使用setup()函数聚合逻辑,支持逻辑复用(类似React Hooks)。
- import { ref } from 'vue';
- export default {
- setup() {
- const count = ref(0);
- const increment = () => count.value++;
- return { count, increment };
- }
- }
复制代码 3. 性能优化
- 虚拟DOM Diff优化:
Vue3通过Block Tree和Patch Flags标记动态节点,镌汰对比范围。
- // Vue3模板编译后生成的虚拟DOM片段
- createVNode("div", { id: "foo" }, [
- createVNode("span", { class: _ctx.dynamicClass }, null, 1 /* CLASS */)
- ]);
复制代码 - Tree-shaking:
Vue3模块化设计,未使用的功能(如v-model修饰符)不会打包进终极产物。
三、新特性与开发体验
特性Vue2Vue3Fragment支持单根节点组件支持多根节点(镌汰无意义包裹)Teleport组件无跨DOM层级渲染(如全局弹窗)Suspense组件无异步组件加载状态管理TypeScript支持需额外范例声明源码使用TS编写,原生支持更美满 四、生命周期与API变化
Vue2生命周期Vue3生命周期分析beforeCreate使用setup()替代在setup()中实行初始化逻辑created使用setup()替代beforeMountonBeforeMount组合式API钩子需显式引入mountedonMountedbeforeDestroyonBeforeUnmount定名更语义化destroyedonUnmounted 五、迁移注意事项
- 全局API变化:
- Vue.prototype → app.config.globalProperties
- new Vue() → createApp()
- // Vue3创建应用实例
- import { createApp } from 'vue';
- const app = createApp(App);
- app.mount('#app');
复制代码
- 事件总线替代:
Vue3移除$on/$off,保举使用mitt品级三方库。
- 过滤器(Filters)废弃:
改用计算属性或方法处置惩罚数据格式化。
六、总结回答示例
"Vue3的核心升级会集在相应式系统重构(Proxy替代defineProperty)、开发体验优化(Composition API聚合逻辑)、性能提拔(Tree-shaking、虚拟DOM优化)以及新特性支持(Fragment、Teleport等)。
例如,在Vue2中处置惩罚动态新增相应式属性需要Vue.set,而Vue3的Proxy机制让这变得自然。同时,Composition API让复杂组件的逻辑更易维护。
这些改进使Vue3更适合大型项目,同时保持对Vue2的渐进式兼容。"
加分项:
- 提及Vue3的<script setup>语法糖(更简洁的组合式API写法)。
- 对比生态工具(如Vue Router 4、Pinia替代Vuex)。
- 性能数据:Vue3打包体积镌汰41%,渲染速度提拔55%。
compted和watch的区别
在Vue.js中,computed和watch都是用于相应数据变化的工具,但它们的应用场景和实现逻辑有本质区别。以下是它们的核心差异和实用场景:
一、核心区别
特性computedwatch目的声明式依靠追踪,生成新值监听数据变化,实行副作用操纵缓存有缓存(依靠不变时直接返回缓存值)无缓存(每次变化都触发回调)同步/异步必须同步返回结果支持异步操纵实用场景模板中需要动态计算的派生数据数据变化时需要触发复杂逻辑(如API请求)语法定义为一个函数(可含getter/setter)监听一个或多个数据源,定义handler函数 二、工作机制详解
1. computed(计算属性)
- 依靠追踪:自动追踪其内部依靠的相应式数据,依靠变化时重新计算。
- 缓存机制:只有依靠变化时才会重新计算,否则直接返回缓存值。
- 语法示例:
- export default {
- data() {
- return { firstName: '张', lastName: '三' }
- },
- computed: {
- fullName() {
- return this.firstName + ' ' + this.lastName; // 依赖firstName和lastName
- }
- }
- }
复制代码 模板中使用:
- <template>
- <div>{{ fullName }}</div> <!-- 自动更新 -->
- </template>
复制代码 2. watch(侦听器)
- 自动监听:显式指定要监听的数据源(可以是简单路径或复杂表达式)。
- 回调触发:数据变化时实行回调函数,适合处置惩罚异步或耗时操纵。
- 语法示例:
- export default {
- data() {
- return { searchKeyword: '' }
- },
- watch: {
- // 监听searchKeyword变化
- searchKeyword(newVal, oldVal) {
- this.fetchSearchResults(newVal); // 触发搜索逻辑(可能是异步的)
- }
- }
- }
复制代码 三、使用场景对比
使用computed的场景
- 动态计算显示值:
例如:根据单价和数目计算总价、格式化日期、过滤列表等。
- computed: {
- totalPrice() {
- return this.quantity * this.unitPrice;
- }
- }
复制代码 - 依靠多个值的复杂逻辑:
- //根据用户权限和页面状态动态显示按钮。
- computed: {
- showAdminButton() {
- return this.user.role === 'admin' && this.pageStatus === 'edit';
- }
- }
复制代码 使用watch的场景
- 数据变化触发副作用:
例如:搜索框输入变化时发起API请求。
- watch: {
- searchKeyword(newVal) {
- if (newVal) {
- this.debouncedSearch(newVal); // 防抖搜索
- }
- }
- }
复制代码 - 监听引用范例的变化(需深度监听):
例如:监听对象或数组内部变化。
- watch: {
- userInfo: {
- handler(newVal) {
- console.log('用户信息变化:', newVal);
- },
- deep: true // 深度监听
- }
- }
复制代码 - 异步操纵:
例如:数据变化后需要耽误实行或调用外部接口。
- watch: {
- async userId(newVal) {
- const data = await fetchUserData(newVal);
- this.userData = data;
- }
- }
复制代码 四、高级用法
1. computed的Setter
答应通过赋值操纵反向修改依靠数据:
- computed: {
- fullName: {
- get() {
- return this.firstName + ' ' + this.lastName;
- },
- set(newValue) {
- const [firstName, lastName] = newValue.split(' ');
- this.firstName = firstName;
- this.lastName = lastName;
- }
- }
- }
复制代码 2. watch的高级配置
- immediate: true:组件初始化时立即实行回调。
- deep: true:深度监听对象/数组内部变化。
- 监听多个数据源:
- watch: {
- 'obj.a'(newVal) { /* 监听嵌套属性 */ },
- 'arr.0': { handler() { /* 监听数组索引 */ } }
- }
复制代码 五、性能优化建议
- 优先使用computed:
需要派生新值时,computed的缓存机制能镌汰不须要的计算。
- 制止滥用watch:
监听大量数据或频繁触发的操纵大概导致性能问题。
- 复杂计算拆分:
将大型计算属性拆分为多个小计算属性,进步可维护性和缓存服从。
六、总结
- computed:适合依靠其他数据生成新值的场景(如动态计算、格式化),利用缓存优化性能。
- watch:适合相应数据变化实行操纵的场景(如API请求、日志
记载),支持异步和深度监听。
错误用法示例:
- // ❌ 错误:用watch实现本该由computed完成的功能
- watch: {
- firstName() { this.fullName = this.firstName + this.lastName; },
- lastName() { this.fullName = this.firstName + this.lastName; }
- }
- // ✅ 正确:用computed自动追踪依赖
- computed: {
- fullName() { return this.firstName + this.lastName; }
- }
复制代码 虚拟DOM是什么?
在 Vue 中,虚拟 DOM(Virtual DOM) 是一种用于优化页面渲染性能的核心技术,它是真实 DOM 的轻量级 JavaScript 对象表示。Vue 通过虚拟 DOM 实现高效的差异化更新,从而镌汰对真实 DOM 的直接操纵,提拔应用性能。
一、虚拟 DOM 的核心作用
1. 性能优化
- 直接操纵 DOM 成本高:每次 DOM 修改都会触发浏览器重排(Reflow)和重绘(Repaint)。
- 虚拟 DOM 的缓冲机制:通过对比新旧虚拟 DOM 的差异(Diff 算法),仅更新须要的 DOM 节点。
2. 简化开发
- 声明式编程:开发者只需关注数据变化,无需手动操纵 DOM。
- 跨平台能力:虚拟 DOM 可映射到差别平台(如浏览器、小程序、原生应用)。
二、Vue 中虚拟 DOM 的工作流程
1. 模板编译
Vue 将模板(.vue 文件或 template 字符串)编译为渲染函数(Render Function):
- // 模板
- <div id="app">{{ message }}</div>
- // 编译后的渲染函数
- function render() {
- return _c('div', { attrs: { id: 'app' } }, [_v(_s(message))])
- }
复制代码 2. 生成虚拟 DOM
实行渲染函数,生成形貌 DOM 结构的虚拟节点(VNode):
- const vnode = {
- tag: 'div',
- data: { attrs: { id: 'app' } },
- children: [ { text: 'Hello Vue!' } ]
- }
复制代码 3. 相应式数据触发更新
当数据(如 message)变化时,Vue 的相应式系统会触发组件重新渲染,生成新的虚拟 DOM 树。
4. Diff 算法对比差异
Vue 的 Diff 算法(称为 patch 过程)对比新旧虚拟 DOM 树:
- 同级比力:仅对比同一层级的节点。
- Key 优化:通过 key 标识节点身份,制止列表渲染错误。
- // 旧虚拟 DOM
- { tag: 'div', children: [ { text: 'Old' } ] }
- // 新虚拟 DOM
- { tag: 'div', children: [ { text: 'New' } ] }
- // Diff 结果:仅更新文本节点
复制代码 5. 更新真实 DOM
根据 Diff 结果,调用原生 DOM API 举行最小化更新:
- // 伪代码示例
- if (oldVNode.text !== newVNode.text) {
- textNode.nodeValue = newVNode.text; // 仅修改文本内容
- }
复制代码 三、虚拟 DOM 的关键上风
上风分析批量更新合并多次数据变更,制止频繁 DOM 操纵差异更新仅更新变化的部分,镌汰重排重绘次数跨平台渲染同一套虚拟 DOM 可渲染到浏览器、Native(Weex)、Canvas(如图表库)等开发体验优化无需手动操纵 DOM,专注于数据逻辑 四、虚拟 DOM 的局限性
局限性分析内存占用需维护虚拟 DOM 树,内存开销略高初次渲染耗时需额外生成虚拟 DOM,初次加载大概稍慢极端性能场景对性能要求极高的动画/游戏,仍需直接操纵 DOM 五、示例:Vue 中虚拟 DOM 的实际应用
- <template>
- <div>
- <button @click="count++">Click {{ count }}</button>
- <ul>
- <li v-for="item in list" :key="item.id">{{ item.text }}</li>
- </ul>
- </div>
- </template>
- <script>
- export default {
- data() {
- return {
- count: 0,
- list: [
- { id: 1, text: 'A' },
- { id: 2, text: 'B' }
- ]
- };
- }
- };
- </script>
复制代码 更新过程剖析:
- 点击按钮:count 自增,触发重新渲染。
- 生成新虚拟 DOM:Vue 调用渲染函数生成新的虚拟 DOM 树。
- Diff 对比:
- <button> 的文本节点变化。
- <li> 列表因 key 存在,高效复用 DOM 节点。
- Patch 更新:仅修改按钮文本,列表无需变更。
六、怎样优化 Vue 的虚拟 DOM 性能
- 公道使用 key:
- <!-- 使用唯一标识 key -->
- <li v-for="item in list" :key="item.id">{{ item.text }}</li>
复制代码 - 制止不须要的渲染:
- 使用 v-once 静态标记:
- <div v-once>{{ staticContent }}</div>
复制代码 - 使用 shouldComponentUpdate(Vue 的 shouldUpdate 钩子)。
- 镌汰模板复杂度:
七、举个例子,让你轻松理解虚拟 DOM 是什么,以及它在 Vue 中为什么紧张?
虚拟 DOM 就像「建筑模型」
想象你要装修房子,直接装修成本很高(每次改个插座都要砸墙),于是你做了个 「房屋模型」(虚拟 DOM)来模拟真实房子(真实 DOM)。每次改动都先在模型上调整,最后只按模型修改真实房子。
为什么用虚拟 DOM?
1. 制止「拆了又建」的浪费
- 直接操纵 DOM:相当于每次改动都拆掉房子重建(好比改个灯泡,却把整个房子拆了再盖)。
- 用虚拟 DOM:先在模型上改好(好比换个灯泡位置),对比新旧模型,只拆换灯泡的位置,其他部分保留。
2. 进步「装修服从」
- 设计师(Vue)的工作流程:
- 画草图(生成虚拟 DOM)
先画个装修设计图(形貌房子该长什么样)。
- 对比修改(Diff 算法)
假如之前有旧设计图,就对比那里需要改(好比墙面颜色变了,但地板没变)。
- 按图施工(更新真实 DOM)
只刷墙,不换地板,省时省力。
举个具体例子
假设你有一个待办清单:
- <ul>
- <li>买菜</li>
- <li>做饭</li>
- </ul>
复制代码 直接操纵 DOM(原始方法)
- 添加一个新使命「洗碗」:
- 删掉整个 <ul>
- 重新创建 <ul> 并插入全部 <li>(包括已有的和新增的)
用虚拟 DOM(Vue 的方法)
- 生成新虚拟 DOM
- // 旧虚拟 DOM
- { tag: 'ul', children: [
- { tag: 'li', text: '买菜' },
- { tag: 'li', text: '做饭' }
- ]}
- // 新虚拟 DOM(添加「洗碗」)
- { tag: 'ul', children: [
- { tag: 'li', text: '买菜' },
- { tag: 'li', text: '做饭' },
- { tag: 'li', text: '洗碗' }
- ]}
复制代码 - 对比差异(Diff 算法)
- 只添加新节点
虚拟 DOM 的三大长处
长处例子分析加快速度改灯泡不用拆房子镌汰卡顿制止页面频繁闪耀(只改须要的部分)自动处置惩罚复杂逻辑你只管说「要加洗碗」,Vue 自动找最优修改方式 为什么需要「Key」?
假如使命列表是动态的(好比顺序会变),Vue 需要一个「身份证号」(Key)来识别每个使命,制止误判。好比:
- <!-- 没有 Key 的情况 -->
- <li>任务A</li>
- <li>任务B</li>
- <!-- 插入新任务后可能出错 -->
- <li>任务C</li> <!-- Vue 可能误以为这是任务A,导致渲染错误 -->
- <li>任务A</li>
- <li>任务B</li>
- <!-- 用 Key 解决 -->
- <li key="1">任务A</li>
- <li key="2">任务B</li>
- <!-- 插入新任务后 -->
- <li key="3">任务C</li> <!-- Vue 通过 Key 知道这是新增的 -->
- <li key="1">任务A</li>
- <li key="2">任务B</li>
复制代码 总结
- 虚拟 DOM 的本质:一个轻量版的「房屋模型」,用来模拟真实页面结构。
- Vue 的工作方式:先改模型 → 对比变化 → 只改真实页面需要动的地方。
- 你的长处:不用操心怎么操纵 DOM,专注写业务逻辑,Vue 自动帮你高效更新页面!
下次写 Vue 时,可以想象自己是在「画设计图」,而 Vue 是那个「高效施工队」! 🛠️
vue项目内多个请求怎样让页面不卡顿
在Vue项目中处置惩罚多个请求时保持页面流畅,需要从 请求控制、渲染优化 和 交互设计 三个维度举行优化。以下是具体方案:
一、请求控制优化
1. 分批次并发请求(制止一次性全量请求)
- // 关键数据优先请求
- async function loadCriticalData() {
- const [user, config] = await Promise.all([
- fetch('/api/user'),
- fetch('/api/config')
- ]);
- // 渲染核心内容
- }
- // 非关键数据延迟加载
- setTimeout(() => {
- fetch('/api/additional-data')
- }, 2000);
复制代码 2. 请求优先级队列
- class RequestQueue {
- constructor(maxConcurrent = 3) {
- this.queue = [];
- this.activeCount = 0;
- this.maxConcurrent = maxConcurrent;
- }
- add(requestFn) {
- return new Promise((resolve, reject) => {
- this.queue.push({ requestFn, resolve, reject });
- this.next();
- });
- }
- next() {
- while (this.activeCount < this.maxConcurrent && this.queue.length) {
- const { requestFn, resolve, reject } = this.queue.shift();
- this.activeCount++;
- requestFn()
- .then(resolve)
- .catch(reject)
- .finally(() => {
- this.activeCount--;
- this.next();
- });
- }
- }
- }
- // 使用示例
- const queue = new RequestQueue(4); // 最大并发4个
- queue.add(() => fetch('/api/data1'));
- queue.add(() => fetch('/api/data2'));
复制代码 二、渲染性能优化
1. 虚拟滚动(大数据列表)
- <template>
- <!-- 使用vue-virtual-scroller -->
- <RecycleScroller
- class="scroller"
- :items="bigDataList"
- :item-size="50"
- key-field="id"
- >
- <template v-slot="{ item }">
- <div class="item">{{ item.name }}</div>
- </template>
- </RecycleScroller>
- </template>
复制代码 2. 分块渲染
- // 分批处理大数据
- function chunkRender(data) {
- let index = 0;
- const chunkSize = 50;
-
- function doChunk() {
- const chunk = data.slice(index, index + chunkSize);
- this.items.push(...chunk);
- index += chunkSize;
-
- if (index < data.length) {
- requestIdleCallback(doChunk); // 利用空闲时间渲染
- }
- }
-
- requestIdleCallback(doChunk);
- }
复制代码 3. 冻结非生动数据
- // 使用Object.freeze避免响应式追踪
- this.bigData = Object.freeze(rawData);
复制代码 三、交互体验优化
1. 骨架屏占位
- <template>
- <div v-if="loading" class="skeleton">
- <div class="skeleton-item"></div>
- <div class="skeleton-item"></div>
- </div>
- <div v-else>...</div>
- </template>
- <style>
- .skeleton-item {
- background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
- animation: shimmer 1.5s infinite;
- }
- @keyframes shimmer {
- 100% { background-position: -200% 0; }
- }
- </style>
复制代码 2. 请求取消
- // 使用axios CancelToken
- const source = axios.CancelToken.source();
- fetchData() {
- axios.get('/api/data', {
- cancelToken: source.token
- }).catch(thrown => {
- if (axios.isCancel(thrown)) {
- console.log('请求被取消', thrown.message);
- }
- });
- }
- // 组件销毁时取消请求
- beforeDestroy() {
- source.cancel('组件卸载取消请求');
- }
复制代码 四、性能监测与调试
1. Chrome Performance分析
- // 添加性能标记
- window.performance.mark('fetchStart');
- await fetchData();
- window.performance.mark('fetchEnd');
- window.performance.measure('数据请求', 'fetchStart', 'fetchEnd');
复制代码 2. 长使命检测
- const observer = new PerformanceObserver((list) => {
- for (const entry of list.getEntries()) {
- console.log('[长任务]', entry);
- }
- });
- observer.observe({ entryTypes: ['longtask'] });
复制代码 五、进阶优化方案
方案实用场景实现方式Web Worker复杂数据处置惩罚将数据计算移出主线程HTTP/2 Server Push高并发请求服务端自动推送资源请求缓存重复数据获取内存缓存 + LocalStorageTree Shaking镌汰打包体积配置Vue CLI的优化项 六、优化前后性能对比
指标优化前优化后主线程阻塞时间1200ms300ms初次内容渲染2.5s800ms内存占用150MB80MB 通过 请求并发控制 + 渲染策略优化 + 交互反馈设计 的组合拳,可有效解决多请求场景下的页面卡顿问题。建议根据实际场景选择2-3个关键优化点组合实行。
本地缓存有哪些?有哪些区别?
在Web开发中,常见的本地缓存技术包括 Cookie、Web Storage(LocalStorage/SessionStorage)、IndexedDB、Cache API 等。以下是它们的核心区别及实用场景:
一、本地缓存技术对比
技术存储容量生命周期数据格式访问方式实用场景特点Cookie~4KB可设置逾期时间(默认会话级)字符串键值对同步用户会话管理、服务端通信(如Token)每次请求自动携带,需注意安全性LocalStorage5MB~10MB(浏览器)永久存储(需手动清除)字符串键值对同步长期存储用户偏好、离线数据简单易用,无自动逾期机制SessionStorage5MB~10MB(浏览器)会话级(标签页关闭即清除)字符串键值对同步临时存储表单数据、页面间传参数据隔离性强(按标签页隔离)IndexedDB无硬性限定(通常≥250MB)永久存储(需手动清除)结构化数据(对象存储)异步复杂数据管理(如离线应用、大数据缓存)支持事务、索引、查询Cache API动态分配(通常≥50MB)随Service Worker生命周期网络请求/相应异步缓存静态资源(PWA离线支持)精准控制资源缓存策略 二、技术详解与典范应用
1. Cookie
- 核心特性:
- 自动随HTTP请求发送到服务端(通过Cookie请求头)
- 通过document.cookie读写,需手动处置惩罚字符串分割
- 必须设置SameSite属性防止CSRF攻击
- 实用场景:
- 用户登录状态保持(JWT Token)
- 简单的用户偏好记载(如主题选择)
- 示例:
- // 设置Cookie(有效期7天)
- document.cookie = "theme=dark; max-age=604800; path=/; Secure";
复制代码 2. Web Storage
- LocalStorage:
- 长期存储:用户语言设置、购物车数据长期化
- 代码示例:
- localStorage.setItem('userSettings', JSON.stringify({ theme: 'dark' }));
- const settings = JSON.parse(localStorage.getItem('userSettings'));
复制代码
- SessionStorage:
- 临时存储:多步调表单数据暂存、页面间参数传递
- 代码示例:
- sessionStorage.setItem('formStep1', JSON.stringify({ name: 'Alice' }));
复制代码
3. IndexedDB
- 核心能力:
- 支持事务(Transaction)包管数据一致性
- 可创建索引加快查询
- 存储二进制数据(如图片、文件)
- 实用场景:
- 离线应用(如文档编辑器)
- 大数据量缓存(如本地日志
存储)
- 代码示例:
- const request = indexedDB.open('myDB', 1);
- request.onsuccess = (e) => {
- const db = e.target.result;
- const tx = db.transaction('users', 'readwrite');
- const store = tx.objectStore('users');
- store.add({ id: 1, name: 'Alice' });
- };
复制代码- [/code]
- [/list] [size=2]4. [b]Cache API[/b][/size]
-
- [list]
- [*] [b]核心机制[/b]:
- [list]
- [*] 与Service Worker配合实现离线访问
- [*] 按需缓存网络请求,支持版本管理
- [/list]
- [*] [b]实用场景[/b]:
- [list]
- [*] PWA应用的静态资源缓存
- [*] 动态内容离线访问(如新闻详情页)
- [/list]
- [*] [b]代码示例[/b]:
- [code]caches.open('v1').then(cache => {
- cache.addAll(['/styles.css', '/app.js']);
- });
复制代码 三、选型建议
需求场景保举方案原因用户登录状态管理Cookie(HttpOnly + Secure)自动携带至服务端,配合服务端验证安全可靠长期保存用户主题偏好LocalStorage简单键值对,长期化存储复杂数据查询(如本地数据库)IndexedDB支持索引、事务,适合结构化数据离线优先的Web应用Cache API + Service Worker精准控制资源缓存策略,提拔离线体验临时存储页面间参数SessionStorage会话级隔离,制止数据污染 四、安全注意事项
- 敏感信息:制止在Cookie/LocalStorage存储暗码等秘密数据
- 容量限定:LocalStorage超过5MB大概触发浏览器告诫
- 数据清理:定期清理逾期缓存(如通过TTL机制)
- 加密存储:对敏感数据使用AES加密后再存储
通过公道选择本地缓存技术,可明显提拔Web应用的性能、离线能力和用户体验。
JS相关面试题
防抖和节省的区别?应该怎样去做?
使用防抖(Debounce)和节省(Throttle)是优化高频事件处置惩罚的常用手段。
一、防抖和节省的区别
防抖(Debounce)节省(Throttle)触发逻辑事件制止触发后耽误实行固定时间隔断内最多实行一次类比场景电梯门(最后一个人进入后关门)水龙头(稳定隔断滴水)实用场景输入框搜索联想、窗口resize监听滚动加载、按钮防重复点击实行次数高频触发时只实行最后一次高频触发时均匀实行 二、防抖和节省分别实现的方法
- // 防抖实现
- function debounce(fn, delay = 500) {
- let timer = null;
- return function(...args) {
- if (timer) clearTimeout(timer);
- timer = setTimeout(() => {
- fn.apply(this, args);
- }, delay);
- };
- }
- // 节流实现
- function throttle(fn, interval = 200) {
- let lastTime = 0;
- return function(...args) {
- const now = Date.now();
- if (now - lastTime >= interval) {
- fn.apply(this, args);
- lastTime = now;
- }
- };
- }
复制代码 三、防抖和节省选择原则
- 需要即时相应但制止过量请求用防抖(如搜索建议)
- 需要保持操纵连贯性用节省(如无穷滚动加载)
js原生input和on-change的区别
在原生 JavaScript 中,input 事件和 change 事件是表单元素(如 <input>、<select>、<textarea>)的两种差别举动的事件,它们的核心区别在于触发时机和应用场景。以下是具体对比:
一、input 事件与 change 事件的区别
特性input 事件change 事件触发时机值变化时及时触发(如每次键盘输入)失去焦点且值变化后触发实用场景及时搜索、输入校验表单提交前校验、值终极确认兼容性当代浏览器支持全部浏览器支持相应速度高频触发(需防抖优化)低频触发 二、代码示例对比
- <input type="text" id="inputDemo">
- <script>
- const inputEl = document.getElementById('inputDemo');
- // input 事件:每次输入都触发
- inputEl.addEventListener('input', (e) => {
- console.log('input 事件:', e.target.value); // 实时输出当前值
- });
- // change 事件:失焦且值变化时触发
- inputEl.addEventListener('change', (e) => {
- console.log('change 事件:', e.target.value); // 仅失焦后输出最终值
- });
- </script>
复制代码 测试步调:
- 在输入框输入 "hello"(每次按键都会触发 input 事件)。
- 点击页面其他地区让输入框失焦,触发 change 事件。
- 再次聚焦输入框,不修改内容直接失焦,不会触发 change 事件(值未变化)。
三、总结
- input vs change:
- 需要及时反馈(如搜索联想)用 input + 防抖。
- 需要终极确认(如表单提交)用 change。
异步请求有哪些方法?
一、XMLHttpRequest (原生)
底层API,全部当代异步请求的基础
- const xhr = new XMLHttpRequest();
- xhr.open('GET', 'https://api.example.com/data');
- // 状态监听
- xhr.onreadystatechange = function() {
- if (xhr.readyState === 4) { // 请求完成
- if (xhr.status === 200) {
- console.log(JSON.parse(xhr.responseText));
- } else {
- console.error('请求失败:', xhr.status);
- }
- }
- };
- // 错误处理
- xhr.onerror = function() {
- console.error('网络错误');
- };
- xhr.send();
复制代码 特点:
- ✅ 兼容性极好(IE6+)
- ❌ 回调地狱风险
- ❌ 需手动处置惩罚JSON剖析
二、Fetch API (当代标准)
基于Promise的标准化方案
- fetch('https://api.example.com/data', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ key: 'value' })
- })
- .then(response => {
- if (!response.ok) throw new Error('HTTP错误:' + response.status);
- return response.json();
- })
- .then(data => console.log(data))
- .catch(error => console.error('请求失败:', error));
复制代码 优化本领:
- 超时控制:
- const timeout = (ms) => new Promise((_, reject) =>
- setTimeout(() => reject(new Error('请求超时')), ms)
- );
- Promise.race([fetch(url), timeout(5000)])
- .then(...);
复制代码 特点:
- ✅ 原生Promise链式调用
- ❌ 默认不携带Cookie(需配置credentials: 'include')
- ❌ 部分旧浏览器需polyfill
三、Axios (主流第三方库)
功能最全面的请求库
- import axios from 'axios';
- // 发起请求
- axios.get('https://api.example.com/data', {
- params: { id: 123 },
- timeout: 5000
- })
- .then(response => console.log(response.data))
- .catch(error => {
- if (error.response) {
- // 服务器响应异常(4xx/5xx)
- console.log(error.response.status);
- } else if (error.request) {
- // 请求已发出但无响应
- console.log('无响应:', error.request);
- } else {
- // 其他错误
- console.log('错误:', error.message);
- }
- });
- // 全局配置
- axios.defaults.baseURL = 'https://api.example.com';
- axios.interceptors.request.use(config => {
- config.headers.Authorization = localStorage.getItem('token');
- return config;
- });
复制代码 核心上风:
- ✅ 自动JSON转换
- ✅ 请求/相应拦截器
- ✅ 并发控制:axios.all()
- ✅ 取消请求:CancelToken
四、async/await (终极异步方案)
用同步写法处置惩罚异步(需配合Fetch/Axios)
- async function fetchData() {
- try {
- const response = await fetch('https://api.example.com/data');
- const data = await response.json();
- console.log(data);
- } catch (error) {
- console.error('请求失败:', error);
- }
- }
- // 并行请求
- async function fetchMulti() {
- const [user, posts] = await Promise.all([
- fetch('/user'),
- fetch('/posts')
- ]);
- }
复制代码
五、WebSocket (双向及时通信)
非HTTP协议的长期化毗连
- const socket = new WebSocket('wss://api.example.com/ws');
- socket.onopen = () => {
- socket.send(JSON.stringify({ type: 'join' }));
- };
- socket.onmessage = (event) => {
- console.log('收到消息:', event.data);
- };
- socket.onclose = () => {
- console.log('连接关闭');
- };
复制代码 六、技术选型对比表
方案实用场景优点缺点XMLHttpRequest兼容IE的低要求项目无需外部依靠代码冗长、回调地狱Fetch API当代浏览器项目原生支持、Promise化错误处置惩罚不敷直观Axios企业级复杂应用功能全面、拦截器机制需额外引入库WebSocket及时聊天、股票行情双向及时通信需后端配合协议支持 七、最佳实践建议
- 首选Axios:复杂项目用拦截器统一处置惩罚鉴权/错误
- SSR兼容:服务端渲染使用isomorphic-fetch或axios
- 性能优化:
- 请求去重:同一API短时间内只发一次
- 缓存策略:Cache-Control + 本地缓存
- 安全防护:
掌握这些方法后,可根据项目需求机动选择最合适的异步通信方案!
js循环机制
JavaScript 的循环机制(事件循环)就像一家繁忙的餐厅,用三步就能理解它的工作原理:
一、餐厅厨房的比喻
脚色对应 JavaScript 概念作用厨师👨🍳主线程(JS 引擎)一次只能炒一个菜(单线程)服务员💁♀️事件循环(Event Loop)协调谁先上菜(使命调度)煮面锅🍜异步使命(setTimeout、网络请求等)需要等待的操纵出菜口📤使命队列(Task Queue)存放煮好的面(完成的使命) 二、工作流程(三步走)
1. 先做马上能出餐的菜(同步使命)
- 场景:顾客点了一份炒饭(同步使命)和一碗需要煮的面(异步使命)
- 操纵:
- console.log('开始炒饭'); // 同步任务,立即执行
- setTimeout(() => { // 异步任务,交给"煮面锅"
- console.log('面煮好了');
- }, 2000);
- console.log('继续做其他菜'); // 继续执行同步任务
复制代码
- 结果输出:
2. 煮好的面要排队(使命队列)
- 煮面过程:
- 厨师把锅交给后厨(浏览器其他线程处置惩罚)
- 继续做其他菜(实行后续代码)
- 面煮好后,服务员把面放到出菜口(使命队列)
3. 服务员按规则上菜(事件循环)
- 规则:
- VIP 订单先处置惩罚(微使命队列)
好比:Promise 的回调(.then())、MutationObserver
- Promise.resolve().then(() => {
- console.log('VIP 面条加急处理!');
- });
复制代码 - 平凡订单后处置惩罚(宏使命队列)
好比:setTimeout、setInterval、用户点击事件
- 完备流程:
- 1. 做完所有同步任务(炒饭)
- 2. 立即处理所有 VIP 订单(微任务)
- 3. 如果有新 VIP 订单,继续处理直到清空
- 4. 上一个宏任务结束,取一个普通订单(如煮面回调)
- 5. 重复这个循环...
复制代码 三、真实代码示例
- console.log('开始点餐'); // 同步任务
- // 宏任务(普通订单)
- setTimeout(() => {
- console.log('您的面煮好了');
- }, 0);
- // 微任务(VIP 订单)
- Promise.resolve().then(() => {
- console.log('VIP 小菜已赠送');
- });
- console.log('继续服务其他顾客'); // 同步任务
复制代码 输出顺序:
开始点餐 继续服务其他顾客 VIP 小菜已赠送 您的面煮好了
四、为什么这样设计?
- 不卡顿:厨师(主线程)不会傻等煮面(异步使命),可以继续干活
- 高效:VIP 订单(微使命)能插队处置惩罚,包管高优先级使命快速相应
- 有序:全部使命排队处置惩罚,不会出现混乱
下次看到 Promise 和 setTimeout 时,想想这个餐厅模型就明白了! 🍔 → 🍜 → 💁♀️ → 🔁
uniapp相关面试题
uniapp制作的app想上传文件pdf怎样实现?
一、完备实现流程
- 选择文件 → 2. 校验文件 → 3. 显示预览 → 4. 上传服务器 → 5. 处置惩罚结果
二、核心代码实现(全平台兼容版)
- <template>
- <view class="container">
- <!-- 上传按钮 -->
- <button @click="selectPDF" type="primary">选择PDF文件</button>
-
- <!-- 文件信息展示 -->
- <view v-if="fileInfo" class="file-info">
- <text>文件名:{{ fileInfo.name }}</text>
- <text>大小:{{ (fileInfo.size / 1024 / 1024).toFixed(2) }}MB</text>
- <button @click="startUpload" type="primary">开始上传</button>
- </view>
- <!-- 进度条 -->
- <progress v-if="progress > 0" :percent="progress" show-info />
- </view>
- </template>
- <script>
- export default {
- data() {
- return {
- fileInfo: null, // 文件信息
- progress: 0, // 上传进度
- uploadTask: null // 上传任务对象(用于取消上传)
- }
- },
- methods: {
- // 1. 选择PDF文件
- async selectPDF() {
- try {
- const res = await uni.chooseFile({
- count: 1,
- type: 'file',
- extension: ['pdf'],
- sourceType: ['album', 'camera'] // 相册或文件系统
- })
-
- // 获取文件信息
- this.fileInfo = {
- path: res.tempFiles[0].path,
- name: res.tempFiles[0].name,
- size: res.tempFiles[0].size
- }
-
- // 预览文件(仅App可用)
- if (uni.getSystemInfoSync().platform === 'android' || uni.getSystemInfoSync().platform === 'ios') {
- uni.openDocument({
- filePath: this.fileInfo.path,
- fileType: 'pdf'
- })
- }
- } catch (err) {
- uni.showToast({ title: '选择文件失败', icon: 'none' })
- }
- },
- // 2. 上传文件
- async startUpload() {
- if (!this.fileInfo) return
-
- // 校验文件大小(示例限制20MB)
- if (this.fileInfo.size > 20 * 1024 * 1024) {
- uni.showToast({ title: '文件不能超过20MB', icon: 'none' })
- return
- }
-
- this.progress = 0
-
- try {
- this.uploadTask = uni.uploadFile({
- url: 'https://your-api.com/upload',
- filePath: this.fileInfo.path,
- name: 'file',
- formData: {
- userId: '123',
- timestamp: Date.now()
- },
- success: (res) => {
- if (res.statusCode === 200) {
- const data = JSON.parse(res.data)
- uni.showToast({ title: '上传成功', icon: 'success' })
- console.log('服务器返回:', data)
- } else {
- uni.showToast({ title: `上传失败:${res.errMsg}`, icon: 'none' })
- }
- },
- fail: (err) => {
- console.error('上传失败:', err)
- uni.showToast({ title: '上传失败', icon: 'none' })
- },
- complete: () => {
- this.uploadTask = null
- }
- })
-
- // 监听进度
- this.uploadTask.onProgressUpdate((res) => {
- this.progress = res.progress
- })
- } catch (err) {
- uni.showToast({ title: '上传异常', icon: 'none' })
- }
- },
- // 3. 取消上传
- cancelUpload() {
- if (this.uploadTask) {
- this.uploadTask.abort()
- uni.showToast({ title: '已取消上传', icon: 'none' })
- }
- }
- }
- }
- </script>
- <style>
- .container {
- padding: 20px;
- }
- .file-info {
- margin-top: 20px;
- padding: 15px;
- background-color: #f5f5f5;
- border-radius: 5px;
- }
- </style>
复制代码- [/code] [size=3]三、关键配置分析[/size]
- [size=2]1. 平台适配要点[/size]
- [table][tr]平台配置项[/tr][tr][td][b]Android[/b][/td][td]需在manifest.json中添加权限:
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>[/td][/tr][tr][td][b]iOS[/b][/td][td]需在manifest.json中配置:
- "ios" : { "permission" : { "NSPhotoLibraryUsageDescription" : "需要访问相册选择文件" } }[/td][/tr][tr][td][b]全部平台[/b][/td][td]使用uni.chooseFile统一接口,无需处置惩罚平台差异[/td][/tr][/table] [size=2]2. 文件选择参数优化[/size]
- [code]// 更严格的文件类型校验
- const isPDF = file.type === 'application/pdf' ||
- file.name.endsWith('.pdf') ||
- /\.pdf$/i.test(file.name)
- // 获取文件真实路径(App专用)
- if (plus.io) {
- file.realPath = plus.io.convertLocalFileSystemURL(file.path)
- }
复制代码 四、服务器端示例(Node.js + Express)
- const express = require('express')
- const multer = require('multer')
- const path = require('path')
- const app = express()
- // 配置文件存储
- const storage = multer.diskStorage({
- destination: (req, file, cb) => {
- cb(null, path.join(__dirname, 'uploads'))
- },
- filename: (req, file, cb) => {
- cb(null, `${Date.now()}-${file.originalname}`)
- }
- })
- // 文件过滤
- const fileFilter = (req, file, cb) => {
- if (file.mimetype === 'application/pdf') {
- cb(null, true)
- } else {
- cb(new Error('只允许上传PDF文件'), false)
- }
- }
- const upload = multer({
- storage,
- fileFilter,
- limits: { fileSize: 20 * 1024 * 1024 } // 限制20MB
- })
- // 上传接口
- app.post('/upload', upload.single('file'), (req, res) => {
- console.log('接收到文件:', req.file)
- console.log('附加参数:', req.body)
-
- res.json({
- code: 200,
- message: '上传成功',
- data: {
- filename: req.file.filename,
- originalname: req.file.originalname,
- size: req.file.size,
- downloadUrl: `/download/${req.file.filename}`
- }
- })
- })
- // 启动服务
- app.listen(3000, () => {
- console.log('服务器运行在 http://localhost:3000')
- })
复制代码 五、增强功能实现
1. 断点续传(App端)
- // 获取文件分片
- const fileManager = plus.io.getFileSystemManager()
- fileManager.readFile({
- filePath: file.path,
- position: 0,
- length: chunkSize,
- success: (res) => {
- const chunk = res.data
- // 上传分片逻辑...
- }
- })
复制代码 2. 文件加密上传
- // 使用crypto-js加密
- import CryptoJS from 'crypto-js'
- const encryptFile = (file) => {
- const wordArray = CryptoJS.lib.WordArray.create(file)
- return CryptoJS.AES.encrypt(wordArray, 'secret-key').toString()
- }
复制代码 3. 七牛云/OSS直传
- // 获取服务端签名后直传
- uni.request({
- url: 'https://your-api.com/get-oss-token',
- success: (res) => {
- const ossConfig = res.data
- uni.uploadFile({
- url: ossConfig.host,
- filePath: this.fileInfo.path,
- name: 'file',
- formData: {
- key: ossConfig.key,
- policy: ossConfig.policy,
- OSSAccessKeyId: ossConfig.accessid,
- signature: ossConfig.signature
- }
- })
- }
- })
复制代码- [/code] [size=3]六、常见问题解决方案[/size]
- [size=2]1. [b]Android文件路径问题[/b][/size]
- [code]// 转换真实路径
- if (this.fileInfo.path.startsWith('file://')) {
- this.fileInfo.path = this.fileInfo.path.replace('file://', '')
- }
复制代码 2. iOS无法选择文件
检查manifest.json是否精确配置:
- "ios" : {
- "permission" : {
- "NSPhotoLibraryUsageDescription" : "需要访问相册选择文件",
- "NSPhotoLibraryAddUsageDescription" : "需要保存文件到相册"
- }
- }
复制代码 3. 大文件上传失败
- 分片上传(每5MB一个分片)
- 显示进度和暂停/继续功能
- 后台服务需支持分片合并
七、性能优化建议
- 前端压缩:使用pdf-lib等库压缩PDF
- 缓存机制:已上传文件记载MD5制止重复上传
- 队列控制:多个文件时限定并发上传数
uni-app实现扫码的几种方式
1、官方扫码接口
优点:uni.scanCode(OBJECT)就可以直接调用,方便快捷,支持安卓、ios,假如制作小程序,便可以直接转换成微信等官方扫码api。
缺点:安卓、ios端识别率非常差,并且扫码耗时很长,很多条件下,无法识别二维码。
2、付出宝扫码插件
直接在插件市场就可以下载使用
优点:免费,并且识别率非常高
缺点:需要配置阿里云控制台,而且阿里在控制台有初始埋点,第一次使用假如没有关闭,会直接短信关照欠费,不能离线扫码,数据安全性不详。
3、华为扫码插件
直接在插件市场就可以下载使用
优点:识别率不错,可以离线扫码
缺点:需要付费88元,我在测试阶段扫码扫出空字符,导致数据传值失败,需要在扫码获取的值做一些处置惩罚,下面附上处置惩罚 方式。
- this.invoicedata = ret.sValue.replace(/[\xa0\x00-\x09\x0b\x0c\x0e-\x1f\x7f]/g, '');
复制代码 4、微信扫码
优点:免费,识别率高
缺点:目前只支持小程序,没有开放接口。
这道答案来源于uni-app实现扫码的几种方式_uniapp scancode-CSDN博客
CSS相关面试题
PC网页转h5怎样相应式结构?
在将 PC 网页转换为 H5 相应式结构时,需遵循移动优先原则并分身多端适配。以下是完备实现方案:
一、核心结构策略
- <!-- 基础视口配置 -->
- <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
- <!-- 移动端专用 meta -->
- <meta name="format-detection" content="telephone=no">
- <meta name="apple-mobile-web-app-capable" content="yes">
复制代码 1. 弹性容器结构
- .container {
- width: 100%;
- max-width: 1200px; /* PC端最大宽度限制 */
- margin: 0 auto;
- padding: 0 15px; /* 移动端安全边距 */
- display: flex;
- flex-wrap: wrap; /* 允许换行适应小屏 */
- }
复制代码 2. 网格系统实现
- /* 12列响应式网格 */
- .col {
- flex: 0 0 100%; /* 移动端默认占满宽度 */
- padding: 10px;
- }
- @media (min-width: 768px) {
- .col-md-6 {
- flex: 0 0 50%; /* 平板分两列 */
- }
- }
- @media (min-width: 992px) {
- .col-lg-3 {
- flex: 0 0 25%; /* PC端分四列 */
- }
- }
复制代码 二、元素级适配方案
1. 字体相应式
- :root {
- font-size: 14px; /* 基准字号 */
- }
- @media (min-width: 768px) {
- :root {
- font-size: 16px; /* 中屏增大字号 */
- }
- }
- h1 {
- font-size: 2rem; /* 28px -> 32px */
- line-height: 1.3;
- }
复制代码 2. 图片适配
- <picture>
- <!-- 移动端优先加载小图 -->
- <source media="(max-width: 767px)" srcset="mobile.jpg">
- <!-- 平板 -->
- <source media="(max-width: 991px)" srcset="tablet.jpg">
- <!-- PC端 -->
- <img src="desktop.jpg" alt="示例"
- style="width:100%; height:auto;">
- </picture>
复制代码 3. 表格优化
- .table-wrapper {
- overflow-x: auto; /* 横向滚动 */
- -webkit-overflow-scrolling: touch;
- }
- table {
- min-width: 600px; /* 保持表格最小可读宽度 */
- }
复制代码 三、交互适配本领
1. 导航栏改造
- <!-- 移动端汉堡菜单 -->
- <button class="menu-toggle">☰</button>
- <nav class="main-nav">
- <a href="#">首页</a>
- <a href="#">产品</a>
- </nav>
- <style>
- .menu-toggle {
- display: none; /* PC端隐藏 */
- }
- @media (max-width: 767px) {
- .menu-toggle {
- display: block; /* 移动端显示 */
- }
- .main-nav {
- display: none;
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- background: white;
- }
- .main-nav.active {
- display: block;
- }
- }
- </style>
复制代码 2. 表单优化
- input[type="text"],
- input[type="email"] {
- width: 100%;
- height: 40px;
- padding: 8px;
- font-size: 16px; /* 避免移动端缩放 */
- border: 1px solid #ddd;
- }
复制代码 四、性能优化专项
1. 资源按需加载
- <!-- 移动端不加载大图 -->
- <div class="hero-image"
- data-src="desktop-bg.jpg"
- data-mobile-src="mobile-bg.jpg"></div>
- <script>
- function loadAdaptiveImage() {
- const images = document.querySelectorAll('[data-mobile-src]');
- images.forEach(img => {
- const src = window.innerWidth < 768 ?
- img.dataset.mobileSrc :
- img.dataset.src;
- img.style.backgroundImage = `url(${src})`;
- });
- }
- window.addEventListener('resize', loadAdaptiveImage);
- </script>
复制代码 2. 触控反馈优化
- .button {
- min-width: 44px; /* 最小点击区域 */
- min-height: 44px;
- padding: 12px 24px;
- transition: opacity 0.3s;
- }
- .button:active {
- opacity: 0.7; /* 按压反馈 */
- }
复制代码 五、多端调试方案
1. Chrome 设备模拟
- 打开 DevTools → Device Toolbar
- 测试差别DPR(Device Pixel Ratio)
2. 真机调试
- # 本地服务绑定IP
- npm run dev -- --host 0.0.0.0
- # 手机访问电脑IP:端口
复制代码 3. 自动化测试工具
- // 使用Puppeteer多分辨率截图
- const puppeteer = require('puppeteer');
- async function testResponsive() {
- const browser = await puppeteer.launch();
- const page = await browser.newPage();
-
- const devices = [
- {name: 'mobile', width: 375, height: 667},
- {name: 'tablet', width: 768, height: 1024},
- {name: 'desktop', width: 1200, height: 800}
- ];
- for (let device of devices) {
- await page.setViewport(device);
- await page.goto('http://localhost:3000');
- await page.screenshot({path: `screenshot-${device.name}.png`});
- }
- await browser.close();
- }
- testResponsive();
复制代码 六、常见问题解决
- .border {
- position: relative;
- }
- .border::after {
- content: '';
- position: absolute;
- left: 0;
- bottom: 0;
- width: 100%;
- height: 1px;
- background: #ddd;
- transform: scaleY(0.5);
- }
复制代码
- .header {
- position: fixed;
- top: 0;
- width: 100%;
- /* 开启GPU加速 */
- transform: translateZ(0);
- backface-visibility: hidden;
- }
复制代码
通过以上方案,可实现从PC到H5的高质量相应式适配,分身视觉结果与交互体验。核心原则是:弹性结构打底 + 媒体查询微调 + 移动优先交互。
css想做动画有哪些方法?
在 CSS 中实现动画重要有以下几种核心方法,每种方法都有实在用场景和特性:
一、基础动画方法
1. Transition(过渡动画)
- 用途:元素属性变化时的平滑过渡(如 hover 结果)
- 代码示例:
- .box {
- width: 100px;
- transition: width 0.3s ease-in-out;
- }
- .box:hover {
- width: 200px;
- }
复制代码 - 特点:
- 只能定义开始和竣事状态
- 支持属性:transform、opacity、color 等可过渡属性
- 性能优化:优先使用 transform 和 opacity(触发 GPU 加快)
2. Keyframes Animation(关键帧动画)
- 用途:复杂多阶段动画(如旋转、闪耀)
- 代码示例:
- @keyframes rotate {
- 0% { transform: rotate(0deg); }
- 100% { transform: rotate(360deg); }
- }
- .spinner {
- animation: rotate 2s linear infinite;
- }
复制代码 - 核心属性:
- animation: name duration timing-function delay iteration-count direction fill-mode;
复制代码 - 高级本领:
- 使用 steps() 函数实现逐帧动画:
- @keyframes walk {
- from { background-position: 0 0; }
- to { background-position: -1600px 0; }
- }
- .character {
- animation: walk 1s steps(8) infinite;
- }
复制代码
二、性能优化本领
1. 硬件加快
- .animate {
- transform: translateZ(0); /* 或 will-change: transform; */
- }
复制代码 2. 限定重绘地区
- .container {
- contain: strict; /* 限制浏览器重绘范围 */
- }
复制代码 3. 制止结构抖动
- 优先使用 transform 替代 top/left 位移
- 使用 requestAnimationFrame 同步动画(JS 辅助)
三、进阶动画方案
1. CSS 变量驱动动画
- :root {
- --rotate: 0deg;
- }
- .box {
- transform: rotate(var(--rotate));
- transition: transform 0.3s;
- }
- /* JS 控制动画 */
- box.style.setProperty('--rotate', '45deg');
复制代码 2. SVG 动画
- <svg viewBox="0 0 100 100">
- <circle cx="50" cy="50" r="40">
- <animate
- attributeName="r"
- from="40" to="20"
- dur="1s"
- repeatCount="indefinite"/>
- </circle>
- </svg>
复制代码 3. 动画库扩展
- Animate.css(预设动画库):
- <div class="animate__animated animate__bounce"></div>
复制代码 - Motion One(当代轻量库):
- import { animate } from "motion";
- animate(".box", { x: 100 }, { duration: 0.3 });
复制代码 四、方案对比表
方法实用场景优点缺点Transition简单状态过渡简单高效、性能好无法控制中间状态Keyframes复杂多阶段动画完全控制动画流程代码量较大CSS 变量动态控制动画参数机动、易于JS交互兼容性要求 IE11+SVG 动画矢量图形动画分辨率无关、路径动画学习成本较高动画库快速实现复杂结果开箱即用、结果丰富增加项目体积 五、调试工具
- Chrome DevTools 动画面板:
- 性能监测:
- 使用 Performance 面板分析帧率
- 检查 Layout/Paint 耗时
六、最佳实践建议
- 优先使用 CSS 原生动画(而非 JS 动画)
- 限定同时运行的动画数目(一般不超过 5 个)
- 移动端优化:
- 动画时长建议 200-500ms
- 使用 @media (prefers-reduced-motion) 适配无障碍
- @media (prefers-reduced-motion: reduce) {
- * { animation: none !important; }
- }
复制代码
掌握这些方法后,可根据具体需求选择最合适的动画实现方案!🎉
其他综合问题
怎样不让网页上的水印被去除?
在PC端页面实现防篡改水印需要综合多种技术手段,以下是一套较为全面的解决方案:
一、动态Canvas水印方案
- function createDynamicWatermark(text) {
- const canvas = document.createElement('canvas');
- const ctx = canvas.getContext('2d');
- canvas.width = 300;
- canvas.height = 200;
-
- // 绘制基础水印
- ctx.font = '14px Arial';
- ctx.fillStyle = 'rgba(200,200,200,0.2)';
- ctx.rotate(-20 * Math.PI / 180);
- ctx.fillText(text, 10, 100);
- // 添加防伪波纹
- for(let i=0;i<canvas.width;i+=30){
- ctx.beginPath();
- ctx.moveTo(i, 0);
- ctx.lineTo(i, canvas.height);
- ctx.strokeStyle = 'rgba(0,0,0,0.02)';
- ctx.stroke();
- }
- // 生成动态背景图
- const dataUrl = canvas.toDataURL();
- const watermarkDiv = document.createElement('div');
- watermarkDiv.style.cssText = `
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- pointer-events: none;
- z-index: 999999;
- background: url(${dataUrl}) left top repeat;
- mix-blend-mode: multiply;
- `;
- // 防删除保护
- watermarkDiv.setAttribute('id', 'secure-watermark');
- Object.defineProperty(watermarkDiv, 'id', {
- configurable: false,
- writable: false
- });
- document.body.appendChild(watermarkDiv);
- }
复制代码 二、防篡改保护机制
DOM变更监控
- const observer = new MutationObserver(mutations => {
- mutations.forEach(mutation => {
- Array.from(mutation.removedNodes).forEach(node => {
- if (node.id === 'secure-watermark') {
- document.body.appendChild(node);
- }
- });
- });
- });
- observer.observe(document.body, {
- childList: true,
- subtree: true
- });
复制代码 样式篡改防御
- const styleGuard = setInterval(() => {
- const watermark = document.getElementById('secure-watermark');
- if (!watermark) {
- createDynamicWatermark();
- return;
- }
-
- const styles = window.getComputedStyle(watermark);
- const protectedProps = {
- 'display': 'block',
- 'opacity': '1',
- 'visibility': 'visible'
- };
- Object.entries(protectedProps).forEach(([prop, value]) => {
- if (styles[prop] !== value) {
- watermark.style[prop] = value;
- }
- });
- }, 1000);
复制代码 三、用户举动监控
- window.addEventListener('keydown', (e) => {
- // 屏蔽开发者工具快捷键
- const devtoolKeys = [
- e.ctrlKey && e.keyCode === 85, // Ctrl+U
- e.ctrlKey && e.shiftKey && e.keyCode === 73, // Ctrl+Shift+I
- e.keyCode === 123 // F12
- ];
-
- if (devtoolKeys.some(Boolean)) {
- e.preventDefault();
- alert('安全警告:禁止开发者工具操作!');
- window.location.reload();
- }
- });
- // 防止右键检查
- document.addEventListener('contextmenu', (e) => {
- e.preventDefault();
- return false;
- });
- // 防截图干扰
- const noiseCanvas = document.createElement('canvas');
- noiseCanvas.style.cssText = `
- position: fixed;
- top:0;
- left:0;
- pointer-events: none;
- z-index: 9999999;
- `;
- document.body.appendChild(noiseCanvas);
- function generateNoise() {
- const ctx = noiseCanvas.getContext('2d');
- const imageData = ctx.createImageData(noiseCanvas.width, noiseCanvas.height);
- const data = imageData.data;
-
- for(let i=0;i<data.length;i+=4){
- const val = Math.random()*255;
- data[i] = data[i+1] = data[i+2] = val;
- data[i+3] = 5;
- }
-
- ctx.putImageData(imageData, 0, 0);
- requestAnimationFrame(generateNoise);
- }
- noiseCanvas.width = window.innerWidth;
- noiseCanvas.height = window.innerHeight;
- generateNoise();
复制代码 四、服务端配合方案
- // 每次请求携带动态token
- async function getWatermarkText() {
- const res = await fetch('/api/watermark-token');
- const { token } = await res.json();
- return `机密 ${token} ${Date.now()}`;
- }
- // 定期更新水印
- setInterval(async () => {
- const newText = await getWatermarkText();
- updateWatermark(newText);
- }, 300000);
复制代码
- // 使用CryptoJS进行内容签名
- const contentHash = CryptoJS.HmacSHA256(
- document.body.innerText,
- 'secretKey'
- ).toString();
- // 后台校验示例
- app.post('/verify-content', (req, res) => {
- const clientHash = req.body.hash;
- const serverHash = CryptoJS.HmacSHA256(
- req.body.content,
- 'secretKey'
- ).toString();
-
- if(clientHash !== serverHash) {
- // 触发内容篡改警报
- sendAlert('检测到内容篡改!');
- }
- });
复制代码 五、防御等级分析
防御层级技术手段防御能力初级防御基础CSS水印★☆☆☆☆中级防御Canvas动态水印+DOM监控 ★★★☆☆高级防御举动监控+服务端验证★★★★☆终极防御硬件级DRM方案★★★★★ 注意事项:
- 定期更新水印算法(建议每月迭代)
- 结合业务日志
记载用户操纵举动
- 紧张数据建议使用PDF等不可逆格式展示
- 法律层面增加水印声明条款
任何前端方案都无法做到绝对防篡改,建议根据业务安全等级选择适当方案,关键数据应结合后端验证和权限控制。
HTTP和HTTPS的区别
HTTP(超文本传输协议)和 HTTPS(安全超文本传输协议)的核心区别在于数据传输的安全性,以下是两者的具体对比:
一、核心区别总结
特性HTTPHTTPS安全性明文传输,数据可被窃听/篡改加密传输,防窃听/防篡改协议应用层协议HTTP + SSL/TLS 安全层默认端口80443证书无需证书需CA机构颁发的SSL证书性能无加密开销,速度稍快有加密计算开销,但当代硬件影响可忽略SEO搜索引擎大概降权搜索引擎优先收录(如Google明确支持) 二、技术实现差异
1. 加密机制(核心)
- HTTP:
数据以明文情势传输,可直接被中间人读取:
- GET /login HTTP/1.1
- Host: example.com
- username=admin&password=123456 ← 明文传输!
复制代码 - HTTPS:
通过SSL/TLS协议加密数据,传输内容为密文:
- ���]�k�M�I�4��Q�V��x�d� ← 加密后的乱码数据
复制代码 2. SSL/TLS工作流程
- 握手阶段(非对称加密):
- 客户端验证服务器证书有效性
- 协商加密算法(如RSA、ECDHE)
- 生成会话密钥(Session Key)
- 数据传输(对称加密):
三、HTTPS核心上风
1. 防窃听(Encryption)
- 全部数据加密传输,即使被截获也无法解密
- 示例:公共WiFi下登录账号时,HTTPS可防止暗码泄露
2. 防篡改(Integrity)
- 使用MAC(消息认证码)验证数据完备性
- 示例:制止运营商插入广告代码
3. 身份认证(Authentication)
- SSL证书验证网站真实身份,防止钓鱼网站
- 示例:浏览器地址栏显示锁标志和公司名称
四、摆设HTTPS的要求
1. SSL证书范例
证书范例验证等级实用场景DV(域名验证)验证域名全部权个人博客、小型网站OV(组织验证)验证企业/组织身份企业官网EV(扩展验证)严格身份验证(地址栏绿标)银行、电商等敏感领域 2. 摆设步调
- 购买或申请免费证书(如Let's Encrypt)
- 在服务器安装证书(Nginx示例):
- server {
- listen 443 ssl;
- ssl_certificate /path/to/cert.pem;
- ssl_certificate_key /path/to/private.key;
- }
复制代码 - 逼迫HTTP跳转HTTPS:
- server {
- listen 80;
- return 301 https://$host$request_uri;
- }
复制代码 五、性能优化本领
- 会话复用(Session Resumption)
镌汰SSL握手次数,降低耽误:
- ssl_session_cache shared:SSL:10m;
- ssl_session_timeout 10m;
复制代码
- 启用HTTP/2
HTTPS下可启用HTTP/2协议提拔性能:
六、常见误解澄清
- 误区1:"HTTPS会明显拖慢网站速度"
→ 当代服务器性能下,TLS握手开销仅增加约1 RTT(经优化可降至0 RTT)
- 误区2:"只有付出页面需要HTTPS"
→ 当代Web标准(如PWA、Geolocation API)要责备站HTTPS
- 误区3:"HTTPS无法被挟制"
→ 若客户端安装伪证书(如企业监控),仍大概被中间人攻击
七、怎样验证HTTPS安全性
- 浏览器地址栏显示锁形图标
- 通过SSL Labs测试检查配置:
总结:HTTPS通过加密和身份验证构建安全传输通道,是当代Web应用的必备基础,全部新建网站都应默认启用HTTPS。
前端浏览器安全怎样注意?
为了保障前端浏览器的安全性,需要从代码防护、数据传输、用户输入处置惩罚等多方面入手。以下是系统化的安全防护方案及具体实行方法:
一、基础防护体系
1. 启用HTTPS逼迫加密
- # Nginx配置(强制跳转HTTPS)
- server {
- listen 80;
- server_name example.com;
- return 301 https://$host$request_uri;
- }
复制代码 附加步伐:
- add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
复制代码 2. 安全头配置
- # 安全响应头示例
- add_header X-Frame-Options "SAMEORIGIN"; # 防点击劫持
- add_header X-Content-Type-Options "nosniff"; # 禁止MIME嗅探
- add_header Referrer-Policy "strict-origin-when-cross-origin"; # 控制Referer泄露
- add_header Permissions-Policy "geolocation=()"; # 禁用不必要权限
复制代码 二、攻击防御专项
1. XSS(跨站脚本攻击)防御
- 输入过滤:
- // 使用DOMPurify过滤HTML
- import DOMPurify from 'dompurify';
- const cleanHTML = DOMPurify.sanitize(userInput);
复制代码 - 输出转义:
- <!-- Vue自动转义 -->
- <div>{{ userContent }}</div>
- <!-- 手动转义 -->
- <div v-html="safeHTML"></div>
复制代码 - CSP策略:
- <meta http-equiv="Content-Security-Policy"
- content="default-src 'self'; script-src 'self' 'unsafe-inline'">
复制代码 2. CSRF(跨站请求伪造)防御
- Token验证:
- // 后端生成Token
- const csrfToken = crypto.randomBytes(32).toString('hex');
- res.cookie('XSRF-TOKEN', csrfToken);
- // 前端发送请求携带Token
- axios.defaults.headers.common['X-XSRF-TOKEN'] = getCookie('XSRF-TOKEN');
复制代码 - SameSite Cookie:
- // 设置Cookie属性
- res.cookie('sessionID', '123', {
- sameSite: 'Strict',
- secure: true
- });
复制代码 3. 点击挟制防御
- add_header X-Frame-Options "DENY"; # 完全禁止嵌入
- # 或
- add_header Content-Security-Policy "frame-ancestors 'self'";
复制代码 三、数据安全防护
1. 敏感数据处置惩罚
- 前端加密:
- // 使用Web Crypto API加密
- const encryptedData = await crypto.subtle.encrypt(
- { name: 'AES-GCM', iv },
- key,
- new TextEncoder().encode(data)
- );
复制代码 - 安全存储:
- // 避免localStorage存储敏感信息
- sessionStorage.setItem('tempData', encryptedData);
复制代码 2. 第三方资源校验
- <!-- 使用SRI校验CDN资源 -->
- <script src="https://cdn.example.com/jquery.js"
- integrity="sha384-...">
- </script>
复制代码 四、用户输入与认证
1. 输入验证
- // 邮箱格式校验
- const isValidEmail = (email) =>
- /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
- // 防止SQL注入
- const sanitizeInput = (input) =>
- input.replace(/['"\\;]/g, '');
复制代码 2. 认证安全
- 暗码策略:
- // 前端密码强度校验
- const isStrongPassword = (pwd) =>
- pwd.length >= 8 &&
- /[A-Z]/.test(pwd) &&
- /[0-9]/.test(pwd);
复制代码 - 双因素认证:
- // 集成Google Authenticator
- import { authenticator } from 'otplib';
- const secret = authenticator.generateSecret();
- const token = authenticator.generate(secret);
复制代码 五、连续监控与更新
1. 依靠安全扫描
- # 使用npm audit检查漏洞
- npm audit
- # 使用Snyk深度检测
- npx snyk test
复制代码 2. 及时监控
六、安全等级对照表
安全等级实用场景防护步伐基础级小型展示网站HTTPS + CSP + XSS过滤标准级企业级应用基础级 + CSRF Token + 输入验证 + 安全头高级级金融/政务系统标准级 + 双因素认证 + 及时监控 + 前端加密 + 人工渗出测试 七、应急相应方案
- 毛病相应流程:
- 数据泄露处置惩罚:
- 立即重置相关凭证
- 启用日志审计追溯攻击路径
- 根据法律要求关照受影响用户
通过以上综合步伐,可构建多层次的前端安全防护体系。关键点在于:默认安全原则 + 纵深防御 + 连续监控,同时需结合具体业务场景调整防护策略。
PC端开发大文件怎样上传?卡住了怎么办?失败了后怎样继续上传?
在前端PC端实现大文件上传并处置惩罚制止和续传的问题,可以采用分块上传与断点续传的策略。以下是具体的解决方案:
1. 分块上传(Chunked Upload)
将大文件分割为多个小块(如1MB/块),分别上传,降低单次请求压力,便于失败后重试。
实现步调:
- 前端分块处置惩罚:
- const CHUNK_SIZE = 1 * 1024 * 1024; // 1MB
- const file = document.getElementById('file-input').files[0];
- const chunks = [];
- let start = 0;
- while (start < file.size) {
- const chunk = file.slice(start, start + CHUNK_SIZE);
- chunks.push(chunk);
- start += CHUNK_SIZE;
- }
复制代码 - 生成文件唯一标识:
使用文件内容的哈希(如SHA-256)作为唯一标识,制止同名文件冲突。
- async function computeFileHash(file) {
- const buffer = await file.arrayBuffer();
- const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);
- return Array.from(new Uint8Array(hashBuffer)).map(b => b.toString(16).padStart(2, '0')).join('');
- }
复制代码 - 上传单个块:
- async function uploadChunk(chunk, chunkIndex, fileHash) {
- const formData = new FormData();
- formData.append('chunk', chunk);
- formData.append('chunkIndex', chunkIndex);
- formData.append('fileHash', fileHash);
- formData.append('totalChunks', totalChunks);
- await fetch('/upload-chunk', {
- method: 'POST',
- body: formData,
- });
- }
复制代码 2. 断点续传(Resumable Upload)
记载已上传的块,失败后仅上传缺失部分。
实现步调:
- 查询服务器已上传的块:
- async function getUploadedChunks(fileHash) {
- const response = await fetch(`/uploaded-chunks?fileHash=${fileHash}`);
- return await response.json(); // 返回已上传的块索引数组,如[0,1,2]
- }
复制代码 - 过滤未上传的块并继续上传:
- const uploadedChunks = await getUploadedChunks(fileHash);
- const chunksToUpload = chunks.filter((_, index) => !uploadedChunks.includes(index));
- // 控制并发上传(如最多5个并行)
- const MAX_CONCURRENT = 5;
- const queue = chunksToUpload.map((chunk, index) => () => uploadChunk(chunk, index, fileHash));
- // 使用p-limit库控制并发
- import pLimit from 'p-limit';
- const limit = pLimit(MAX_CONCURRENT);
- await Promise.all(queue.map(task => limit(task)));
复制代码 3. 失败处置惩罚与重试
- 网络错误重试: 为每个块的上传添加重试机制。
- async function uploadWithRetry(chunk, chunkIndex, fileHash, retries = 3) {
- try {
- await uploadChunk(chunk, chunkIndex, fileHash);
- } catch (error) {
- if (retries > 0) {
- await uploadWithRetry(chunk, chunkIndex, fileHash, retries - 1);
- } else {
- throw error;
- }
- }
- }
复制代码
- 服务端错误处置惩罚: 返回明确的错误状态码,前端根据状态码决定重试或报错。
4. 服务器端实现
关键接口:
- 上传块接口(/upload-chunk):
- 吸收块文件、块索引、文件哈希。
- 将块保存到临时目次(按文件哈希组织)。
- 查询已上传块接口(/uploaded-chunks):
- 合并文件接口(/merge-file):
示例伪代码(Node.js):
- // 保存块
- app.post('/upload-chunk', (req, res) => {
- const { chunk, chunkIndex, fileHash } = req.body;
- const chunkDir = `./temp/${fileHash}`;
- fs.mkdirSync(chunkDir, { recursive: true });
- fs.writeFileSync(`${chunkDir}/${chunkIndex}`, chunk.buffer);
- res.status(200).send('Chunk uploaded');
- });
- // 合并文件
- app.post('/merge-file', (req, res) => {
- const { fileHash, fileName } = req.body;
- const chunkDir = `./temp/${fileHash}`;
- const chunks = fs.readdirSync(chunkDir).sort((a, b) => a - b);
- const filePath = `./uploads/${fileName}`;
- chunks.forEach(chunk => {
- fs.appendFileSync(filePath, fs.readFileSync(`${chunkDir}/${chunk}`));
- });
- fs.rmdirSync(chunkDir, { recursive: true }); // 清理临时文件
- res.status(200).send('File merged');
- });
复制代码 5. 用户体验优化
- 进度显示: 监听每个块的上传进度,计算总进度。
- let uploadedSize = 0;
- chunks.forEach((chunk, index) => {
- uploadChunk(chunk, index).then(() => {
- uploadedSize += chunk.size;
- const progress = (uploadedSize / file.size) * 100;
- updateProgressBar(progress);
- });
- });
复制代码 - 暂停/恢复: 使用AbortController取消请求,保存已上传状态。
- const controllers = new Map();
- function uploadChunk(chunk, index) {
- const controller = new AbortController();
- controllers.set(index, controller);
- return fetch(url, {
- signal: controller.signal,
- // ...
- });
- }
- // 暂停上传
- function pauseUpload() {
- controllers.forEach(controller => controller.abort());
- }
复制代码 6. 注意事项
- 文件一致性校验: 合并后对比服务器端文件的哈希与前端计算的哈希。
- 清理临时文件: 设置定时使命清理未完成的逾期上传。
- 分块大小调整: 根据网络情况动态调整块大小(如1MB-5MB)。
通过上述方案,可有效解决大文件上传中的制止、续传和失败重试问题,提拔用户体验。
怎样记载接口报错并且展示?
在前端中记载接口报错并展示错误信息,是提拔用户体验和排查问题的紧张手段。以下是分步调的实现方案:
一、错误捕获与记载
1. 使用请求拦截器(以 Axios 为例)
- // 创建 axios 实例
- const instance = axios.create();
- // 响应拦截器
- instance.interceptors.response.use(
- (response) => response,
- (error) => {
- const { config, response } = error;
- const errorInfo = {
- time: new Date(),
- url: config?.url,
- method: config?.method,
- status: response?.status,
- message: response?.data?.message || error.message,
- };
-
- // 记录错误(可扩展为上报服务器)
- logError(errorInfo);
-
- // 展示错误(可选,部分错误可能不需要立即展示)
- showErrorUI(errorInfo);
-
- return Promise.reject(error);
- }
- );
复制代码 2. 错误日志记载方式
- 本地存储(临时记载)
- function logError(error) {
- const MAX_LOG_SIZE = 50;
- const logs = JSON.parse(localStorage.getItem('api_errors') || []);
- logs.push(error);
- if (logs.length > MAX_LOG_SIZE) logs.shift();
- localStorage.setItem('api_errors', JSON.stringify(logs));
- }
复制代码 - 上报到服务器(保举生产环境使用)
- function reportErrorToServer(error) {
- navigator.sendBeacon('/api/logs', JSON.stringify(error));
- }
复制代码 二、错误展示策略
1. 通用错误提示组件(React 示例)
- import { Toast } from 'antd-mobile';
- function showErrorUI(error) {
- let message = '请求失败,请稍后重试';
-
- // 根据错误类型定制提示
- if (error.status === 401) {
- message = '登录已过期,请重新登录';
- redirectToLogin();
- } else if (error.status >= 500) {
- message = '服务器开小差了,请联系管理员';
- }
-
- Toast.show({
- content: message,
- duration: 3000,
- icon: 'fail',
- });
- }
复制代码 2. 专用错误信息面板
- function ErrorPanel() {
- const [errors, setErrors] = useState([]);
- useEffect(() => {
- const errors = JSON.parse(localStorage.getItem('api_errors')) || [];
- setErrors(errors);
- }, []);
- return (
- <div className="error-panel">
- <h3>最近接口错误 ({errors.length})</h3>
- {errors.map((err, i) => (
- <div key={i} className="error-item">
- <p>[{err.time}] {err.method} {err.url}</p>
- <p>状态码: {err.status} | 信息: {err.message}</p>
- </div>
- ))}
- </div>
- );
- }
复制代码 三、高级优化方案
- 错误分类处置惩罚
- function handleErrorByType(error) {
- if (!navigator.onLine) {
- showNetworkError();
- } else if (error.code === 'ECONNABORTED') {
- showTimeoutError();
- } else {
- showGenericError();
- }
- }
复制代码 - 错误频率限定
- let lastToastTime = 0;
- function showError(message) {
- if (Date.now() - lastToastTime > 5000) {
- Toast.show(message);
- lastToastTime = Date.now();
- }
- }
复制代码 - 敏感信息过滤
- function sanitizeError(error) {
- const sanitized = { ...error };
- delete sanitized.headers.Authorization;
- delete sanitized.request.body.password;
- return sanitized;
- }
复制代码 四、技术栈扩展建议
- 错误监控平台集成
- 使用 Sentry/Bugsnag 举行专业错误监控
- import * as Sentry from '@sentry/browser';
- Sentry.init({ dsn: 'your_dsn' });
- function logError(error) {
- Sentry.captureException(new Error(error.message), {
- extra: error.context
- });
- }
复制代码
- 性能监控结合
- const startTime = Date.now();
- axios.get('/api/data')
- .then(() => {
- logApiPerformance(Date.now() - startTime);
- });
复制代码 五、注意事项
- 生产环境应制止显示原始错误堆栈
- 401 错误需要特殊处置惩罚(清除登录态)
- 紧张接口可参加自动重试机制
- function retryRequest(config, retries = 3) {
- return axios(config).catch(err => {
- return retries > 0
- ? retryRequest(config, retries - 1)
- : Promise.reject(err);
- });
- }
复制代码 通过以上方案,可以实现完备的接口错误处置惩罚流程,平衡用户体验与开发调试需求。建议根据具体业务场景选择适合的优化点举行扩展。
前端输入url后浏览器会先做什么?
以下是前端输入 URL 后浏览器的重要处置惩罚流程,按顺序分阶段分析:
一、URL 剖析阶段(URL Parsing)
- 协议补全
自动补全协议头(如输入 google.com → 补全为 https://google.com)
- 格式校验
检查 URL 合法性(如特殊字符转换 空格→%20)
二、缓存检查阶段(Cache Check)
缓存范例检查顺序典范场景Service Worker最先检查PWA 离线缓存Memory Cache内存缓存(短期)前进退却页面Disk Cache磁盘缓存(长期)CSS/JS 文件缓存Push CacheHTTP/2 推送缓存(会话级)服务器推送资源 *触发逼迫刷新(Ctrl+F5)时会跳过全部缓存*
三、DNS 剖析阶段(DNS Lookup)
- 优化手段
- <!-- 预解析特定域名 -->
- <link rel="dns-prefetch" href="//cdn.example.com">
复制代码 四、建立毗连阶段(TCP Handshake)
- 三次握手建立 TCP 毗连
- Client->Server: SYN
- Server->Client: SYN-ACK
- Client->Server: ACK
复制代码 - TLS 握手(HTTPS)
- 协商加密协议版本
- 验证证书有效性
- 交换对称加密密钥
五、发送请求阶段(HTTP Request)
- GET /index.html HTTP/1.1
- Host: www.example.com
- Accept: text/html
- Cookie: session_id=abc123
复制代码
- 关键过程
- 携带 Cookie 等身份信息
- 检查 Connection: keep-alive 复用毗连
- 处置惩罚 Content-Encoding 压缩格式
六、相应处置惩罚阶段(Response Processing)
- 状态码处置惩罚
- // 常见状态码处理逻辑
- switch(statusCode) {
- case 301:
- handleRedirect(response.headers.Location);
- break;
- case 404:
- show404Page();
- break;
- case 500:
- logServerError();
- break;
- }
复制代码 - 解压相应体
七、剖析渲染阶段(Rendering)
- 关键阻塞点
- <script> 标签会阻塞 DOM 构建(除非添加 async/defer)
- CSS 文件会阻塞渲染树合成(CSSOM 构建)
八、连续通信阶段(Keep-Alive)
- TCP 毗连复用
- // HTTP/1.1 默认开启 keep-alive
- // 同一域名最多维持 6 个 TCP 连接(浏览器差异)
复制代码 - HTTP/2 多路复用
关键性能优化点
- 镌汰 DNS 查询(使用 dns-prefetch)
- 复用 TCP 毗连(公道配置 keep-alive)
- 压缩资源体积(Gzip/Brotli)
- 制止渲染阻塞(CSS 内联/JS 异步加载)
- 利用 CDN 加快(镌汰网络耽误)
理解整个流程可以资助前端开发者针对性优化关键路径,提拔页面加载性能。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
|