京东-物流-秋招 · 第 2 轮 · 第二轮

← 第 1 轮 · 返回本次面经 · 已是最后一轮 → 面试时间: 60分钟 本轮共 16 道题。答案默认折叠,便于先自行作答。 1. 前端和后端有什么区别? 题目要点 职责分工不同,前端关注用户体验,后端关注业务逻辑,但需要密切协作完成产品开发。 参考答案 前端和后端在职责、技术栈、关注点等方面都有明显区别。前端主要负责用户界面和交互体验,使用HTML、CSS、JavaScript等技术,关注的是如何让用户更好地使用产品。后端则负责业务逻辑处理、数据存储、系统架构等,使用Java、Python、Node.js等语言,关注的是系统的稳定性、安全性和性能。 工作内容上,前端需要将设计稿转化为可交互的页面,处理用户输入、数据展示、页面跳转等;后端需要设计数据库结构、编写API接口、处理业务逻辑、确保数据安全等。思维方式也不同,前端更多从用户角度思考问题,注重体验和视觉效果;后端更多从系统角度思考,注重逻辑严谨性和数据一致性。 但两者并不是完全独立的,现代Web开发中前后端需要密切协作,通过API接口进行数据交互,共同完成产品功能。随着技术发展,界限也在模糊,比如Node.js让前端开发者也能做后端开发。 2. 如果现在有一个需要前后端协作的功能开发需求,你认为前端和后端在沟通和配合上最重要的是什么? 题目要点 API接口设计一致性、数据格式规范、清晰的沟通方式、完善的联调流程是协作的关键。 参考答案 最重要的是在项目开始前就API接口的设计达成一致。这包括接口的URL设计、请求方法、参数格式、返回数据结构、错误处理方式等。清晰的接口文档能够避免后续很多沟通成本和返工问题。 其次是建立统一的数据格式规范。比如时间格式、状态码定义、分页参数等,这些看似细节的问题如果不提前约定,会在开发过程中产生很多摩擦。还要约定好错误处理机制,什么情况下返回什么状态码,错误信息的格式等。 沟通方式也很重要。建议使用统一的协作工具,如Postman分享API文档,或者使用Swagger等工具生成接口文档。定期的技术评审会议也必不可少,确保双方对需求理解一致。 最后是要建立联调测试的流程。前端可以先用Mock数据开发,后端接口完成后再进行联调,这样能够并行开发,提高效率。 3. 项目是怎么接到的? 题目要点 通过课程项目、实验室项目、实习项目和个人项目等多种渠道积累开发经验。 参考答案 主要通过几个渠道获得项目经验。首先是课程项目,在Web开发、软件工程等课程中,老师会布置一些实际的开发任务,比如开发一个在线学习平台、图书管理系统等。这些项目虽然规模不大,但涵盖了完整的开发流程。 实验室项目是另一个重要来源。导师的研究项目中经常需要开发一些数据展示、实验管理的Web系统,会让研究生参与开发。这类项目更贴近实际应用,需求也更复杂一些。 还有就是实习期间参与的公司项目。虽然作为实习生主要是辅助开发,但能够接触到真实的商业项目,了解完整的产品开发流程。 另外也会主动寻找一些开源项目参与贡献,或者自己发起一些小项目来练习新技术。比如看到一个有趣的API,就会想着用它开发一个小应用来练手。 4. 讲一个你觉得最有成就感的项目 题目要点 科研数据可视化平台,解决了性能优化和数据适配的技术挑战,最终被多个实验室采用。 参考答案 最有成就感的是在实验室开发的一个科研数据可视化平台。这个项目是为了帮助实验室的研究人员更好地分析和展示实验数据。 项目的挑战在于需要处理大量的科研数据,并且要支持多种图表类型的动态切换。数据量大的时候页面会卡顿,用户体验很差。通过学习和实践,使用了虚拟滚动技术处理大列表,用Web Workers在后台处理数据计算,用Canvas替代DOM渲染复杂图表,最终解决了性能问题。 另一个难点是不同研究方向的数据格式差异很大,需要设计一个灵活的数据适配层。通过抽象出通用的数据模型,并提供配置化的字段映射功能,让系统能够适应不同的数据格式。 最终这个平台不仅在我们实验室使用,还被其他几个实验室采用,帮助研究人员提高了数据分析效率。看到自己开发的系统真正解决了实际问题,并且得到用户认可,这种成就感是最大的收获。 5. 前端怎么样才是一种好的体验? 题目要点 性能优化、交互反馈改进、错误处理优化、移动端适配、基于反馈的持续改进。 参考答案 好的前端体验应该是用户感知不到技术存在的体验。首先是性能方面,页面加载要快,交互要流畅,用户不应该感受到明显的等待时间。这需要做好资源优化、懒加载、缓存策略等。 界面设计要直观易懂,用户能够快速找到需要的功能,操作流程要符合用户习惯。响应式设计也很重要,在不同设备上都能提供良好的体验。 交互反馈要及时准确。用户的每个操作都应该有明确的反馈,比如按钮点击效果、加载状态提示、操作结果通知等。错误处理要友好,当出现问题时,要给用户清晰的错误信息和解决建议。 可访问性也是好体验的重要组成部分。要考虑到不同能力用户的需求,比如键盘导航、屏幕阅读器支持等。 最后是要保持一致性,整个应用的交互模式、视觉风格、信息架构都要保持统一,让用户形成稳定的使用预期。 6. 平时怎么学习前端的? 题目要点 系统性学习、实践项目、阅读源码、关注社区、记录总结、保持好奇心。 参考答案 学习前端主要通过几个渠道。首先是系统性学习,通过在线课程平台如慕课网、极客时间等学习完整的技术体系。看技术书籍也很重要,比如《JavaScript高级程序设计》、《CSS权威指南》等经典书籍能够建立扎实的基础。 实践是最重要的学习方式。会跟着教程做一些小项目,然后尝试改进和扩展功能。GitHub上有很多优秀的开源项目,通过阅读源码能够学到很多最佳实践。 技术社区是获取最新信息的重要渠道。经常浏览掘金、思否、MDN等网站,关注前端技术的发展趋势。参加一些技术分享会和线上讲座,能够了解行业动态和实际应用经验。 建立了自己的学习笔记系统,将学到的知识点整理成文档,定期回顾和总结。还会写一些技术博客,通过输出来加深理解。 保持好奇心很重要,看到新技术会主动去了解和尝试。比如最近学习了WebAssembly、微前端等新技术,虽然可能暂时用不到,但能够拓宽技术视野。 7. 最近了解到的新技术讲一下 题目要点 WebAssembly提供高性能计算能力,微前端解决大型应用架构问题,新CSS特性增强布局能力。 ...

July 27, 2025

V8 引擎的优化机制

V8 是 Google 开发的 JavaScript 引擎(用于 Chrome、Node.js 等),它的核心优化机制可以总结为:即时编译(JIT) + 隐式类型优化 + 内联缓存 + 垃圾回收机制,通过这些方式来大幅提升 JS 的执行性能。 一、V8 的执行流程 V8 不直接解释执行 JavaScript,而是: 解析(Parser):将 JS 源码解析为 AST(抽象语法树) 生成字节码(Bytecode):由 Ignition 引擎生成并执行初始字节码 收集类型信息(Profiler):运行时收集变量类型、函数调用等信息 优化编译(JIT):将热点代码交给 TurboFan 引擎编译为高效的机器码 二、核心优化机制 1. 隐藏类(Hidden Class)与内联缓存(Inline Cache) 为了优化对象属性访问,V8 引入了隐藏类机制,类似 C++ 中的类结构: 初次创建对象时,生成一个隐藏类(HiddenClass) 添加属性时会生成新的隐藏类链表结构 相同结构的对象共享隐藏类,从而使访问更快 不要动态给对象添加属性,尽量保持对象结构稳定。 内联缓存:缓存某个属性的访问位置,使得后续同样结构的对象访问不必重新查找。 function Person(name) { this.name = name; } const p1 = new Person("Tom"); const p2 = new Person("Jerry"); // 内联缓存会优化 this.name 的访问路径 2. 类型反馈与优化编译(Type Feedback + JIT) V8 在执行字节码时会收集类型信息(比如函数参数、返回值的类型),并标记频繁调用的代码为“热点代码”: 热点代码会被交给 TurboFan 做优化编译 编译时使用收集到的类型信息来生成专用的机器码(类型特化) 例如: function add(a, b) { return a + b; } add(1, 2); // 优化为整数加法 add("a", "b"); // 类型变化 -> 去优化 不要在同一个函数中混用不同类型,可能导致优化失败或退化。 ...

December 17, 2024

元素、组件、实例和节点

React 中的元素、组件、实例和节点,是React中关系密切的4个概念,也是很容易让React 初学者迷惑的4个概念。现在,老干部就来详细地介绍这4个概念,以及它们之间的联系和区别,满足喜欢咬文嚼字、刨根问底的同学(老干部就是其中一员)的好奇心。 元素 (Element) React 元素其实就是一个简单JavaScript对象,一个React 元素和界面上的一部分DOM对应,描述了这部分DOM的结构及渲染效果。一般我们通过JSX语法创建React 元素,例如: const element = <h1 className='greeting'>Hello, world</h1>; element是一个React 元素。在编译环节,JSX 语法会被编译成对React.createElement()的调用,从这个函数名上也可以看出,JSX语法返回的是一个React 元素。上面的例子编译后的结果为: const element = React.createElement( 'h1', {className: 'greeting'}, 'Hello, world!' ); 最终,element的值是类似下面的一个简单JavaScript对象: const element = { type: 'h1', props: { className: 'greeting', children: 'Hello, world' } } React 元素可以分为两类:DOM类型的元素和组件类型的元素。DOM类型的元素使用像h1、div、p等DOM节点创建React 元素,前面的例子就是一个DOM类型的元素;组件类型的元素使用React 组件创建React 元素,例如: const buttonElement = <Button color='red'>OK</Button>; buttonElement就是一个组件类型的元素,它的值是: const buttonElement = { type: 'Button', props: { color: 'red', children: 'OK' } } 对于DOM类型的元素,因为和页面的DOM节点直接对应,所以React知道如何进行渲染。但是对于组件类型的元素,如buttonElement,React是无法直接知道应该把buttonElement渲染成哪种结构的页面DOM,这时就需要组件自身提供React能够识别的DOM节点信息,具体实现方式在介绍组件时会详细介绍。 有了React 元素,我们应该如何使用它呢?其实,绝大多数情况下,我们都不会直接使用React 元素,React 内部会自动根据React 元素,渲染出最终的页面DOM。更确切地说,React元素描述的是React虚拟DOM的结构,React会根据虚拟DOM渲染出页面的真实DOM。 组件 (Component) React 组件,应该是大家最熟悉的React中的概念。React通过组件的思想,将界面拆分成一个个可以复用的模块,每一个模块就是一个React 组件。一个React 应用由若干组件组合而成,一个复杂组件也可以由若干简单组件组合而成。 React组件和React元素关系密切,React组件最核心的作用是返回React元素。这里你也许会有疑问:React元素不应该是由React.createElement() 返回的吗?但React.createElement()的调用本身也是需要有“人”负责的,React组件正是这个“责任人”。React组件负责调用React.createElement(),返回React元素,供React内部将其渲染成最终的页面DOM。 既然组件的核心作用是返回React元素,那么最简单的组件就是一个返回React元素的函数: function Welcome(props) { return <h1>Hello, {props.name}</h1>; } Welcome是一个用函数定义的组件。如果使用类(class)定义组件,返回React元素的工作具体就由组件的render方法承担,例如: ...

December 17, 2024

浏览器的缓存机制

一、前言 缓存可以说是性能优化中简单高效的一种优化方式了。一个优秀的缓存策略可以缩短网页请求资源的距离,减少延迟,并且由于缓存文件可以重复利用,还可以减少带宽,降低网络负荷。 对于一个数据请求来说,可以分为发起网络请求、后端处理、浏览器响应三个步骤。浏览器缓存可以帮助我们在第一和第三步骤中优化性能。比如说直接使用缓存而不发起请求,或者发起了请求但后端存储的数据和前端一致,那么就没有必要再将数据回传回来,这样就减少了响应数据。 接下来的内容中我们将通过缓存位置、缓存策略以及实际场景应用缓存策略来探讨浏览器缓存机制。 如需获取思维导图或想阅读更多优质文章请猛戳GitHub博客 二、缓存位置 从缓存位置上来说分为四种,并且各自有优先级,当依次查找缓存且都没有命中的时候,才会去请求网络。 Service Worker Memory Cache Disk Cache Push Cache 1.Service Worker Service Worker 是运行在浏览器背后的独立线程,一般可以用来实现缓存功能。使用 Service Worker的话,传输协议必须为 HTTPS。因为 Service Worker 中涉及到请求拦截,所以必须使用 HTTPS 协议来保障安全。Service Worker 的缓存与浏览器其他内建的缓存机制不同,它可以让我们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存,并且缓存是持续性的。 Service Worker 实现缓存功能一般分为三个步骤:首先需要先注册 Service Worker,然后监听到 install 事件以后就可以缓存需要的文件,那么在下次用户访问的时候就可以通过拦截请求的方式查询是否存在缓存,存在缓存的话就可以直接读取缓存文件,否则就去请求数据。 当 Service Worker 没有命中缓存的时候,我们需要去调用 fetch 函数获取数据。也就是说,如果我们没有在 Service Worker 命中缓存的话,会根据缓存查找优先级去查找数据。但是不管我们是从 Memory Cache 中还是从网络请求中获取的数据,浏览器都会显示我们是从 Service Worker 中获取的内容。 2.Memory Cache Memory Cache 也就是内存中的缓存,主要包含的是当前中页面中已经抓取到的资源,例如页面上已经下载的样式、脚本、图片等。读取内存中的数据肯定比磁盘快,内存缓存虽然读取高效,可是缓存持续性很短,会随着进程的释放而释放。 一旦我们关闭 Tab 页面,内存中的缓存也就被释放了。 那么既然内存缓存这么高效,我们是不是能让数据都存放在内存中呢? 这是不可能的。计算机中的内存一定比硬盘容量小得多,操作系统需要精打细算内存的使用,所以能让我们使用的内存必然不多。 当我们访问过页面以后,再次刷新页面,可以发现很多数据都来自于内存缓存 内存缓存中有一块重要的缓存资源是preloader相关指令(例如<link rel="prefetch">)下载的资源。总所周知preloader的相关指令已经是页面优化的常见手段之一,它可以一边解析js/css文件,一边网络请求下一个资源。 需要注意的事情是,内存缓存在缓存资源时并不关心返回资源的HTTP缓存头Cache-Control是什么值,同时资源的匹配也并非仅仅是对URL做匹配,还可能会对Content-Type,CORS等其他特征做校验。 3.Disk Cache Disk Cache 也就是存储在硬盘中的缓存,读取速度慢点,但是什么都能存储到磁盘中,比之 Memory Cache 胜在容量和存储时效性上。 ...

December 17, 2024

类型判断

JS中的类型判断也是咱的老朋友了,如果要你说两句,那便是啥玩意typeof、instanceof的,张口就来,但你真的掌握了每一种类型判断,并熟知其原理吗?就比如说Object.prototype.toString.call(),当面试官问其中的 call有什么作用时,咱们又该如何游说呢。 typeof原理 所有的类型判断里当属typeof最为经典,其最为出名之处就是这玩意不顶用,typeof居然会将null判定为object,但这到底是什么原因?实际上,这就是JS这门编程语言的官方团队造出来的一个bug。 let s = '123' let n = 123 let f = true let u = undefined let nu = null let sy = Symbol(123) let big = 1234n let obj = {} let arr = [] let fn = function(){} let date = new Date(); console.log(typeof(s)); // string console.log(typeof(n)); // number console.log(typeof(f)) // boolean console.log(typeof(u)) // undifined console.log(typeof(sy)) // symbol console.log(typeof(big)) // bigint console.log(typeof(nu)); //将null判断为object //以下为引用类型,除了function,一律被判断为object console.log(typeof(obj)) //object console.log(typeof(arr)) //object console.log(typeof(date)) //object // 所有引用类型只能判断function console.log(typeof(fn)) //function // 原因就是:typeof会把所有传进去的值都转成二进制,而当年JS制定的规则便是:原始类型的被转为二进制的前面三个值绝对不为零,而typeof会把前三位为零的类型全部认定为对象,null这个类型又是JS语言后来引入的。遵循其他语言的原则,JS语言将null的二进制值定为一长串的0,因此typeof在判断时会将其认定为object。 因此,面试官一般不会询问判断方法 typeof 的原理。 手写一个instanceof 相较于typeof,instanceof有其优越之处,也有其不足之处,比如,instanceof只能判断引用类型,并且是通过原型链查找来判断类型。 let s = '123' let n = 123 let f = true let u = undefined let nu = null let sy = Symbol(123) let big = 1234n let obj = {} let arr = [] let fn = function(){} let date = new Date(); console.log(s instanceof String); // false instanceof 不能判断原始类型 // 以下为引用类型 console.log(obj instanceof Object) ; // true console.log(arr instanceof Object); // true console.log(fn instanceof Function); // true console.log(date instanceof Date); // true 首先要知道每个类型的原型都不相同。 ...

November 4, 2024

伪类和伪元素

什么是伪类和伪元素? 伪类:以冒号(:)开头,用于选择处于特定状态的元素。 伪元素:以双冒号(::)开头,用于在文档中插入虚构的元素。 这么说有点抽象,我们来看具体案例。例如下面的伪类语法表达的意思是:文档里的那些已经被用户访问过的 a 标签,当鼠标悬浮在它上面的时候,颜色为红色。 a:visited:hover { color: red; } 效果如下: 可以看到,无论是 visited还是 hover,都表示了 a 标签的某种状态,选择器始终选择的是伪类前面的元素,只不过当出现伪类描述的状态时,样式才生效而已。 注意,上面使用的是无痕浏览器打开的页面,是不可能出现 visited 状态的,因为无痕不记录访问历史,我们可以点击控制台的 :hov 图标手动勾选 :visited 和 :hover 来模拟 a 标签「已访问」和「悬浮」两种状态。 我们再来看一下伪元素的案例,下面的语法表达的意思是: div 标签里面首字的颜色为红色。 div::first-letter { color: red; } 效果如下: 与伪类不同的是,首字并不是 div 元素的某种状态,而是浏览器创建出来的一个虚拟的元素,我们可以在上图的右下角看到 Pseudo ::first-letter element提示,相当于浏览器自动选中了「彻」字。 注意:::first-letter 伪元素只作用于块状元素上面,如果把 div 换成 span,就无法选中首字了,除非手动设置 span 的 display 属性为 block 或 inline-block 等块状值。 从上面的案例当中,我们可以得出两个结论: 伪类用于向某些已经存在的选择器添加特殊效果(当状态改变时) 伪元素用于将特殊效果添加到不存在的虚拟元素中(浏览器自动创建) 也就是说伪类的本质还是类(class),作用于标签本身,只不过限定了状态条件;而伪元素的本质是元素(element),作用于该虚拟元素的内容本身。 伪类有哪些? 按照功能,可划分为以下几类: 动态伪类::visited、:focus、:hover等 状态伪类::disabled、:empty、:required 等 结构伪类::first-child、:nth-of-type等 其他伪类::target、:lang、:not()等 下面的表格详细记录了各种伪类及其描述: 伪类 描述 兼容性 :active 元素处于活动状态时 ✅ :focus 元素已获取焦点时 ✅ :hover 元素处于悬浮状态时 ✅ :link 链接未访问时 ✅ :visited 链接已访问时 ✅ :first-child 元素是首个子元素时 ✅ :last-child 元素是最后一个子元素时 ✅ :nth-child() 元素是第 n 个子元素时 ✅ :nth-last-child() 元素是倒数第 n 个子元素时 ✅ :only-child 元素是唯一子元素时 ✅ :first-of-type 元素是首个特定类型的子元素时 ✅ :last-of-type 元素是最后一个特定类型的子元素时 ✅ :nth-of-type() 元素是第 n 个特定类型的子元素时 ✅ :nth-last-of-type() 元素是倒数第 n 个特定类型的子元素时 ✅ :only-of-type 元素是唯一的特定类型的子元素时 ✅ :not 不满足指定条件时 ✅ :target 元素 id 匹配到哈希值时 ✅ :root 元素是文档树的根元素时 ✅ :lang() 匹配到指定语言时 ✅ :empty 元素处于没有子元素状态时 ✅ :invalid 和 :valid 表单项是否有效 ✅ :required 和 :optional 表单项是否必填 ✅ :in-range和 :out-of-range 表单项是否超出范围 ✅ :read-only和 :read-write 表单项是否只读 ✅ :enabled和 :disabled 表单项是否禁用 ✅ :fullscreen 当前处于全屏显示模式时 ⚠️ :blank 输入框处于输入为空状态时 ❌ :dir() 匹配到特定文字书写方向时 ❌ 上表中的兼容性表示: ...

October 8, 2024

Webpack与Vite

前言 vite比webpack快? vite比webpack简单? 今天我们就来分析一下,到底有什么不一样的地方! 定位 对比之前,我们先要搞懂,vite与webpack的定位以及关系才可以。 那前端社区中常谈到的这些工具webpack、rollup、parcel、esbuild、vite、vue-cli、create-react-app、umi他们之间的关系是怎样的。 webpack、rollup、parcel、esbuild都是打包工具,代码写好之后,我们需要对代码进行压缩、合并、转换、分割、打包等操作,这些工作需要打包工具去完成。 vue-cli、create-react-app、umi 是基于webpack的上层封装,通过简单的配置就可以快速创建出一个项目,把更多的时间放在业务开发上。 vite开发环境依赖esbuild进行预构建,生产环境则依赖rollup进行打包,并且充分利用了现代浏览器的特性,比如http2、ES module,vite是站在众多巨人肩膀上的一个产物, 类似webpack + webpack-dev-server的结合体,是一个非常棒的前端项目的构建工具。 运行原理 首先,我们从运行原理上分析一下,vite为什么比webpack快。 webpack运行原理 当我们使用webpack启动项目时,webpack会根据我们配置文件(webpack.config.js) 中的入口文件(entry),分析出项目项目所有依赖关系,然后打包成一个文件(bundle.js),交给浏览器去加载渲染。 这样就会带来一个问题,项目越大,需要打包的东西越多,启动时间越长。 关于ES module 在讲vite运行原理之前,我们先说一下ES module 目前,绝大多数现代浏览器都已经支持ES module了, 我们只需要在<script>标签中添加type="module",就可以使用ES module了。 下面这段代码是可以直接在浏览器中运行的。 // test.js export default function hello() { console.log('hello world'); } // index.html <script type="module"> import hello from './test.js'; hello(); // hello world </scirpt> vite运行原理 在<script type="module">中,浏览器遇到内部的import引用时,会自动发起http请求,去加载对应的模块。 vite也正是利用了ES module这个特性,使用vite运行项目时,首先会用esbuild进行预构建,将所有模块转换为es module,不需要对我们整个项目进行编译打包,而是在浏览器需要加载某个模块时,拦截浏览器发出的请求,根据请求进行按需编译,然后返回给浏览器。 这样一来,首次启动项目(冷启动)时,自然也就比webpack快很多了,并且项目大小对vite启动速度的影响也很小。 构建方式 我们再来看一下,vite与webpack在项目构建上有哪些区别。 webpack webpack是基于nodejs运行的,但js只能单线程运行,无法利用多核CPU的优势,当项目越来越大时,构建速度也就越来越慢了。 vite vite预构建与按需编译的过程,都是使用esbuild完成的。 esbuild是用go语言编写的,可以充分利用多核CPU的优势,所以vite开发环境下的预构建与按需编译速度,都是非常快的。 http2 vite充分利用了http2可以并发请求的优势,这也是速度快的一个主要原因。 接下来,我们了解一下http2的来龙去脉。 在之前http1的时候,浏览器对同一个域名的请求,是有并发限制的,一般为6个,如果并发请求6个以上,就会造成阻塞问题,所以在http1的时代,我们要减少打包产物的文件数量,减少并发请求,来提高项目的加载速度。 2015年以后,http2出现了,他可以并发发送多个请求,不会出现http1的并发限制。这时候,将打包产物分成多个小模块,并行去加载,反而会更快。 vite也充分利用了这一优势,对项目资源进行了合理的拆分,访问项目时,同时加载多个模块,来提升项目访问速度。 热更新 vite速度快的另一个原因是与webpack不同的热更新机制。 ...

October 5, 2024

数据绑定

1. 定义 1.1 双向绑定 & 单向绑定 vue的双向绑定,即数据与视图的响应式设计。具体表现为:View的改变能实时让Model发生变化,而Model的变化也能实时更新View。 什么情况下用户可以更新View呢?如:填写表单。当用户填写表单时,View的状态就被更新了,如果此时MVVM框架可以自动更新Model的状态,那就相当于把Model和View做了双向绑定。 注意区别:单向数据绑定,所有数据只有一份,一旦数据变化,就去更新页面(只有data–>DOM,没有DOM–>data)。 用户在页面作出更新,需要用户手动收集(双向绑定是自动收集),在合并到原有的数据中。 Vue.js中的v-model主要用在表单的input输入框,完成视图和数据的双向绑定: <!DOCTYPE html> <html> <head></head> <body> <div id="app"> <input type="text" v-model="message"> <p>{{message}}</p> </div> <script> var app = new Vue({ el: '#app', data: { message: '' } }); </script> </body> </html> 1.2 v-model来添加双向绑定 <input v-model="xxx"> <!-- 上面的代码等价于 --> <input :value="xxx" @input="xxx = $event.target.value"> <!-- 双向绑定 = 单向绑定 + UI事件监听 --> 1.3 双向绑定、单向绑定的优缺点 单向绑定:数据流也是单向的,对于复杂应用来说是实施统一状态管理(如redux)的前提。 双向绑定:在一些需要实时反应用户输入的场合会非常方便(如多级联动菜单)。但常认为复杂应用中这种便利比不上引入状态管理带来的优势。因为不知道状态什么时候发生改变,是谁造成的改变,数据变更也不会通知。 响应式的思路:mvvm Model,View,View-Model 2. 原理概述 Vue 数据双向绑定原理是通过 数据劫持 + 发布者-订阅者模式 的方式来实现的,首先是通过 ES5 提供的 Object.defineProperty() 方法来劫持(监听)各属性的 getter、setter,并在当监听的属性发生变动时通知订阅者,是否需要更新,若更新就会执行对应的更新函数。详见 vue源码。 ...

October 4, 2024

CSRF攻击

CSRF是什么 CSRF(Cross-site request forgery)跨站请求伪造:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求,利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。 一个典型的CSRF攻击有着如下的流程: 受害者登录a.com,并保留了登录凭证(Cookie)。 攻击者引诱受害者访问了b.com。 b.com 向 a.com 发送请求。 a.com接收到请求后,对请求进行验证,并确认是受害者的凭证,误以为是受害者自己发送的请求。 a.com以受害者的名义执行了这个请求。 攻击完成,攻击者在受害者不知情的情况下,冒充受害者,让a.com执行了自己定义的操作。 CSRF的特点 攻击一般发起在第三方网站,而不是被攻击的网站。被攻击的网站无法防止攻击发生 攻击利用受害者在被攻击网站的登录凭证,冒充受害者提交操作;而不是直接窃取数据 整个过程攻击者并不能获取到受害者的登录凭证,仅仅是“冒用” 跨站请求可以用各种方式:图片URL、超链接、CORS、Form提交等等。 如何进行预防 CSRF通常从第三方网站发起,被攻击的网站无法防止攻击发生,只能通过增强自己网站针对CSRF的防护能力来提升安全性。 防止csrf常用方案如下: 阻止不明外域的访问 同源检测 Samesite Cookie 提交时要求附加本域才能获取的信息 CSRF Token 双重Cookie验证 同源检测 Cookie的同源和浏览器的同源策略有所区别: 浏览器同源策略:协议、域名和端口都相同即同源; Cookie同源策略:域名相同即同源; 在HTTP协议中,每个异步请求都会携带两个header,用来标记来源域名: Origin Header Referer Header 这两个Header在浏览器发起请求时,大多数情况会自动带上,并且不能由前端修改,服务器接收到后可以根据这两个Header确定来源的域名; 综上所述: 同源验证是一个相对简单的防范方法,能够防范绝大多数的CSRF攻击。但这并不是万无一失的,对于安全性要求较高,或者有较多用户输入内容的网站,我们就要对关键的接口做额外的防护措施。 Samesite Cookie属性 在Chrome 51版本后,浏览器的 Cookie 新增加了一个SameSite属性,用来防止 CSRF 攻击。 Cookie的Samesite属性用来限制第三方Cookie, 从而减少安全风险,它有三个值: Set-Cookie: SameSite = Strict; Set-Cookie: SameSite = Lax; Set-Cookie: SameSite = None; Strict: 最为严格,完全禁止第三方Cookie, 跨站点时,任何情况都不发送Cookie; Lax: 限制稍微宽松,大多数情况下时不发送第三方Cookie的,除了a链接、预加载请求和GET表单; None: 关闭SameSite属性,但必须同时设置Secure属性, ...

October 2, 2024

模版字符串

未见过的函数用法: function fun(strings,params1){ console.log(strings); console.log(params1) } fun`params1 = ${333}` // 输出分别为 // ['params1 = ', ''] // 333 不知道各位在学习或开发中有没有遇到过这样的函数调用方式呢?秉承着我没遇到的东西就要发文章来分享分享的原则,经过我查阅资料并加以学习,就让本文来带大家了解一下JS中的标签函数吧。 模板字符串 标签函数的主要作用就是处理模板字符串,所以在了解标签函数之前先了解一下模板字符串(模板字面量)吧。 基本用法 模板字符串是ES6新增的使用模板字面量定义字符串的能力,用反引号```````` 进行分隔。与使用''或""的定义的字符串字面量不同是模板字面量保留换行字符,可以跨行定义字符串。 例如: let str1 = 'xxhh\n2233' let str2 = `xxhh 2233` console.log(str1 === str2) // 结果为true 要注意的是模板字符串中的所有字符都会包含在字符串中,特别在使用缩进的时候,缩进的空格都会包含在字符串中。 let str = ` <div> <h1>xxhh</h1> </div> ` console.log(str.length) // 结果为44,包含了缩进所产出的所有空格 字符串插值 在没有模板字符串时,我们通常会使用+加法运算符来进行字符串的拼接,但有了模板字符串后我们并可通过插值占位符${}来将各字面量、变量或表达式结果来进行拼接,这样可以更容易地创建复杂和动态的字符串,同时保持代码的可读性和简洁性。这是模板字符串最常用的一个特性。 let name = 'xxhh'; let str1 = "name:"+name; let str2 = `name:${name}`; console.log(str1,str2) // name:xxhh name:xxhh console.log(str1 === str2) // true 要注意的是使用字符串插值的所有插入的值都会使用toString()强制转型为字符串,而且任何JavaScript 表达式都可以用于插值。 ...

October 2, 2024