前端面试题手册

梳理高频技术问题,帮助你按主题复习和查漏补缺。

前端阅读 1062024年7月2日 12:16

在开发 Electron 应用程序时,如何处理用户身份验证和授权?

在开发 Electron 应用程序时,处理用户身份验证和授权可以通过以下步骤实现:选择合适的身份验证方法:可以使用传统的用户名和密码验证。集成第三方身份验证服务,如 OAuth, OpenID Connect, 或使用 Google, Facebook 等社交登录。前端实现:在 Electron 的渲染进程中,创建登录界面。使用 HTML 表单收集用户凭证。安全传输:确保应用与服务器之间的通信是加密的,使用 HTTPS 来传输用户信息。后端验证:在主进程中或服务器端,接收并验证用户凭证。可以在本地使用数据库存储用户信息,或使用外部服务进行验证。使用 JWT 或 Session 管理会话:登录成功后,服务器生成一个 JWT(JSON Web Token)或 session,并发送回客户端。Electron 应用存储这个 token,并在后续请求中使用它来验证用户身份。授权:根据用户的角色和权限设置,判定用户可以访问的资源。可以在每次请求中检查 token 或 session 的有效性,以及用户的授权状态。安全注意事项:保护用户数据,避免存储敏感信息或在本地明文存储密码。定期更新和维护安全措施,防止潜在的安全威胁。通过这些步骤,可以在 Electron 应用程序中实现有效的用户身份验证和授权。
前端阅读 262024年7月1日 12:08

Flutter 为什么需要 mixin?

在Flutter中,mixin主要用于在多个类之间共享代码。mixin可以帮助开发者在不必创建复杂的类继承结构的情况下,实现代码的重用。这是非常有益的,尤其是在需要将某些功能跨多个类共享时。1. 增强代码的可维护性和可读性Mixin允许开发者将不同功能模块化,每个mixin维护一组特定的方法或属性。这种方式可以使得代码结构更清晰,每个部分都有明确的责任划分,从而使得代码更易于理解和维护。2. 避免多重继承的问题在一些编程语言中,多重继承可能导致诸多问题,如菱形继承问题(Diamond Problem)。而Flutter中的mixin类似于多重继承,但它提供了一种更安全的方式来合并多个类的功能,无须担心传统多重继承中的问题。3. 提高代码复用性通过使用mixin,可以在多个类之间共享方法和属性而不需要继承它们。这在Flutter开发中尤其有用,因为它有助于减少代码冗余,通过复用增加代码的效率。示例:假设我们正在开发一个Flutter应用,需要在多个页面中处理用户的输入验证。我们可以创建一个mixin来处理输入验证:mixin InputValidator { bool isValidEmail(String email) { return email.contains('@'); } bool isValidPassword(String password) { return password.length > 6; }}class LoginForm with InputValidator { String email; String password; void validateCredentials() { if (isValidEmail(email) && isValidPassword(password)) { print("Credentials are valid"); } else { print("Invalid credentials"); } }}class SignupForm with InputValidator { String email; String password; void validateCredentials() { if (isValidEmail(email) && isValidPassword(password)) { print("Credentials are valid"); } else { print("Invalid credentials"); } }}在这个例子中,InputValidator mixin 被用于LoginForm和SignupForm类,用于复用电子邮件和密码的验证逻辑。这样做不仅减少了代码重复,而且使得维护变得更加容易,因为所有的输入验证逻辑都在一个地方维护。如果验证逻辑需要更新,我们只需修改InputValidator mixin。使用mixin,我们能够构建一个更加模块化、清晰且易于维护的Flutter应用。
前端阅读 402024年7月1日 11:29

Flutter 热重载和热重启有什么区别?

Flutter 中的热重载和热重启都是用来提高开发效率的工具,它们可以让开发者在不完全重启应用的情况下,快速查看代码更改的效果。不过,这两者在功能和使用场景上有所区别:热重载 (Hot Reload)热重载功能可以让你在进行小的更改后迅速看到结果,它主要用于UI层面的调整。当你对代码进行修改后,例如更改一个颜色、调整布局等,通过热重载,Flutter 可以在几秒钟内将更改应用到正在运行的应用程序上,而无需重新启动整个应用。这个过程不会影响应用状态,这意味着你的应用数据和状态会被保留。例子:假设你正在开发一个购物应用,你发现购物车界面的“结账”按钮颜色不符合设计规范。你可以直接在代码中更改颜色值,然后使用热重载,几秒钟内按钮的颜色就会更新,而且购物车中的商品列表和用户的选择状态不会受到影响。热重启 (Hot Restart)热重启则是一个更为全面的重载过程,它会重置应用的状态。这通常用于更加根本的代码更改,比如更改了应用的数据结构,或者进行了大范围的方法或类的重构。热重启会完全重新启动应用程序,但是比冷启动(完全停止再启动应用程序)要快得多。例子:继续上面购物应用的例子,如果你决定重构整个购物车的数据处理逻辑,例如从单例模式改为使用状态管理库来处理状态,这时候仅仅使用热重载可能无法正确显示更改的效果,因为根本的数据处理方式已经改变。此时,使用热重启能够重新构建整个应用的状态,确保所有的更改都能正确应用。总结简而言之,热重载适用于快速的视觉和小逻辑更改,保持应用状态不变,而热重启用于更深层次的代码更改,但会重置应用状态。两者都是Flutter提供的强大工具,极大地提高了开发效率和体验。
前端阅读 402024年7月1日 01:07

Flutter 如何实现可拖动的小部件?

在 Flutter 中实现可拖动的小部件,可以使用 Draggable 和 DragTarget 这两个 Widget。下面我将详细说明如何使用这些 Widgets 来创建一个基本的拖放功能。使用 Draggable Widget:Draggable widget 允许用户在屏幕上拖动它。你需要定义以下属性:data: 这是被拖动时传递给 DragTarget 的数据。child: 当不被拖动时显示的 Widget。feedback: 拖动时跟随手指移动的 Widget。childWhenDragging: 在原位置留下的 Widget,可以是透明的或任何占位符。示例代码: Draggable( data: '你拖动的数据', child: Container( width: 100.0, height: 100.0, color: Colors.blue, child: Center( child: Text('Drag me'), ), ), feedback: Container( width: 120.0, height: 120.0, color: Colors.lightBlue, child: Center( child: Text('Dragging'), ), ), childWhenDragging: Container( width: 100.0, height: 100.0, color: Colors.grey, child: Center( child: Text('Original Position'), ), ), );使用 DragTarget Widget:DragTarget widget 允许接收被拖动的 Widget。它需要指定一些回调函数来处理接受或拒绝数据:onWillAccept: 确定数据是否被接受的函数。onAccept: 当接受数据时调用的函数。builder: 构建显示的 Widget,可以根据是否有 Widget 正在悬停来改变显示。示例代码: DragTarget( onWillAccept: (data) { return true; // 根据数据决定是否接受拖动的 Widget }, onAccept: (data) { // 处理接受数据的逻辑 print('Data received: $data'); }, builder: ( BuildContext context, List<dynamic> accepted, List<dynamic> rejected, ) { return Container( width: 200.0, height: 200.0, color: accepted.isEmpty ? Colors.red : Colors.green, child: Center( child: Text('Drop here!'), ), ); }, );通过组合使用 Draggable 和 DragTarget,你就可以在 Flutter 应用中创建复杂的拖放交互。这种方法不仅适用于简单的数据传递,也可以用于更复杂的交互,如排序列表、移动卡片等。
前端阅读 2642024年6月24日 16:43

JavaScript有几种类型的值

JavaScript中有8种基本类型的值:Undefined:一个未给定值的变量的类型是undefined。例如: let x;Null:表示缺少或者空值的类型。例如:let x = null;Boolean:有两个boolean操作符:true 和 false。例如:let x = true;String:用于显示文本数据的类型。例如:let x = 'hello world';Number:用于表示整数和浮点数。例如:let x = 3.14;BigInt:一种用于存储和操作任意大小整数的类型。例如:let x = 9007199254740991n;Symbol:一种唯一并且不可变的数据类型。例如:let x = Symbol('hi');Object:对象数据类型用于存储更复杂的数据集。例如: let x = {firstName:"John", lastName:"Doe"};以上8种类型可大致分为两类:原始值(Undefined, Null, Boolean, Number, String, Symbol, BigInt对象值(Object)。
前端阅读 1032024年6月24日 16:43

什么是同源策略?什么是跨域问题?有什么手段可以解决跨域问题?

同源策略同源策略是一种对浏览器发出请求的安全策略。根据这种策略,一个Web页面只能从同一来源(协议,域名和端口都必须相同)获取数据。跨域问题.当一个Web页面尝试从不同的源访问资源时,就会出现跨域问题。例如,一个在 www.example1.com 上托管的脚本尝试访问 www.example2.com 上的资源,这就违反了同源策略,所以浏览器会阻止这个请求并产生跨域错误。解决跨域问题的手段以下是一些常见的解决跨域问题的方法:CORS(跨源资源共享):CORS是一种让服务器允许来自特定源访问资源的机制。服务器通过设置特定的HTTP头部告诉浏览器哪些Web页面可以访问这些资源。JSONP(JSON with Padding):JSONP是一种通常用于解决跨域数据获取问题的方式。不过,这种方式局限于GET请求,并且安全性较差。代理服务器:可以使用服务器端的代理转发请求,因为在服务器端不受同源策略限制。postMessage API:使用HTML5引入的 window.postMessage 方法,可以安全地实现跨源通信。WebSocket协议:WebSocket是一种建立在TCP协议之上的全双工通信协议,不受同源策略影响。WebSockets:WebSockets API 是另一种通信协议,它不受同源策略的影响,可以用于任何地方的通信。Document.domain+iframe:基于 document.domain 的跨域方法只适合主域相同的情况,也就是abc.example.com到www.example.com这样的跨域,区域子域和顶级字段相同。
前端阅读 482024年6月24日 16:43

列举 3 种强制类型转换和 2 种隐式类型转换

强制类型转换强制类型转换 是指开发者显式地将一种数据类型转换为另一种数据类型。强制类型转换的例子:Number转换为String:使用 toString()方法来转换数字为字符串。例如:(123).toString(),结果为 "123"。String转换为Number:使用 Number()函数将字符串转换为数字。例如:Number("123"),结果为 123。非布尔值转换为布尔值:使用 Boolean()函数将非布尔值转换为布尔值。例如:Boolean(1),结果为 true。 隐式类型转换隐式类型转换,又被称为隐式类型强制转换,是指JavaScript引擎在处理表达式时自动完成的类型转换。隐式类型转换的两个例子:加法运算符:当通过加法运算符加入字符串和非字符串(数字,布尔值等)时,非字符串将被转换为字符串。例如:"5" + 3,结果为 "53"。相等性比较:如果比较的值具有不同的类型,JavaScript会尝试通过诸如转换字符串为数字,或转换布尔值为数字等方式,来进行比较。例如:"5" == 5,结果为 true。
前端阅读 342024年6月24日 16:43

Web前端安全攻击手段有哪些?以及应该如何做相应的防御措施?

Web前端安全攻击主要有以下几种常见的方式:XSS攻击(跨站脚本攻击):攻击者通过在目标网站上注入恶意的HTML代码,当用户浏览该网站时就会运行这些恶意代码。防御措施:输入验证与过滤、输出编码、使用CSP(内容安全策略)防止不安全的动态脚本执行。CSRF攻击(跨站请求伪造):攻击者诱导用户点击链接,使用用户的登录凭证发送恶意请求。防御措施:使用CSRF token,验证每个请求。点击劫持:攻击者将透明的恶意网站覆盖在真实网站上,诱导用户在不知情的情况下进行恶意操作。防御措施:使用X-FRAME-OPTIONS来防止网页被iframe调用。DoS攻击(拒绝服务攻击):恶意请求过多使得服务无法处理正常的请求。防御措施:限制访问频率、使用CDN(内容分发网络)等方式分散流量。SQL注入攻击:攻击者通过输入特殊的SQL查询语句,来获取数据库的敏感信息。防御措施:使用预处理语句(Prepared Statements)或参数化的SQL命令,拒绝直接执行动态生成的SQL语句。上传恶意文件:攻击者通过上传恶意文件,例如包含病毒或者后门的文件,来破坏服务器或者网页。防御措施:限制可上传文件类型,扫描上传的文件以防止上传恶意软件,对上传文件名严格过滤等。
前端阅读 1312024年6月24日 16:43

为什么有移动端 1px 的问题以及有什么方案可以解决移动端 1px 的问题?

为什么存在 1px 问题移动端的1px问题主要是由于设备的物理像素和逻辑像素的不同所致。在高清显示屏(即设备独立像素比例devicePixelRatio大于1)的设备上,一个CSS像素可能会对应多个设备像素。这就导致了CSS的1px边框在高清屏上显示得比预期更粗。 1px以下是一些主要的解决1px问题的方案:视口缩放(Viewport Scale)视口缩放的原理是将页面的视口设置为设备宽度的一半或一部分,然后布局以这个新的视口宽度为基准进行。由于视口被缩小,一个CSS像素也就对应了更少的设备像素。 <meta name="viewport" content="width=device-width,initial-scale=.5,maximum-scale=.5,user-scalable=no">使用Media Query可以使用CSS的Media Query配合devicePixelRatio设定不同dpr下的边框样式,这样可以确保在不同dpr的设备上边框都看起来只有1px。 @media only screen and (-webkit-device-pixel-ratio: 2) { .border { border-width: 0.5px; } }使用伪元素 + transform: scaleY(.5)/scaleX(.5)利用伪元素,将元素的边框做一次.5的缩放,使得高清屏下1px边框变细。 .border:after { content: ''; position: absolute; bottom: 0; background: #000; width: 100%; height: 1px; transform: scaleY(.5); -webkit-transform: scaleY(.5); }使用SVG可以使用SVG矢量图形来实现1px边框。由于SVG是矢量图形,在任何分辨率的屏幕上都会很清晰。 <svg width="100%" height="1" xmlns="http://www.w3.org/2000/svg" version="1.1"> <line x1="0" y1="0" x2="100%" y2="0" stroke="black" stroke-width=".5"/> </svg>边框图片(Border Image)border-image 属性可以让你使用图像作为边框,你可以自由定义图像如何填充和拉伸。以上只是其中的一些常用解决方案,每种方法都有其适用范围和局限。在实际开发过程中,需要根据具体的应用情况选择最合适的方案。
前端阅读 1392024年6月24日 16:43

CSS 选择器有哪些

CSS 选择器用于决定某个HTML元素上应用哪些样式。以下是一些常用的CSS选择器:1. 元素选择器基于HTML元素的名称选择元素。例如,选择所有的 <p> 元素:p { color: red;}2. id 选择器使用HTML元素的 id 属性选择特定元素。id 选择器在CSS中使用 #" 符号定义。例如,选择 id="intro"的元素:#intro { color: blue;}3. 类选择器使用HTML元素的 class 属性选择特定元素。类选择器在CSS中使用 . 符号定义。例如,选择 class="highlight"的所有元素:.highlight { background-color: yellow;}4. 属性选择器可以选择带有指定属性的HTML元素。例如,选择所有带有 target 属性的 <a> 元素:a[target] { background-color: pink;}5. 伪类选择器用于选择HTML元素的特定状态。例如,选择鼠标悬停在上面的 <a> 元素:a:hover { color: green;}6. 伪元素选择器用于选择元素的某一部分。例如,选择每个 <p> 元素的第一行:p::first-line { color: orange;}7. 组合选择器有时候,我们需要选择满足多项条件的元素,这时候就可以使用组合选择器。常见的组合选择器如下:后代选择器(空格)div p { color: brown;}子代选择器(>)div > p { color: purple;}相邻兄弟选择器(+)div + p { color: gray;}一般兄弟选择器(~)div ~ p { color: teal;}
前端阅读 792024年6月24日 16:43

display: none;与visibility: hidden;的区别

联系:它们都能让元素不可见区别:display:none;会让元素完全从渲染树中消失,渲染的时候不占据任何空间;visibility: hidden;不会让元素从渲染树消失,渲染时元素继续占据空间,只是内容不可见。display: none;是非继承属性,子孙节点消失由于元素从渲染树消失造成,通过修改子孙节点属性无法显示;visibility: hidden;是继承属性,子孙节点由于继承了 hidden 而消失,通过设置 visibility: visible,可以让子孙节点显示。修改常规流中元素的 display 通常会造成文档重排。修改 visibility 属性只会造成本元素的重绘。读屏器不会读取 display: none;元素内容;会读取 visibility: hidden;元素内容。
前端阅读 822024年6月24日 16:43

link与@import的区别

link和 @import都是用于在HTML文档中链接和导入CSS样式的方法,但它们之间存在一些关键区别:表现形式link是HTML标签,这种方式通过HTML的 <link>元素来提供链接;@import是CSS提供的方式,可以在一个CSS样式表中导入其他样式表;加载方式link在页面一开始加载时就会同时加载CSS文件;@import则是在页面加载完之后再加载CSS,因此如果CSS文件较多、较大时,可能会导致页面加载不同步,影响网页性能;兼容性link是XHTML标签,无论是早期的HTML版本还是现在的XHTML,都支持 link标签;@import是在CSS2.1才出现的,所以早期的浏览器不支持 @import方式;使用条件link可以定义RSS、链接到打印版的CSS等,更具有扩展性;@import只能加载CSS以上就是 link和 @import的主要区别。
前端阅读 682024年6月24日 16:43

Form 表单可以执行跨域请求吗?

HTML表单(form)是可以执行跨域请求的。在Web开发中,跨域请求指的是从一个源(domain、协议、端口)发起的请求试图获取另一个源上的资源。通常,出于安全考虑,浏览器会实施同源策略(Same-Origin Policy),这意味着如果使用XMLHttpRequest或Fetch API来发起Ajax请求,那么请求通常会受到限制,除非目标服务器明确允许跨源资源共享(CORS,Cross-Origin Resource Sharing)。但是,HTML表单不受同源策略的限制,因此可以向任何URL发起POST或GET请求,即使这个URL指向的是另一个域。当表单提交时,浏览器会将用户输入的数据作为请求参数发送到表单的 action属性所指定的URL。不过,使用表单进行跨域提交时,浏览器会导航到响应页面,也就是说用户的当前页面会被新页面替换。下面是一个简单的表单跨域请求的例子:<form action="https://example.com/api/submit" method="POST"> <input type="text" name="username" value="User"> <input type="text" name="password" value="Pass"> <input type="submit" value="Submit"></form>在此示例中,表单数据将提交给位于 example.com域的服务器,即使表单所在的HTML页面可能托管在不同的域上。当用户点击提交按钮时,浏览器会将表单数据作为POST请求的一部分发送到 example.com。需要注意的是,即使表单可以跨域提交,服务端仍然需要处理来自不同源的请求。此外,跨域表单提交不会提供Ajax那样的客户端JavaScript接口来访问响应内容,除非服务器在响应中包含适当的CORS头部信息。
前端阅读 422024年6月24日 16:43

什么是 web 语义化,有什么好处

什么是 Web 语义化Web 语义化是指通过 HTML 标记表示页面包含的信息,包含了 HTML 标签的语义化和 css 命名的语义化。HTML 标签的语义化通过使用包含语义的标签(如 h1-h6)恰当地表示文档结构 。CSS 命名的语义化 为 html 标签添加有意义的 class,id 补充未表达的语义,如通过添加符合规则的 class 描述信息。 Web 语义化的好处需要语义化的主要理由有以下几点:去掉样式后页面呈现清晰的结构盲人使用读屏器更好地阅读搜索引擎更好地理解页面,有利于收录便团队项目的可持续运作及维护
前端阅读 292024年6月24日 16:43

JavaScript 执行过程分为哪些阶段?

JavaScript 的执行过程大致可以分为以下几个阶段:1. 解析(Parsing)在这一阶段,JavaScript 引擎会读取源代码,并将其解析成抽象语法树(AST)。抽象语法树是一种深层次的、结构化的代码表达方式,能够以树形结构表现代码中的每个语句、表达式等元素。解析过程中,如果遇到语法错误,会抛出错误,停止进一步执行。2. 编译(Compilation)JavaScript 引擎(如V8)通常会将解析后的代码进行即时编译(JIT)。编译器会先生成字节码,这是一种低级的、比源代码更接近机器语言的代码。随后根据程序的执行情况,编译器可能会把热点代码(经常执行的代码)编译成优化的机器码,提高执行效率。3. 执行(Execution)编译后得到的字节码或机器码被送到 JavaScript 引擎的执行环境中执行。在执行过程中,会进入下面的子阶段:创建执行上下文(Execution Context):首先会创建全局执行上下文,随后每当调用一个函数时,就会为该函数创建一个新的执行上下文。执行上下文包括变量对象、作用域链和 this 引用等信息。变量提升(Hoisting):在执行代码前,函数声明和变量(声明)会被提升到它们各自的执行上下文的顶部。变量会初始化为 undefined,而函数则会完整地提升。执行代码(Running Code):按照执行上下文中的代码逐行执行,进行变量赋值、函数调用等。垃圾回收(Garbage Collection):在执行过程中,引擎会进行内存管理,自动释放那些不再被需要的内存空间。4. 优化(Optimization)在代码执行的过程中,某些代码可能会被执行多次,引擎会尝试对这些频繁执行的代码进行优化。例如,在V8引擎中,有一个称为“TurboFan”的优化编译器,它可以根据代码执行的特点对代码进行优化,提高性能。如果优化假设失败了(即出现了“去优化” deoptimization),引擎还可以将代码回退到一个较少优化的版本。5. 回收(Deoptimization & Garbage Collection)对于那些不再需要的数据和优化,JavaScript 引擎会进行去优化和垃圾回收,以保证内存的高效使用。例子:假设我们有这样一个简单的 JavaScript 函数:function sum(a, b) { return a + b;}let result = sum(5, 3);首先,该函数会被解析成 AST。然后,它可能会被编译成字节码,当我们调用 sum(5, 3) 时,会创建一个新的执行上下文,包含 a 和 b 的参数以及任何局部变量。在这个上下文中,a 和 b 被赋予了值 5 和 3,函数执行,并返回结果 8。这个过程中可能还包括了对 sum 函数的优化,如果函数被频繁调用。最后,当执行上下文离开作用域,如果没有其他引用指向其中的数据,垃圾回收器最终会清理掉这些对象。
前端阅读 432024年6月24日 16:43

JavaScript 为什么要区分微任务和宏任务?

JavaScript 区分微任务(microtasks)和宏任务(macrotasks)主要是为了有效地管理异步操作的执行时机和顺序。这两种类型的任务允许 JavaScript 引擎维持一个控制异步操作何时何地执行的精细化调度机制。宏任务 (Macrotasks)宏任务通常是浏览器的主要任务,包括但不限于:setTimeoutsetIntervalI/O 操作UI 渲染事件处理(如点击、滚动事件)每次执行栈为空时,事件循环都会从任务队列中取出一个宏任务执行。微任务 (Microtasks)微任务通常是需要快速响应的任务,执行时机在每个宏任务之后,以及JavaScript执行环境准备好以后。微任务包括:Promise 回调(例如.then、.catch和.finally)MutationObserver 的回调queueMicrotask函数执行顺序在每个宏任务执行完毕后,在执行下一个宏任务之前,JavaScript 引擎会处理所有队列中的微任务。这意味着微任务总是在当前宏任务结束和下一个宏任务开始之间执行,并在新的UI渲染前完成。这样可以确保异步操作的快速响应,同时因为微任务的延迟更小,所以适合高优先级的任务。为什么区分区分微任务和宏任务的原因包括:性能优化: 通过微任务,JavaScript 可以在不影响用户界面渲染的情况下,快速执行简单的操作,如承诺的解决。这提高了应用程序的响应速度和性能。控制异步操作顺序: 微任务和宏任务的区分允许开发者控制异步操作的执行顺序。例如,一个由Promise产生的微任务可以确保其在下次UI渲染之前解决,而setTimeout可能会推迟到下一个宏任务。避免阻塞: 对于需要快速执行的代码,使用微任务可以避免阻塞宏任务队列,这有助于避免长时间运行的任务阻塞UI更新。示例假设我们有以下代码:console.log('宏任务开始');setTimeout(() => { console.log('宏任务');}, 0);Promise.resolve().then(() => { console.log('微任务');});console.log('宏任务结束');执行顺序会是这样的:打印"宏任务开始"宏任务结束时,设置了一个setTimeout打印"宏任务结束"当前宏任务已结束,开始执行微任务队列中所有任务打印"微任务"微任务队列为空,开始下一个宏任务打印"宏任务"通过这个例子,我们可以看到微任务总是在当前执行栈清空后立即执行,而宏任务的执行则可能会因为任务队列中的其他宏任务而延迟。这种机制允许JavaScript有效地处理异步事件,同时保持对执行顺序的细粒度控制。
前端阅读 262024年6月24日 16:43

jsonp 为什么不支持 post 方法?

JSONP(JSON with Padding)是一种利用<script>标签不受同源策略限制的特性来实现跨源请求的技术。因为<script>标签的初衷是加载静态的JavaScript文件,所以<script>标签仅支持GET方法来请求资源,它并不支持POST方法。这就是为什么JSONP不支持POST方法的根本原因。当使用JSONP进行通信时,您会将请求参数包含在URL中,并通过动态创建<script>标签的方式将其发出。服务器接收到GET请求后,将数据包裹在一个函数调用中,并将其作为响应返回。客户端定义好回调函数后,这段包裹着JSON数据的JavaScript被执行,回调函数便会被调用并处理返回的数据。例如,假设您的页面需要从http://example.com获取一些用户数据,您可能会发送如下的JSONP请求:<script type="text/javascript"> // 定义回调函数 function handleResponse(data) { console.log('Received data:', data); }</script><script type="text/javascript" src="http://example.com/data?callback=handleResponse"></script>服务器端需要接收到callback参数后,把数据包装在该函数调用中:// 服务器端响应handleResponse({ "user": "Alice", "age": 25 });如上所述,JSONP请求的本质是一个GET请求,它是通过<script>标签的src属性来发起的。因此,它不能使用POST方法,后者通常用于传输大量数据或者发送需要安全传输的数据。如果您需要进行跨域的POST请求,可以考虑使用更现代的技术,如CORS(跨源资源共享),它允许在各种HTTP方法中使用跨源请求,同时提供了更好的安全性。
前端阅读 352024年6月24日 16:43

Ajax 如何处理请求跨域问题?CORS 如何设置?

Ajax 处理请求跨域问题Ajax(Asynchronous JavaScript and XML)本身是不支持跨源请求的,这是因为浏览器出于安全考虑实施的同源策略(Same-Origin Policy)。同源策略限制了来自不同源的文档或脚本对当前文档读取或设置某些属性的能力。不过,有几种方法可以绕过这个限制来实现跨源请求:JSONP(JSON with Padding):这是一种老的技巧,它利用 <script>标签不受同源策略限制的特点来进行跨域请求。服务器返回的响应拼接一个函数调用作为JavaScript代码。这种方法的缺点是它只能用于GET请求,并且存在一定的安全隐患。CORS(Cross-Origin Resource Sharing):这是现在推荐的方法,它允许服务器显式地指定哪些源可以访问该服务器上的资源。CORS是一个 W3C 标准,它通过在服务器上设置特定的HTTP头来工作。代理服务器:使用一个服务器充当中间人的角色,接受来自客户端的跨源请求,然后转发请求到目标服务器。代理服务器接收到响应后,再将数据返回给客户端。这种方法需要额外的服务器端配置。WebSockets: WebSockets提供了一个全双工的通信渠道,可以在浏览器和服务器之间建立持久连接。虽然WebSocket协议与HTTP不同,但它可以绕过同源策略,从而实现跨域通信。CORS 设置当你控制服务器时,可以通过设置HTTP响应头来启用CORS。这些头部主要包括:Access-Control-Allow-Origin: 指定了哪些网站可以参与跨域资源共享。例如,设置为 *代表允许所有域进行跨域请求,但出于安全考虑通常会指定明确的域名。Access-Control-Allow-Methods: 指定了允许的HTTP请求方法,如 GET, POST, PUT, DELETE, OPTIONS等。Access-Control-Allow-Headers: 在实际请求中允许自定义的HTTP头部字段列表。Access-Control-Allow-Credentials: 表示是否允许发送Cookie。如果服务器端设置了这个值为 true,那么前端请求的时候也必须将 withCredentials属性设置为 true。Access-Control-Expose-Headers: 允许客户端访问的服务器白名单头部字段。Access-Control-Max-Age: 表明在多少秒内,不需要再发送预检验请求(对于某一资源的预检请求结果的有效期)。下面是一个简单的例子,展示了如何在Node.js的Express框架中设置CORS响应头:const express = require('express');const app = express();app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', 'https://example.com'); res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization'); res.header('Access-Control-Allow-Credentials', true); if (req.method === 'OPTIONS') { res.header('Access-Control-Max-Age', '86400'); return res.status(200).send(); } next();});// 你的其他路由和逻辑app.listen(3000, () => { console.log('Server running on port 3000');});
前端阅读 752024年6月24日 16:43

base64 为什么能提升性能?

Base64编码通常不是直接用来提升性能的,而是用来确保二进制数据可以通过仅支持文本格式的传输层安全地传输。Base64将二进制数据编码为ASCII字符串,这使得它可以在不支持二进制数据的系统(如电子邮件)中传输。虽然Base64编码的数据比原始二进制数据大约增加了33%,但在某些情况下,它可以间接地提升性能:减少HTTP请求:在Web开发中,Base64编码通常用于将小的图片或其他文件直接嵌入到HTML或CSS中。这样做的好处是可以减少浏览器发起的HTTP请求的数量,因为所有的资源都包含在了主文档中。少了额外的请求,网页加载时间就会缩短,间接提高了用户体验和性能。举个例子,如果一个网页中有多个小图标,通常的做法可能是每个图标一个HTTP请求来获取图像文件。如果将这些图标的图片用Base64编码后嵌入到CSS中,就可以将多个HTTP请求合并为一个请求,从而减少了服务器的请求负载和网络延迟,提高了页面的加载速度。数据URI方案:Base64编码可以使用数据URI方案在Web页面中直接嵌入图像数据,这样可以避免服务器配置对小文件的较慢响应时间。服务器对小文件的处理往往不如大文件高效,因为涉及到磁盘I/O等开销。通过避免这些小文件请求,可以在服务器端节省资源,从而提升性能。安全和兼容性:有些系统不支持二进制数据的传输,或者在传输过程中可能会因为某些字符(如NUL byte)的存在而出现问题。在这种情况下,Base64编码提供了一种可靠的方法来处理和传输数据,避免了潜在的数据损坏和传输错误,从而确保系统的顺畅运行和性能。总之,Base64编码本身增加了数据量,理论上会降低传输效率,但通过减少HTTP请求的数量、充分利用缓存、避免小文件请求开销以及提高数据的安全性和兼容性,它可以在特定场景下间接提升系统的整体性能。