http2.0 服务端推送

HTTP/2.0 的 服务端推送(Server Push) 是它相较于 HTTP/1.x 引入的一项重要功能,旨在优化网页加载性能,尤其是首次加载时资源依赖的获取效率。 一、什么是服务端推送? HTTP/2 Server Push 允许服务器主动将资源“推送”给客户端,而不是等待客户端明确请求。这在某些场景下可以预加载依赖资源、减少请求延迟,提升页面的首屏加载速度。 例如: 客户端请求了 HTML 页面,服务器可以在返回 HTML 的同时,主动推送页面所需的 CSS/JS 资源。 二、服务端推送的工作原理 流程概览: 浏览器发送主资源请求(如 HTML); 服务器识别该请求需要哪些依赖资源(如 CSS、JS); 服务器将这些资源 打包为 PUSH_PROMISE 帧 发送给客户端,声明它即将发送的资源; 客户端接收到 PUSH_PROMISE 后,会缓存该资源的响应; 当浏览器稍后真正需要这个资源时,不会再发起真实请求,而是从缓存中获取推送的内容; 避免了请求的 RTT 往返延迟。 技术细节: PUSH_PROMISE 是 HTTP/2 中新增的帧类型,用于声明服务器准备推送的资源; 所有推送的资源都绑定到一个主请求流(主页面),不能独立存在; 客户端可选择 拒绝(RST_STREAM) 不想要的推送资源; 推送资源会根据缓存策略存储在浏览器的缓存中,不一定立即使用。 三、实际示例(Nginx + Link Header) 在使用 Nginx 部署 HTTP/2 服务时,可以通过 Link 响应头启用 Server Push: location = /index.html { http2_push /style.css; http2_push /main.js; } 或者使用 Link 响应头方式: ...

July 28, 2025

错误边界

人无完人,代码总会出错,出错并不可怕,关键是怎么处理。 我就想问问大家react的应用的错误怎么捕捉呢? 这个时候: 小白+++:怎么处理? 小白++: ErrorBoundary 小白+: ErrorBoundary, try catch 小黑#: ErrorBoundary, try catch, window.onerror 小黑##: 这个是个严肃的问题,我知道N种处理方式,你有什么更好的方案? ErrorBoundary EerrorBoundary是16版本出来的,有人问那我的15版本呢,我不听我不听,反正我用16,当然15有unstable_handleError。 关于ErrorBoundary官网介绍比较详细,这个不是重点,重点是他能捕捉哪些异常。 子组件的渲染 生命周期函数 构造函数 class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } componentDidCatch(error, info) { // Display fallback UI this.setState({ hasError: true }); // You can also log the error to an error reporting service logErrorToMyService(error, info); } render() { if (this.state.hasError) { // You can render any custom fallback UI return <h1>Something went wrong.</h1>; } return this.props.children; } } <ErrorBoundary> <MyWidget /> </ErrorBoundary> 开源世界就是好,早有大神封装了react-error-boundary 这种优秀的库。 你只需要关心出现错误后需要关心什么,还以来个 Reset, 完美。 ...

December 17, 2024

地理定位

HTML5 提供了地理定位(Geolocation)API,用于获取用户的当前位置。通过这一功能,网页可以访问用户的地理位置信息,进而实现如地图服务、定位推荐、基于位置的广告等功能。该 API 主要依赖设备的 GPS、Wi-Fi、IP 地址、蓝牙等信息来确定位置。 Geolocation API 的基本用法 主要通过 navigator.geolocation 对象来访问地理位置信息,提供了以下三个方法: getCurrentPosition(success, error, options) 获取用户的当前位置,一次性返回位置信息。 watchPosition(success, error, options) 持续监听用户的位置变化,并且实时返回新的位置信息。 clearWatch(id) 取消 watchPosition 监听的位置变化。 方法的参数说明 success(position):成功获取位置时的回调函数。position 对象包含位置信息,如经度、纬度、速度等。 error(error):可选参数,获取位置失败时的回调函数。error 对象包含错误信息,如用户拒绝提供位置或获取超时等。 options:可选参数,包含配置选项,影响获取位置的精度、超时时间等。 使用示例 if (navigator.geolocation) { navigator.geolocation.getCurrentPosition( (position) => { const latitude = position.coords.latitude; const longitude = position.coords.longitude; console.log(`Latitude: ${latitude}, Longitude: ${longitude}`); }, (error) => { console.error(`Error Code = ${error.code}: ${error.message}`); }, { enableHighAccuracy: true, timeout: 5000, maximumAge: 0 } ); } else { console.log("Geolocation is not supported by this browser."); } 获取到的 position 对象 position 对象包含用户的位置信息,主要属性包括: ...

October 16, 2024

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

网络性能优化

浏览器在加载页面时,大量的性能瓶颈集中在资源的请求、传输、解析与执行上。因此,网络层的性能优化主要目标是:减少请求次数、降低资源大小、缩短首屏时间、提升加载体验。 一、请求数量优化 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