transition

CSS transition 提供了一种在更改 CSS 属性时控制动画速度的方法。其可以让属性变化成为一个持续一段时间的,而不是立即生效的过程。 比如,将一个元素的颜色从白色改为黑色,通常这个改变是立即生效的,使用 CSS 过渡后该元素的颜色将按照一定的曲线速率从白色变化为黑色。这个过程可以自定义。 通常将两个状态之间的过渡称为隐式过渡,因为开始与结束之间的状态由浏览器决定。 CSS 过渡可以决定哪些属性发生动画效果(通过明确地列出这些属性),何时开始(通过设置延时),持续多久(通过设置时长)以及如何动画(通过定义缓动函数,如线性或先快后慢)。比如在不同的伪元素之间切换,像是 :hover,:active 或者通过 JavaScript 实现的状态变化。 开发者可以定义哪一属性需以何种方式用于动画,由此允许创造复杂的过渡。然而因为为某些属性赋予动画无意义,所以这些属性无动画性。 备注: auto 值常常较复杂,规范指出不要在它上动画。一些用户代理,比如基于 Gecko 的,实现了这个需求;然而另外一些用户代理,比如基于 WebKit 的,没有这么严格限制。在 auto 上使用动画,取决于浏览器及其版本,可能会导致非预期结果,应当避免使用。 属性 以下 transition 属性,若有多个属性值,则以,(逗号)衔接。 transition-property 指定哪个或哪些 CSS 属性名称用于过渡。只有指定的属性才会在过渡中发生动画,其他属性仍如通常那样瞬间变化。如果指定简写属性(比如 background),那么其完整版中所有可以动画的属性都会被应用过渡。 取值如下: none:没有过渡动画。 all(初始值):所有可被动画的属性都表现出过渡动画。 <custom-ident>:属性名称。由小写字母 a 到 z,数字 0 到 9,下划线(_)和破折号(-)。第一个不能为非破折号字符或数字。同时,不能以两个破折号开头。通俗来说就是可以是自定义名称也可以是css规范中的属性名。 transition-duration 表示过渡属性从旧的值转变到新的值所需要的时间。你可以为所有属性指定一个值,或者指定多个值,或者为每个属性指定不同的时长。 该属性值以秒(s)或毫秒(ms)为单位指定过渡动画所需的时间。默认值为 0s,表示不会呈现过渡动画,属性会瞬间完成转变。不接受负值。 可以指定多个时长,每个时长会被应用到由 transition-property 指定的对应属性上。 如果某个属性的值列表小于 transition-property 的属性值数量,那么时长列表会重复,重复规则是按照现有值进行重复。例如: 如3s,则重复多个3s div { transition-property: opacity, left, top, height; transition-duration: 3s; } 将视为: div { transition-property: opacity, left, top, height; transition-duration: 3s, 3s, 3s, 3s; } 如果是3s,5s,则会重复多个3s,5s。 div { transition-property: opacity, left, top, height; transition-duration: 3s, 5s; } 将视为: ...

October 8, 2024

异步组件

当我们的项目达到一定的规模时,对于某些组件来说,我们并不希望一开始全部加载,而是需要的时候进行加载;这样的做得目的可以很好的提高用户体验。 为了实现这个功能,Vue3中为我们提供了一个方法,即defineAsyncComponent,这个方法可以传递两种类型的参数,分别是函数类型和对象类型,接下来我们分别学习。 传递工厂函数作为参数 defineAsyncComponent方法接收一个工厂函数是它的基本用法,这个工厂函数必须返回一个Promise,Promise的resolve应该返回一个组件。 我们这里以Vue Cli创建的项目为例,这里我稍微做了一下修改,将头部的图片拆分为一个组件,代码如下: <template> <logo-img /> <hello-world msg="Welcome to Your Vue.js App" /> </template> <script setup> import LogoImg from './components/LogoImg.vue' import HelloWorld from './components/HelloWorld.vue' </script> 现在我们就将<hello-world>组件修改为异步组件,示例代码如下: <template> <logo-img /> <hello-world msg="Welcome to Your Vue.js App" /> </template> <script setup> import { defineAsyncComponent } from 'vue' import LogoImg from './components/LogoImg.vue' // 简单用法 const HelloWorld = defineAsyncComponent(() => import('./components/HelloWorld.vue'), ) </script> 我们这里为了看到效果,将import延迟执行,示例代码如下: <script setup> import { defineAsyncComponent } from 'vue' import LogoImg from './components/LogoImg.vue' // 定义一个耗时执行的函数,t 表示延迟的时间, callback 表示需要执行的函数,可选 const time = (t, callback = () => {}) => { return new Promise(resolve => { setTimeout(() => { callback() resolve() }, t) }) } // 定义异步组件,这里这样写是为了查看效果 const HelloWorld = defineAsyncComponent(() => { return new Promise((resolve, reject) => { ;(async function () { try { await time(2000) const res = await import('./components/HelloWorld.vue') resolve(res) } catch (error) { reject(error) } })() }) }) </script> 代码运行结果如下所示: ...

October 5, 2024

尾部优化

ES6 引入了尾调用优化(Tail Call Optimization,TCO)的规范,但它的实现依赖于 JavaScript 引擎的支持(比如 V8 并未开启 TCO),所以这是一个理论上重要、实践中需要谨慎对待的考点。 一、什么是尾调用(Tail Call)? 尾调用是指: 函数的最后一步是调用另一个函数,并直接返回这个函数的执行结果。 例如: function foo() { return bar(); // bar 是 foo 的尾调用 } 尾调用意味着: 当前函数的调用栈帧不需要再做任何事。 调用栈可以复用,不需要为当前函数保留栈帧,从而节省内存。 二、什么是尾调用优化(TCO)? 尾调用优化的目标是: 当一个函数的最后一步是调用另一个函数,JS 引擎可以不再保留当前函数的调用记录,直接跳转执行被调用函数,复用当前栈帧。 也就是说: 不会形成函数调用嵌套的“栈帧链”; 可以避免函数递归导致的栈溢出(stack overflow)。 三、尾调用成立的条件 必须同时满足以下三个条件: 条件 说明 1. 在严格模式下 TCO 只在 strict mode 下才可能被引擎开启 2. 是函数的最后一步调用 return 后没有任何操作 3. 返回调用结果 return bar(x) 形式,不能是 bar(x); return y ✅ 尾调用示例: 'use strict'; function f(x) { return g(x); // 最后一步是返回 g(x),符合尾调用优化条件 } ❌ 非尾调用: function f(x) { const y = g(x); return y; // 不是直接返回函数调用 } 四、尾调用优化应用场景:尾递归 递归调用时,若能做到尾调用,就能避免栈溢出。 ...

October 2, 2024

DOM操作

1、DOM基本概念 1.1、DOM、DOM节点与DOM树 DOM:(Document Object Model, 文档对象模型)是文档内容(HTML或XML)在编程语言上的抽象模型,它建模了文档的内容和结构,并提供给编程语言一套完整的操纵文档的API。 本文面向HTML,不讨论XML等文档,下文中“文档===HTML”。 DOM节点:简称节点(Node),是DOM模型的组成单元。HTML的基本单元是标签,节点常常与标签对应,但连续的文本内容也是一个文本标签。 DOM树:DOM树是DOM结构的表示形式,DOM把文档的每个节点根据父子关系连接,形成DOM树。 1.2、节点、节点类型和节点类 节点:前面提过,节点是DOM树的组成单元。在JS看来,一个节点就是JS对象。下面用node表示任意的节点。 节点类型:并非所有的节点都是一样的,DOM规定文档中有12种节点类型,分别用常量1 ~ 12(有与之对应的常量名称Node.XXX_NODE)表示,可以通过node.nodeType属性获取节点的类型常量。 现在,有些类型的节点已经弃用了,常见到的只有几种类型的节点,包括: 元素节点: 类型常量为Node.ELEMENT_NODE或1。最常见的一类节点,对应文档中的元素。大部分DOM操作都是在元素节点层次的。 文本节点:类型常量为Node.TEXT_NODE或2。对应文档中的文本,任何文档内容都有对应的文本节点,即使空格和换行符。 空格和换行不会对页面内容产生影响,但它们确实以文本节点的形式存在于DOM树中。 Document节点:类型常量为Node.DOCUMENT_NODE或9。它不对应文档的内容,而是作为文档的入口节点,每个文档都有且仅有一个入口,因为这种独特性,赋予一个特殊的变量名称document。 注释节点:类型常量为Node.COMMENT_NODE或8。它对应文档中的注释标签,文档的注释内容也是可读取和修改的。 节点类:DOM内置许多节点类,类之间存在继承关系,形成一套节点类框架。每个节点对象都属于节点类,拥有该类和其父类的方法与属性,这使得操作节点十分简单。节点类框架的一部分大概如图: 这些节点上有丰富的属性和方法,是继承的结果,可以看到,一个HTML标签元素至少有四层的继承关系。 以<a>标签为例,它属于HTMLAnchorElement类,获得了a.target,a.download等属性,接着继承了HTMLElement类上的title, hidden等属性和click()等方法,又从Element类继承了tagName, className等属性和getAttribute(), setAttribute()等方法,再从Node类继承了nodeType(前面说过的节点类型), appenChild(), removeChild()等方法,最后从EventTarget类中继承了事件相关的属性和方法。 不要混淆节点类型和节点类这两个概念。前者是一个生活中的类别,后者是编程意义上的类。节点对象的nodeType属性表示了它的类型,而节点类是该节点的从属的类。因为Dode是一个抽象类,所以,如果知道了某个节点从属的类,我们就知道它的节点类型。 区分节点与元素节点。我们经常关心元素节点(简称元素),因为这是一类最常使用的节点,但是并非所有节点都是元素。 1.3、探索DOM结构 前面的说法太抽象了,让我们用实际的例子看看文档、DOM树与节点的关系。 Live DOM Viewer是一个可以根据HTML文档实时查看DOM树的网站。你把下面的例子复制过去,或者自己去探索。 一个简单的HTML文档: <!DOCTYPE HTML> <html> <body> A simple text. <ol title="this is a title"> <li>czpcalm</li> <!-- comment --> </ol> </body> </html> 它对应的DOM图(颜色区分了节点类型): 留意这个图,你要注意几点: 总共有4种类型的节点,分别是标签节点(红色),文本节点(灰色),注释节点(黄色)和DOCTYPE节点(紫色)。 文档没有<head>标签却出现了HEAD节点。这是因为HTML必然存在<html>,<head>,<body>标签,不存在时会自动补上。顺便一提,当出现<table>标签时,也一定会有<tbody>标签。 文档中的文本都会形成文本节点的内容,包括空格␣和换行↵。第一,单独的空格和换行都会形成对应的文本节点;第二,有内容的文本节点的值包含前导和后继的空白。 不是说HTML中的空白字符都被忽略吗?怎么这里又说全都是有效的字符? 在从文档解析生成DOM树的过程中,HTML中的任何字符都是有效的;不过,在接下去的页面渲染的过程中,空白内容被忽略。所以从文档到页面的整过过程中,空白确实被忽略了。 理解DOM树中的父子关系对应文档中的包含(嵌套)关系。一个极佳的类比是文件树,把元素看做文件夹,文本看做文件,文件夹中可以存放文件和新的文件夹,然后一层层深入下去,DOM树也是如此。 几个原则可以帮我们快速理解这个DOM树的构建: ...

October 1, 2024

iOS反射

反射(Reflection)是指程序在运行时检查、访问和修改自身结构(类型、属性、方法等)的能力。在 iOS 开发中,Objective-C 和 Swift 分别提供了不同层次的反射支持:OC 依赖 Runtime 提供完整的动态反射能力,Swift 则通过 Mirror 提供只读的内省能力。 Objective-C 的反射能力 Objective-C 的反射能力完全建立在 Runtime 之上。Runtime 在运行时维护了完整的类型元数据(类对象、元类对象、方法列表、属性列表、成员变量列表、协议列表等),并通过一系列 C 函数 API 暴露给开发者,使得程序可以在运行时动态地查询和修改几乎所有类型信息。 关于 Runtime 的消息发送、消息转发、Method Swizzling、关联对象等核心能力,请参考 runtime。本节聚焦于 Runtime 中与"反射"直接相关的能力——即运行时的类型内省和动态操作。 类型内省 类型内省(Introspection)是反射中最基础的能力——在运行时查询一个对象的类型信息。 NSObject 提供的内省方法 NSObject 定义了一组内省方法,这些方法底层都依赖 Runtime 的 isa 指针和类型元数据来实现: id obj = [[NSMutableArray alloc] init]; // 类型判断 [obj isKindOfClass:[NSArray class]]; // YES — 判断是否是某个类或其子类的实例 [obj isMemberOfClass:[NSMutableArray class]]; // YES — 判断是否是某个类的直接实例 [obj conformsToProtocol:@protocol(NSCoding)]; // YES — 判断是否遵循某个协议 // 方法响应检查 [obj respondsToSelector:@selector(addObject:)]; // YES — 判断是否能响应某个消息 // 获取类型信息 NSStringFromClass([obj class]); // @"__NSArrayM"(真实的私有子类名) NSStringFromSelector(@selector(count)); // @"count" 需要注意 isKindOfClass: 和 isMemberOfClass: 的区别:前者沿继承链向上查找,后者只比较当前类。另外,[obj class] 返回的可能是私有子类(如类簇 NSArray 的实际类是 __NSArrayI 或 __NSArrayM),而 object_getClass(obj) 能获取真正的 isa 指向的类(KVO 场景下会是 NSKVONotifying_ 前缀的子类)。 ...

May 2, 2026

网络性能优化

浏览器在加载页面时,大量的性能瓶颈集中在资源的请求、传输、解析与执行上。因此,网络层的性能优化主要目标是:减少请求次数、降低资源大小、缩短首屏时间、提升加载体验。 一、请求数量优化 1. 合理拆分和合并资源 按需加载(lazy loading / dynamic import):不一次加载所有模块,只加载当前页面/功能所需资源。 资源合并(资源打包):减少请求数量,如合并多个 CSS/JS 文件(现代工具也支持 HTTP2 下保留拆分)。 服务端渲染/预渲染:减轻首屏资源请求压力。 2. 使用缓存避免重复请求 使用浏览器缓存控制(Cache-Control、ETag、Last-Modified); 对于不变资源,设置强缓存(immutable); 对接口返回数据做缓存(前端缓存策略或 HTTP 层缓存); 本地缓存,如 IndexedDB / LocalStorage 存储接口数据。 二、资源体积优化 1. 压缩与代码优化 启用 Gzip / Brotli 压缩静态资源(HTML、CSS、JS); 压缩图像(WebP、AVIF)、字体、视频等媒体资源; 减少 polyfill 体积(如使用 core-js 按需引入); 移除无用代码(Tree Shaking、CSS Purge、babel-plugin-transform-remove-console); 2. 精简传输内容 请求接口时使用精简字段、分页、后端裁剪; GraphQL 可按需返回字段,减少冗余数据传输; 降低 Cookie 和请求头大小,避免请求体负担。 三、加载顺序与优先级优化 1. 关键资源优先加载 使用 preload、prefetch、dns-prefetch 等 <link> 标签提示浏览器提前加载; 使用 Webpack 的资源预加载配置控制打包 chunk 优先级; 首屏资源内联(inline critical CSS)避免阻塞渲染。 2. 非关键资源延后加载 图片懒加载(loading="lazy"、IntersectionObserver); JS 异步加载(<script async|defer>); 第三方 SDK、监控等可延迟初始化。 四、协议与传输层优化 1. 使用 HTTP/2 或 HTTP/3 多路复用,减少 TCP 连接数,提高并发请求效率; 头部压缩与服务器推送(HTTP/2 Push); HTTP/3 基于 QUIC,有更快的连接建立速度与更少的丢包重传。 2. 减少不必要的重定向 避免链式跳转; 减少使用 302/301 等中间状态跳转,优化首次加载路径。 五、安全策略与跨域优化 合理设置 CORS,减少 preflight 请求; 使用同源策略时避免冗余 OPTIONS 请求; 减少跨域请求带来的额外开销(如 DNS、TLS 握手等)。 六、CDN 与资源分发优化 使用 CDN 加速静态资源访问,减少物理距离造成的 RTT; 使用全局 CDN 覆盖,支持边缘缓存与智能调度; 配合版本号和 Hash 控制缓存刷新策略。 七、服务端协同优化 接口合并:多个小请求可由后端合并为一次大请求返回; 接口压缩与限流:避免无用数据占带宽; 优化接口响应结构与字段粒度; 为前端提供异步批量资源接口(如配置/字典项/用户信息等合并加载); 八、实际指标与监控优化点 首字节时间(TTFB)优化; 首屏渲染时间(FCP、LCP)优化; 页面总加载时间(Fully Loaded)优化; 网络错误率(如资源 404、接口失败率)监控; 使用 Performance API、Web Vitals、Lighthouse 工具分析网络瓶颈。 要点总结 网络优化的本质是:更快、更少、更智能地请求资源; 从请求数量、资源大小、传输协议、加载优先级、缓存策略、CDN 分发等多个维度同时入手; 优化不是单一操作,而是持续评估、定位瓶颈、迭代调整的过程; 实际开发中建议结合 Chrome DevTools、Lighthouse、Performance API 等工具进行精细化分析与验证。 常见考点 前端“网络层面的性能优化”是高频面试考点,涵盖了从资源加载、缓存、连接管理,到协议选择等多个方向。面试时往往从“用户输入 URL 到页面加载”这条链路入手,考查在网络传输环节提升页面性能的能力。 ...

July 28, 2025

react-router

写在前面:为什么要学习react-router底层源码? 为什么要弄明白整个路由流程? 学习react-router,有助于我们学习单页面应用(spa)路由跳转原理,让我们理解从history.push,到组件页面切换的全套流程,使我们在面试的时候不再为路由相关的问题发怵,废话不说,让我们开启深入react-router源码之旅吧。 一 正确理解react-router 1 理解单页面应用 什么是单页面应用? 个人理解,单页面应用是使用一个html下,一次性加载js, css等资源,所有页面都在一个容器页面下,页面切换实质是组件的切换。 2 react-router初探,揭露路由原理面纱 ①react-router-dom和react-router和history库三者什么关系 **history** 可以理解为react-router的核心,也是整个路由原理的核心,里面集成了popState,history.pushState等底层路由实现的原理方法,接下来我们会一一解释。 **react-router**可以理解为是**react-router-dom**的核心,里面封装了Router,Route,Switch等核心组件,实现了从路由的改变到组件的更新的核心功能,在我们的项目中只要一次性引入react-router-dom就可以了。 **react-router-dom**,在react-router的核心基础上,添加了用于跳转的Link组件,和histoy模式下的BrowserRouter和hash模式下的HashRouter组件等。所谓**BrowserRouter和HashRouter,也只不过用了history库中createBrowserHistory和createHashHistory方法** react-router-dom 我们不多说了,这里我们重点看一下react-router。 ②来个小demo尝尝鲜? import { BrowserRouter as Router, Switch, Route, Redirect,Link } from 'react-router-dom' import Detail from '../src/page/detail' import List from '../src/page/list' import Index from '../src/page/home/index' const menusList = [ { name: '首页', path: '/index' }, { name: '列表', path: '/list' }, { name: '详情', path: '/detail' }, ] const index = () => { return <div > <div > <Router > <div>{ /* link 路由跳转 */ menusList.map(router=><Link key={router.path} to={ router.path } > <span className="routerLink" >{router.name}</span> </Link>) }</div> <Switch> <Route path={'/index'} component={Index} ></Route> <Route path={'/list'} component={List} ></Route> <Route path={'/detail'} component={Detail} ></Route> {/* 路由不匹配,重定向到/index */} <Redirect from='/*' to='/index' /> </Switch> </Router> </div> </div> } 效果如下 ...

December 17, 2024

拖放API

HTML5 中的拖拽(Drag and Drop,简称 DnD)功能为网页元素提供了原生的拖放支持。通过 HTML5 的拖放 API,开发者可以让元素变得可拖动,并实现拖拽和释放的交互效果。 HTML5 拖拽的基本原理 HTML5 拖拽操作的核心是 dragstart、dragover 和 drop 等事件。以下是拖拽的工作流程: 可拖动元素:使用 draggable="true" 使一个元素可被拖拽。 源元素触发拖拽:当用户点击并开始拖动可拖动元素时,触发 dragstart 事件。 拖拽中:拖拽过程中,目标区域会触发 dragover 事件。 释放元素:当用户释放被拖动的元素到目标区域时,目标会触发 drop 事件。 实现步骤 设置拖拽元素:使用 draggable="true" 使 HTML 元素可拖动。 监听拖拽事件: dragstart:当拖拽开始时触发。 dragover:在拖拽过程中,拖动到可放置目标区域时持续触发。 drop:当拖动的元素被释放到目标区域时触发。 阻止默认行为:dragover 和 drop 事件默认情况下是禁止的,需要使用 event.preventDefault() 来阻止默认行为,从而允许放置元素。 拖拽的相关事件 dragstart:在拖动开始时触发,用来设置拖动数据(如拖动的数据类型和数据内容)。 dragend:拖动操作结束时触发,可以用来清理状态。 dragenter:当拖动的元素进入目标区域时触发。 dragover:当拖动元素在目标区域上方移动时持续触发,默认情况下不会触发 drop,因此需要通过 event.preventDefault() 来允许放置。 dragleave:当拖动的元素离开目标区域时触发。 drop:当拖动的元素释放到目标区域时触发,可以获取拖动的数据。 拖拽实例 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>HTML5 拖拽示例</title> <style> #dragElement { width: 100px; height: 100px; background-color: lightcoral; text-align: center; line-height: 100px; cursor: grab; } #dropZone { width: 200px; height: 200px; background-color: lightblue; margin-top: 20px; text-align: center; line-height: 200px; } </style> </head> <body> <div id="dragElement" draggable="true">拖拽我</div> <div id="dropZone">放置到这里</div> <script> // 获取元素 const dragElement = document.getElementById('dragElement'); const dropZone = document.getElementById('dropZone'); // 拖拽开始事件 dragElement.addEventListener('dragstart', function(event) { // 设置数据,类型和内容 event.dataTransfer.setData('text', event.target.id); }); // 拖拽过程中在目标上方移动时,必须阻止默认行为 dropZone.addEventListener('dragover', function(event) { event.preventDefault(); }); // 放置事件 dropZone.addEventListener('drop', function(event) { event.preventDefault(); // 阻止默认行为 const data = event.dataTransfer.getData('text'); const draggedElement = document.getElementById(data); event.target.appendChild(draggedElement); // 将拖动的元素放入目标区域 }); </script> </body> </html> 代码说明 拖拽元素:draggable="true" 使得 #dragElement 可拖动。 拖拽开始:dragstart 事件设置了拖拽的元素数据,存入 dataTransfer 对象中。 允许放置:dragover 事件通过 event.preventDefault() 允许拖拽元素放置在目标区域。 放置元素:drop 事件在目标区域中,通过 event.dataTransfer.getData() 获取拖动的数据并进行处理。 常见考点 1. 基本概念与原理 定义:HTML5 提供了内置的拖拽功能,通过 draggable 属性和 Drag and Drop API 实现元素拖放操作。 原理:拖拽功能依赖一系列事件,如 dragstart、drag、dragend、dragover、drop 等来实现拖放操作。 2. 如何启用拖拽 draggable 属性:默认情况下,HTML 元素是不可拖拽的。要启用拖拽,需要设置元素的 draggable="true"。 <div draggable="true">Drag me!</div> 事件绑定:考察点可能包括如何通过 dragstart 等事件处理拖拽行为。 const draggableElement = document.getElementById('draggable'); draggableElement.addEventListener('dragstart', (event) => { event.dataTransfer.setData('text/plain', 'This is a draggable element'); }); 3. Drag and Drop 的核心事件 dragstart:用户开始拖动时触发,通常用于设置拖拽的数据和外观。 drag:在拖动过程中持续触发,可用于动态更新拖拽效果。 dragend:拖动结束时触发,可以用于清除拖拽状态。 dragenter:被拖拽的元素进入可放置区域时触发。 dragover:在拖动元素悬停在放置区域时持续触发,通常需要调用 event.preventDefault() 来允许放置。 dragleave:当拖动元素离开放置区域时触发。 drop:用户放开被拖动的元素时触发,用于处理放置操作。 4. 数据传输 dataTransfer 对象:event.dataTransfer 是拖拽过程中传输数据的关键对象。常见考点包括如何通过 dataTransfer.setData() 和 dataTransfer.getData() 方法在 dragstart 和 drop 事件之间传递数据。 // dragstart 中设置数据 event.dataTransfer.setData('text', 'Dragged Data'); // drop 中获取数据 const data = event.dataTransfer.getData('text'); 传输多种数据类型:考察如何传输不同的数据类型(如文本、URL、文件等)以及如何处理这些数据。 5. dropzone 区域的设置 允许放置元素:默认情况下,放置区域不会接受被拖拽的元素,需要在 dragover 事件中调用 event.preventDefault() 来显式地允许放置操作。 const dropzone = document.getElementById('dropzone'); dropzone.addEventListener('dragover', (event) => { event.preventDefault(); // 允许放置 }); 6. 拖拽的样式处理 自定义拖拽效果:通过 event.dataTransfer.setDragImage() 设置自定义的拖拽图片,考察点可能包括如何替换默认的拖拽预览图。 event.dataTransfer.setDragImage(imageElement, offsetX, offsetY); 拖拽时的 CSS 样式:考察如何使用 CSS 类和事件配合,实现拖拽时的视觉效果。 .dragging { opacity: 0.5; } 7. 跨页面或跨应用的拖拽 跨文档拖拽:考察是否了解如何处理跨 iframe 或浏览器窗口的拖拽。 拖拽文件:用户可以将外部文件(如从桌面拖动文件)拖入浏览器的 dropzone,需要处理 dataTransfer.files 属性。 const files = event.dataTransfer.files; 8. 拖拽与输入法的冲突 在移动设备上,拖拽和触摸事件可能存在冲突,因此考察点可能包括如何处理拖拽在移动端的实现或替代方案。 9. 可访问性 键盘可用性:默认拖拽操作主要依赖鼠标,因此考察点可能包括如何使拖拽操作可通过键盘导航,确保网站的无障碍性。 ARIA 支持:在实现拖拽功能时,如何使用 ARIA 属性为屏幕阅读器用户提供反馈是常见的可访问性考察点。 10. 兼容性 浏览器兼容性:HTML5 拖拽在不同浏览器中的支持情况不一致,尤其是在移动端,考察点可能包括如何处理浏览器兼容性问题,特别是在触摸设备上使用拖拽功能时的挑战。 11. 常见的应用场景 文件上传:通过拖拽文件进行上传是常见的考察场景,如何实现文件的拖放上传功能是常见问题。 可拖拽列表:如何通过拖拽重新排序列表项,如实现类似于 Trello 的拖放功能。 游戏和图形界面:拖放在交互式游戏或图形应用中被广泛使用,可能会考察如何通过拖拽元素实现复杂的交互效果。 12. 事件顺序 了解拖拽过程中的事件触发顺序也是一个重要的考察点,常见顺序为:dragstart -> drag -> dragenter -> dragover -> drop -> dragend。 13. 拖拽的取消 取消拖拽:通过调用 event.preventDefault() 可以阻止某些拖拽事件的默认行为,如取消 drop 事件。 14. 使用 JavaScript 框架实现拖拽 有些前端面试可能会考察如何利用 JavaScript 框架(如 React、Vue、Angular)实现拖拽交互,以及如何处理拖拽相关的状态管理和数据绑定。

October 17, 2024

响应式设计

引言 响应式布局指的是同一页面在不同屏幕尺寸下有不同的布局。传统的开发方式是PC端开发一套,手机端再开发一套,而使用响应式布局只要开发一套就够,缺点就是CSS比较重。下面是博客网站对不同设备适配后的结果,分别是iPhone5/SE,iphone6/7/8,iphone 6/7/8 plus,ipad pro,dell台式宽屏(1440 X 900)。 响应式设计与自适应设计的区别:响应式开发一套界面,通过检测视口分辨率,针对不同客户端在客户端做代码处理,来展现不同的布局和内容;自适应需要开发多套界面,通过检测视口分辨率,来判断当前访问的设备是pc端、平板、手机,从而请求服务层,返回不同的页面。 响应式布局的实现方案 1. 媒体查询 CSS3媒体查询可以让我们针对不同的媒体类型定义不同的样式,当重置浏览器窗口大小的过程中,页面也会根据浏览器的宽度和高度重新渲染页面。 如何选择屏幕大小分割点 如何确定媒体查询的分割点也是一个开发中会遇到的问题,下面是市场上的移动设备和电脑屏幕分辨率的分布情况,可以发现不同品牌和型号的设备屏幕分辨率一般都不一样 如果我们选择600px,900px,1200px,1800px作为分割点,可以适配到常见的14个机型: 当然这只是其中的一种分割方案,我们还可以这样划分:480px,800px,1400px,1400px 而作为曾经典型的响应式布局框架,Bootstrap是怎么进行断点的呢? 上面的分割方案不一定满足项目中的实际需求,我们可以先用跨度大的分割点进行分割,如果出现不适配的情况可以再根据实际情况增加新的分割点。 移动优先 OR PC优先 不管是移动优先还是PC优先,都是依据当随着屏幕宽度增大或减小的时候,后面的样式会覆盖前面的样式。因此,移动端优先首先使用的是min-width,PC端优先使用的max-width。 移动优先: /* iphone6 7 8 */ body { background-color: yellow; } /* iphone 5 */ @media screen and (max-width: 320px) { body { background-color: red; } } /* iphoneX */ @media screen and (min-width: 375px) and (-webkit-device-pixel-ratio: 3) { body { background-color: #0FF000; } } /* iphone6 7 8 plus */ @media screen and (min-width: 414px) { body { background-color: blue; } } /* ipad */ @media screen and (min-width: 768px) { body { background-color: green; } } /* ipad pro */ @media screen and (min-width: 1024px) { body { background-color: #FF00FF; } } /* pc */ @media screen and (min-width: 1100px) { body { background-color: black; } } PC优先: ...

October 8, 2024

vue路由

从后端路由开始 路由这个概念最先是后端出现的。在以前用模板引擎开发页面时,经常会看到这样 http://www.xxx.com/login 大致流程可以看成这样: 浏览器发出请求 服务器监听到80端口(或443)有请求过来,并解析url路径 根据服务器的路由配置,返回相应信息(可以是 html 字串,也可以是 json 数据,图片等) 浏览器根据数据包的 Content-Type 来决定如何解析数据 简单来说路由就是用来跟后端服务器进行交互的一种方式,通过不同的路径,来请求不同的资源,请求不同的页面是路由的其中一种功能。 前端路由 1. hash 模式 随着 ajax 的流行,异步数据请求交互运行在不刷新浏览器的情况下进行。而异步交互体验的更高级版本就是 SPA —— 单页应用。单页应用不仅仅是在页面交互是无刷新的,连页面跳转都是无刷新的,为了实现单页应用,所以就有了前端路由。 类似于服务端路由,前端路由实现起来其实也很简单,就是匹配不同的 url 路径,进行解析,然后动态的渲染出区域 html 内容。但是这样存在一个问题,就是 url 每次变化的时候,都会造成页面的刷新。那解决问题的思路便是在改变 url 的情况下,保证页面的不刷新。在 2014 年之前,大家是通过 hash 来实现路由,url hash 就是类似于: http://www.xxx.com/#/login 这种 #。后面 hash 值的变化,并不会导致浏览器向服务器发出请求,浏览器不发出请求,也就不会刷新页面。另外每次 hash 值的变化,还会触发hashchange 这个事件,通过这个事件我们就可以知道 hash 值发生了哪些变化。然后我们便可以监听hashchange来实现更新页面部分内容的操作: function matchAndUpdate () { // todo 匹配 hash 做 dom 更新操作 } window.addEventListener('hashchange', matchAndUpdate) 2. history 模式 14年后,因为HTML5标准发布。多了两个 API,pushState 和 replaceState,通过这两个 API 可以改变 url 地址且不会发送请求。同时还有popstate 事件。通过这些就能用另一种方式来实现前端路由了,但原理都是跟 hash 实现相同的。用了 HTML5 的实现,单页路由的 url 就不会多出一个#,变得更加美观。但因为没有 # 号,所以当用户刷新页面之类的操作时,浏览器还是会给服务器发送请求。为了避免出现这种情况,所以这个实现需要服务器的支持,需要把所有路由都重定向到根页面。 ...

October 5, 2024