输入 URL 到页面渲染完成

一、网站加载概述 面试过程中,常常遇到这样一道面试题,输入URL到页面加载完毕,浏览器做了哪些工作? 首先输入一个URL,你会看到浏览器上面的标签页出现了一个loading图标,开始时是逆时针旋转,接着顺时针旋转,当前页面消失,显示我们常说的空白页面,接着出现显示我们请求的新页面。此时如果网络很差,你有可能看到短暂的DOM页面,然后再看到渲染后的正常页面,这是从表面看到的加载过程,实际浏览器做的要多得多。 为什么浏览器这么多戏呢?直接显示不好吗,当然不行,就像喝粥,能直接吃米、喝水吗? 在用户输入URL到页面展示,浏览器要先向服务器获取前端资源,然后再将服务器返回的字节流转化成对应的页面,每一阶段都需要浏览器对应的能力进行处理的。 作为前端开发,了解整个过程其实很重要,只有知道了浏览器加载页面的整个过程,才能在开发中避免可能跳的坑,才能在发现问题后迅速定位问题,才能在性能优化时提出更多的解决方案。 比如下面问题,看了文章相信你就知道了为什么了 为什么会存在空白页面?(为了解决这个问题,各大厂都在实践各种方案) 为什么js要在Dom后面引入 为什么有个页面崩溃会导致多个页面崩溃 浏览器的缓存策略是怎样的? 为什么我们常常修改host就可以访问对应的域名 等等 二、浏览器的多进程架构 为了看懂下面的内容,这里必须要了解下现代浏览器的多进程架构,进程和线程的关系等知识点。 浏览器也是从单个进程架构一步步迭代到现代的多进程架构。 如上图,我们可以看到线程和进程的关系 通常一个程序实例就是一个进程,浏览器会为他分配内存空间。 一个进程间的数据是共享的,单线程就是一个进程包含一个线程,一个线程处理所有的任务 多线程就是,一个进程中包含多个线程可以同时执行任务,共享数据和内存 线程不能单独存在,必须依附于进程,一个线程失败,会导致进程执行失败,进程销毁,内存会被立即回收。 1、进程 下图是我们打开一个掘金首页后,再打开任务管理器,观察发现,此时浏览器包括的进程有很多,浏览器主进程,GPU进程,网络进程,存储进程,音频进程,渲染进程,多个插件进程。 这些进程负责的功能如下: 进程 说明 浏览器进程 负责浏览器各个子进程的通信,处理浏览器界面,包括地址栏等 渲染进程 也就是我们看到的图中的标签页进程,也就是我们常说的浏览器内核,v8就在这个进程。主要负责解析html、js、css渲染页面等 网络进程 负责发起网络请求,解析返回头信息 GUI进程 负责将渲染进程生成的图块转化成位图 渲染进程是运行在沙箱之中的,可以执行js,但是不能获取系统权限,和浏览器进程通过IPC通讯。这是为了保证浏览器进程的安全,锁进沙箱,即使有恶意代码,也不能突破沙箱读取或写入系统信息。 2、线程 通过上图,我们已经知道线程是不能独立存在的,必须依附于进程。这里我们主要关注下渲染进程,因为他的主要工作就是:完成页面的渲染和展示。 如图,我们可以看到渲染进程包括很多线程,除了图上展示的,还有合成线程等,各有各的作用,具体内容,我们将会在后面一章节,浏览器渲染原理分析中在来讨论。 三、浏览器请求的具体流程 从用户输入域名到浏览器渲染页面完成的过程,可以分为以下几个部分: 1、输入信息处理 2、网络请求 3、服务器返回请求资源 4、浏览器渲染 这里每一步涉及到的知识都非常多,其中缓存也在每个阶段占了很大比重,接着展开描述前三个阶段: 1、地址栏输入信息处理 当输入一个URL,浏览器会判断输入信息是检索信息还是请求URL 如果是检索的信息,就构建请求搜索的URL,调用浏览器默认的搜索引擎进行检索。 如果符合URL格式,浏览器主进程就通过IPC通信机制将URL发送给网络进程。 2、网络进程发起网络请求 1)网络进程首先会查找浏览器缓存,判断缓存是否存在,是否过期 如果存在且不过期,就直接返回缓存信息。具体的缓存策略可以接着看下面的浏览器缓存策略 2)如果没有缓存或过期,就开始进行DNS解析 DNS解析的过程也很复杂,最终目的就是拿到目标主机的IP地址,具体的解析过程可以看下面的域名解析 3)建立http连接或https连接 http通过三次握手建立连接 https需要建立TLS连接,浏览器会验证网站的数字证书是否合法,是否到期,是否安全等,这里https有自己的一套认证逻辑,我们重点不在这一块。 这里两个问题常被问到,不了解可以学习一下 http和https的区别? 简述三次握手、四次挥手? 4)发送请求 网络进程会构建http请求头,向服务器发送实际请求 5)网络传输,服务器处理,返回对应的资源 请求从应用层发出,经过运输层、网络层、物理层、数据链路层找到服务器,服务器拿到请求信息,返回对应的资源 这里的服务器可能是代理服务器,也可能是CDN节点,会判断当前数据是否存在缓存,如果有缓存且有效,直接返回(具体看配置的缓存策略),否则才会从服务器获取。 网络传输数据也是很复杂的过程,具体可以看下面简单的介绍计算机网络体系模型 3、服务器返回对应资源 1)处理返回信息 ...

November 5, 2024

浏览器解析 HTML 文件

1. 什么是浏览器解析和渲染HTML 一句话概括:将html代码转换成页面上显示的内容。 表1:形象展示将HTML转换成页面上显示的内容 2. 浏览器解析和渲染HTML过程? 那么浏览器拿到HTML代码后是如何把其转换成页面上显示的内容的? 2.1 宏观描述执行过程 2.1.1 解析和渲染 首先我们来看两个概念,解析和渲染,确切来说是DOM解析和DOM渲染。 DOM解析: 生成DOM tree的过程。就是把你所写的各种html标签,生成一个DOM tree,可以认为就是生成了一个最原始的页面,一点样式都没有,毫无CSS修饰。 DOM渲染: 将DOM tree与css样式结合,生成Render tree,并将其呈现在浏览器页面上。浏览器会把本身默认的样式+用户自己写得样式整合到一起,形成一个CSS tree,而DOM渲染就是指DOM tree 和CSS tree 结合到一起,生成一个Render tree,呈现出一个带有样式的页面。 这里需要注意,DOM解析和DOM渲染由两个不同的线程来完成的,简单说就是二者可以并行进行。 DOM解析通常是由浏览器的主线程来完成的。DOM渲染是由浏览器的渲染线程来完成的。 分离解析和渲染的这种设计使得浏览器能够在解析 HTML 文档的同时将页面渲染到屏幕上,这样用户就能够更快地看到完整的页面了。 2.1.2 宏观描述执行过程 补充了解析和渲染的概念,我们来看看浏览器具体是如何把其转换成页面上显示的内容的。其过程大致分为下图的四步。 图1:浏览器解析和渲染HTML的过程 第一步:将载入的HTML文件解析成DOM树(DOM tree),并且将各个标记标识解析成DOM树的各个节点;在解析HTML的同时会将CSS样式解析成CSS规则(CSS rules)。 第二步:将解析成的DOM树和CSS规则进行关联生成渲染树(Render tree)。 第三步:进入布局阶段,为DOM树的每个节点分配在屏幕上出现的确切坐标(这一阶段还是渲染树) 第四步:进入绘制阶段,在这里渲染引擎的工作就结束了,接下来就给用户界面后端(UI Backend)对渲染树的每个节点进行绘制,呈现出页面效果。 2.2 细化执行过程 2.2.1 顺序执行 dom解析过程是从上向下,顺序执行的。顺序执行的意思是一行一行的执行,上一行执行完再执行下一行。 <h1>1<h1> <h1>2<h1> <h1>3<h1> <script></script> <style></style> <!-- 先1,再2,再3,按顺序解析。 --> 2.2.2 渐进式 浏览器是渐进式渲染,即边解析边渲染。通俗的说就是,这一行代码解析完,就可以在浏览器的页面渲染展示出来。因为浏览器的解析和渲染由两个不同线程来完成,所以可以实现边解析边渲染。 <h1>1</h1> <script>debugger</script> <h1>2</h1> <script>debugger</script> <h1>3</h1> <!-- 先解析渲染出1,再debugger;再解析渲染出2,再debugger;最后解析渲染出3 --> 2.3 问题:css、js会阻塞解析和渲染吗? 这部分内容可参考这篇掘金内容,内含测试代码:关于 JS 与 CSS 是否阻塞 DOM 的渲染和解析 - 掘金 ...

November 5, 2024

CSR,SSR和SSG

SPA、MPA、SSR 和 CSR 这几个词可能在你的工作生涯中经常出现,网上相关文章也很多。如果你对这些概念感到模糊或完全不了解,希望本文能帮助你理解这些渲染模式。 SPA 与 MPA MPA MPA(multiple page application) 称为多页应用, 指的是有多个页面的应用,从技术手段上来讲,你可以这么粗略地理解。 首屏速度快:各个页面相互独立,需要单独维护多个 html 页面,每个请求都直接返回 html。 切换页面比较慢:基于原生浏览器的文档跳转(navigating across documents)。因此每一次的页面更新都是一次页面重载,这将带来巨大的重启性能消耗。 对 SEO 友好:页面在初始时,就具有全部的页面内容而非 「无状态」,从而达到更好的收录效果。 SPA SPA(single page application)称为单页应用。通过 js 去感知到 url 的变化,动态的将当前页面的内容清除掉,然后将下一个页面的内容挂载到当前页面上。此时的路由不是后端来做了,而是前端来做,动态显示需要的组件。 页面切换速度快:路由跳转是基于特定的实现(如 vue-router,react-router 等前端路由),而非原生浏览器的文档跳转,避免了不必要的整个页面重载。 前后端分离:基于前端路由,SPA 与应用后端解耦,使得前端不再依赖于后端的路由分配。 首屏时间慢:首屏除了 html 还要额外请求并执行 js 文件,通过 js 在空页面上渲染首屏。 SEO 不友好:内容都是靠 js 渲染生成出来的,但搜索引擎并不识别这部分内容,导致 SEO 效果差 CSR(Client Side Rendering) CSR(客户端渲染)是一种在浏览器上执行 JavaScript 以生成 DOM 并显示内容的渲染方法。CSR 更贴近现代前端开发,我们通常使用的 Vue 和 React 默认使用 CSR。其大致流程如下: 浏览器向前端服务器请求 html 和 js html 页面为空,初始加载不显示任何内容,通过执行 js 渲染内容 通过后端暴露的 API 进行交互 对于典型的 CSR 应用程序,浏览器仅接收一个用作应用程序容器的 HTML 页面,因此也称为单页应用,所以 CSR 的特点与之前提到的 SPA 类似。 ...

November 5, 2024

SEO

🌻 前言 SEO 代表搜寻引擎最佳化/搜寻引擎优化(英文全名Search Engine Optimization,简称SEO),是指通过了解搜寻引擎的自然排名的算法逻辑,以提高目标网站在有关搜寻引擎内排名的方式。 网站的 SEO 至关重要,它可以让你的网站获得更好的排名和流量,从而提高网站知名度。对于一些盈利的网站,做好seo,还可以以低成本提高投资回报率。 本文将为你带来非常全面的SEO方案🔥🔥🔥,当然了,网站SEO是长线工作,在做好一些基础的配置之后,更重要的是后期的维护,比如定期更新网站动态文章,不断寻找优质外链资源等🤌。 本文将以三个层面展开: TDK优化 网站质量 SEO手段(10种手段) ❄️ 一、TDK优化 TDK是Title(页面标题)、Meta Description(页面描述)和Meta Keywords(页面关键词)的缩写,对网站的这三个信息的提炼是网站SEO的重要环节。 但是由于一些原因,各大主流搜索引擎基本都已经大大降低甚至移除了 <keywords> 对排名的影响。例如: 百度:几乎放弃了对标签的参考价值。参考:百度官方称:keywords这个meta标签已经被丢进历史垃圾堆了; 谷歌:目前几乎已经不考虑这个标签对于SEO的作用了,它可能更侧重于内容的相关性以及反向链接的考量。参考:# Google 不会将关键字元标记用于网页排名 必应:目前 keywords 标签仍然对排名有一定影响; (一)title标签-网站名片 title标签相当于网站的名片,他会直接显示在搜索结果中。一个好的标题势必可以为网站带来流量,从而提升网站排名。 注意:网站标题避免冗长 例如:<title>掘金</title> (二)META标签-网站信息 META标签是网页head区的辅助性标签,它的作用是经过配置一些参数用以描述页面属性。目前几乎所有搜索引擎都使用网上机器人自动查找meta值来给网页分类。 2.1 meta标签如何使用 meta标签的属性有两种:name和http- equiv。 🍀 “name"属性有以下配置项: Keywords(关键词,现在不再重要了):逗号分隔的关键词列表(告诉搜索引擎页面是与什么相关的); description(网站内容描述,很重要):页面描述。搜索引擎会把这个描述显示在搜索结果中; format-detection:格式检测,比如禁止识别电话,邮箱等; author:作者的名字; Robots:用来告诉搜索机器人哪些页面需要索引,哪些页面不需要索引; theme-color:网站主题色; <meta name="keywords" content="掘金,稀土,Vue.js,前端面试题,Kotlin,ReactNative,Python"> <meta name="description" content="掘金是面向全球中文开发者的技术内容分享与交流平台。我们通过技术文章、沸点、课程、直播等产品和服务,打造一个激发开发者创作灵感,激励开发者沉淀分享,陪伴开发者成长的综合类技术社区。"> <meta name="format-detection" content="telephone=no"> <meta name="author" content="cece"> <Meta name="Robots" Content="Nofollow"> /** all:文件将被检索,且页面上的链接可以被查询; none:文件将不被检索,且页面上的链接不可以被查询;(和 "noindex, no follow" 起相同作用) index:文件将被检索;(让robot/spider登录) follow:页面上的链接可以被查询; noindex:文件将不被检索,但页面上的链接可以被查询;(不让robot/spider登录) nofollow:文件将不被检索,页面上的链接可以被查询。(不让robot/spider顺着此页的连接往下探找) */ <meta name="theme-color" content="#4285f4" /> 🍀 “http- equiv"属性有以下配置项: ...

November 5, 2024

meta 标签

概览 meta标签一般放在整个html页面的head部分,在MDN中对他这样定义: meta是文档级元数据元素,用来表示那些不能由其它 HTML 元相关元素(<base>、<link>, <script>、<style>或 <title>)之一表示的任何元数据。 是不是感觉看起来很抽象?说白了就是为了传达信息。 先看看meta 元素定义的元数据的类型: 如果设置了 name属性,meta 元素提供的是文档级别的元数据,应用于整个页面。 如果设置了 http-equiv属性,meta 元素则是编译指令,提供的信息与类似命名的 HTTP 头部相同。 如果设置了 charset属性,meta 元素是一个字符集声明,告诉文档使用哪种字符编码。 如果设置了 itemprop 属性,meta 元素提供用户定义的元数据。 name属性 name和content一起使用,前者表示要表示的元数据的名称,后者是元数据的值。 author 用来表示网页的作者的名字,例如某个组织或者机构。 <meta name="author" content="aaa@mail.abc.com"> description 是一段简短而精确的、对页面内容的描述。以头条和taobao的description标签为例: keywords 与页面内容相关的关键词,使用逗号分隔。某些搜索引擎在遇到这些关键字时,会用这些关键字对文档进行分类。 还是以头条和taobao为例 viewpoint 为 viewport(视口)的初始大小提供指示。目前仅用于移动设备。 可能你也发现了,我们在vscode中自动生成html的代码片段时,会自动生成: <meta name="viewport" content="width=device-width, initial-scale=1.0"> width用来设置 viewport 的宽度为设备宽度; initial-scale为设备宽度与 viewport 大小之间的缩放比例。 robots 表示爬虫对此页面的处理行为,或者说,应当遵守的规则,是用来做搜索引擎抓取的。 它的content可以为: all:搜索引擎将索引此网页,并继续通过此网页的链接索引文件将被检索 none:搜索引擎讲忽略此网页 index:搜索引擎索引此网页 follow:搜索引擎继续通过此网页的链接索引搜索其它的网页 renderer 用来指定双核浏览器的渲染方式,比如360浏览器,我们可以通过这个设置来指定360浏览器的渲染方式 <meta name="renderer" content="webkit"> //默认webkit内核 <meta name="renderer" content="ie-comp"> //默认IE兼容模式 <meta name="renderer" content="ie-stand"> //默认IE标准模式 http-equiv http-equiv也是和content一起使用,前者表示要表示的元数据的名称,后者是元数据的值。 http-equiv 所有允许的值都是特定 HTTP 头部的名称, ...

November 5, 2024

与 CSS/JS 的交互

HTML、CSS 和 JavaScript 是前端开发的三大基础技术,它们通过不同的方式相互作用,创建交互性良好的网页。 1. HTML 与 CSS 的交互 HTML 负责定义网页的结构,而 CSS 则负责美化这些结构元素。两者的交互主要通过以下方式: <style> 标签:直接在 HTML 中嵌入 CSS 样式。 这种方式将 CSS 写入到 HTML 文件中,但不推荐用于大型项目,因为这会导致样式与结构混杂在一起。 <style> h1 { color: red; } </style> <link> 标签:通过 <link> 标签引入外部 CSS 文件。 这种方式通常用于大型项目中,将样式与结构分离,维护更加清晰。 <link rel="stylesheet" href="styles.css"> 内联样式:直接将样式属性写在 HTML 标签内。 这种方式适合临时的小改动,但不推荐频繁使用,尤其在大型项目中不利于维护。 <h1 style="color: blue;">这是一个标题</h1> 2. HTML 与 JavaScript 的交互 JavaScript 负责操作和控制网页内容,主要与 HTML 进行交互以实现动态效果。以下是交互方式: <script> 标签:通过 <script> 标签直接在 HTML 中嵌入 JavaScript 代码。 此方法会在 HTML 中直接执行 JavaScript,适合小规模的脚本。 <script> alert('这是一个弹窗'); </script> 外部 JavaScript 文件:通过 <script> 标签引入外部 JavaScript 文件。 这种方式将 JavaScript 逻辑与 HTML 分离,便于维护和重用。 <script src="script.js"></script> DOM 操作:通过 JavaScript 操作 DOM(文档对象模型),动态更改 HTML 元素的内容和属性。例如: <p id="text">这是原始文本</p> <script> document.getElementById('text').innerText = '这是通过 JS 改变的文本'; </script> 事件监听:JavaScript 可以通过事件监听器响应用户的交互。例如: <button id="btn">点击我</button> <script> document.getElementById('btn').addEventListener('click', function() { alert('按钮被点击了'); }); </script> 3. CSS 与 JavaScript 的交互 JavaScript 也可以与 CSS 进行交互,动态地控制页面的样式。常见的方法有: ...

October 17, 2024

全屏API

在 HTML 中,可以通过调用浏览器的全屏 API 实现页面或页面中的某个元素全屏显示。全屏 API 是 HTML5 提供的一项功能,允许将元素切换为全屏模式。 使用全屏 API 实现全屏 全屏 API 提供了以下常用方法和属性: requestFullscreen():请求将元素设置为全屏模式。 exitFullscreen():退出全屏模式。 fullscreenElement:返回当前正在全屏显示的 DOM 元素,如果没有元素全屏,则返回 null。 fullscreenchange 事件:当全屏状态发生变化时触发,用来监听进入或退出全屏。 实现全屏的基本步骤 选择你希望全屏的元素。 使用 element.requestFullscreen() 方法来进入全屏。 使用 document.exitFullscreen() 来退出全屏模式。 示例代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>全屏示例</title> <style> #fullscreenContent { width: 100px; height: 100px; background-color: lightblue; margin: 50px auto; text-align: center; line-height: 100px; } </style> </head> <body> <div id="fullscreenContent">点击全屏</div> <button id="exitFullscreenBtn" style="display:none;">退出全屏</button> <script> const fullscreenContent = document.getElementById('fullscreenContent'); const exitFullscreenBtn = document.getElementById('exitFullscreenBtn'); // 进入全屏模式 fullscreenContent.addEventListener('click', function() { if (fullscreenContent.requestFullscreen) { fullscreenContent.requestFullscreen(); } else if (fullscreenContent.mozRequestFullScreen) { // Firefox fullscreenContent.mozRequestFullScreen(); } else if (fullscreenContent.webkitRequestFullscreen) { // Chrome, Safari and Opera fullscreenContent.webkitRequestFullscreen(); } else if (fullscreenContent.msRequestFullscreen) { // IE/Edge fullscreenContent.msRequestFullscreen(); } exitFullscreenBtn.style.display = 'inline-block'; }); // 退出全屏模式 exitFullscreenBtn.addEventListener('click', function() { if (document.exitFullscreen) { document.exitFullscreen(); } else if (document.mozCancelFullScreen) { // Firefox document.mozCancelFullScreen(); } else if (document.webkitExitFullscreen) { // Chrome, Safari and Opera document.webkitExitFullscreen(); } else if (document.msExitFullscreen) { // IE/Edge document.msExitFullscreen(); } exitFullscreenBtn.style.display = 'none'; }); // 监听全屏状态变化 document.addEventListener('fullscreenchange', () => { console.log('全屏状态发生变化'); }); </script> </body> </html> 代码解释 进入全屏:点击 #fullscreenContent 的 div 时,调用 requestFullscreen() 方法使该元素进入全屏模式。 退出全屏:点击按钮 #exitFullscreenBtn,调用 document.exitFullscreen() 退出全屏模式。 跨浏览器兼容性:由于部分浏览器的全屏 API 有不同的实现方法,代码中兼容了各个主流浏览器的实现(例如 Chrome, Firefox, Safari, Edge)。 监听全屏状态变化:使用 fullscreenchange 事件来监听当前全屏模式的变化,可以在进入或退出全屏时执行额外操作。 全屏 API 的一些注意事项 全屏 API 只能在用户交互(如点击或按键事件)后触发,不能通过脚本自动进入全屏模式,这是为了防止用户体验被打断。 不同浏览器的全屏 API 实现可能稍有不同,因此通常需要做一些兼容性处理。 在移动端设备上,全屏模式的行为可能与桌面端有所不同。 总结 HTML5 的全屏 API 提供了一种简单而直观的方法来让页面或元素进入全屏模式。通过 requestFullscreen() 方法可以将任意元素全屏显示,exitFullscreen() 则用来退出全屏模式。结合 fullscreenchange 事件可以对全屏状态进行监控,实现更加丰富的交互体验。 ...

October 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

地理定位

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

Web Workers

在浏览器和服务器上,JavaScript程序在单个处理线程上运行。这意味着程序一次只能做一件事。简单来说,你的新电脑可能有32核的CPU,但当JavaScript应用程序运行时,其中31个核心都处于空闲状态。 JavaScript的单线程避免了复杂的并发情况。如果两个线程同时尝试进行不兼容的更改会发生什么?例如,一个浏览器可能在另一个线程重定向到新的URL并从内存中清除该文档时正在更新DOM。Node.js、Deno和Bun都继承了浏览器的单线程引擎。 这不是JavaScript特定的限制。大多数语言都是单线程的,但像PHP和Python这样的Web选项通常在Web服务器上运行,为每个用户请求启动解释器的新线程的不同实例。这会消耗资源,因此Node.js应用程序通常定义自己的Web服务器,该服务器在单个线程上运行,并异步处理每个传入的请求。 Node.js的方法在处理高流量负载时可能更高效,但长时间运行的JavaScript函数会消耗效率收益。 在我们演示如何使用Web Workers解决执行速度问题之前,我们首先要了解JavaScript的运行方式以及为什么长时间运行的函数会引发问题。 JavaScript非阻塞I/O事件循环 你可能会认为一次只做一件事会导致性能瓶颈,但JavaScript是异步的,这就避免了大部分单线程处理问题,因为: 不需要等待用户在网页上点击按钮。 浏览器触发一个事件,当点击发生时调用JavaScript函数。 不需要等待Ajax请求的响应。 浏览器触发一个事件,在服务器返回数据时调用JavaScript函数。 Node.js应用程序不需要等待数据库查询的结果。 运行时在数据可用时调用JavaScript函数。 JavaScript引擎运行一个事件循环。一旦代码的最后一条语句执行完毕,运行时会循环回来,检查是否有未完成的定时器、待处理的回调函数和数据连接,然后根据需要执行回调函数。 其他操作系统处理线程负责调用诸如HTTP请求、文件处理和数据库连接等输入/输出系统。它们不会阻塞事件循环。事件循环可以继续执行下一个在队列中等待的JavaScript函数。 实质上,JavaScript引擎的主要责任是运行JavaScript代码。操作系统处理所有其他可能导致引擎调用JavaScript函数的I/O操作。 长时间运行的JavaScript函数 JavaScript函数通常由事件触发。它们会进行一些处理,输出一些数据,并且大部分情况下会在毫秒级别内完成,以便事件循环可以继续运行。 不幸的是,一些长时间运行的函数可能会阻塞事件循环。想象一下,你正在开发自己的图像处理函数(如锐化、模糊、灰度化等)。异步代码可以从文件中读取(或写入)数百万字节的像素数据,这对JavaScript引擎影响很小。然而,处理图像的JavaScript代码可能需要几秒钟来计算每个像素。该函数会阻塞事件循环,直到它完成,其他JavaScript代码无法运行。 在浏览器中,用户将无法与页面进行交互。他们无法点击、滚动或输入,并且可能会看到一个“脚本无响应”的错误,其中有停止处理的选项。 对于Node.js服务器应用程序而言,情况更糟糕。在函数执行时,它无法响应其他请求。如果函数需要花费10秒钟来完成,那么在那个时间点访问的每个用户都必须等待最多10秒钟,即使他们没有处理图像。 你可以通过将计算拆分为较小的子任务来解决这个问题。下面的代码使用传递的imageFn函数,最多处理1,000个像素(从一个数组中)。然后,它使用1毫秒的setTimeout延迟调用自身。事件循环的阻塞时间较短,这样JavaScript引擎可以在迭代之间处理其他传入的事件: // pass callback function, image processing function, and input image // return an output image to a callback function function processImage( callback, imageFn = i => {}, imageIn = [] ) { const chunkSize = 1000; // pixels to process per iteration // process first chunk let imageOut = [], pointer = 0; processChunk(); // process a chunk of data function processChunk() { const pointerEnd = pointer + chunkSize; // pass chunk to image processing function imageOut = imageOut.concat( imageFn( imageIn.slice( pointer, pointerEnd ) ) ); if (pointerEnd < imageIn.length) { // process next chunk after a short delay pointer = pointerEnd; setTimeout(processChunk, 1); } else if (callback) { // complete - return output image to callback function callback( null, imageOut ); } } } 这种方法可以防止脚本无响应,但不总是切实可行的。单个执行线程仍然会执行所有的工作,即使CPU可能有更多的处理能力。为了解决这个问题,我们可以使用Web Workers。 ...

October 16, 2024