跨域

一、什么是跨域? 在前端领域中,跨域是指浏览器允许向服务器发送跨域请求,从而克服Ajax只能同源使用的限制。 什么是同源策略? 同源策略是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。 同源策略限制以下几种行为: Cookie、LocalStorage 和 IndexDB 无法读取 DOM和JS对象无法获得 AJAX 请求不能发送 二、常见的跨域场景 URL 说明 是否允许通信 www.domain.com/a.js www.domain.com/b.js www.domain.com/lab/c.js 同一域名,不同文件或路径 允许 www.domain.com:8000/a.js www.domain.com/b.js 同一域名,不同端口 不允许 http://www.domain.com/a.js https://www.domain.com/b.js 同一域名,不同协议 不允许 http://www.domain.com/a.js http://192.168.4.12/b.js 域名和域名对应相同ip 不允许 http://www.domain.com/a.js http://x.domain.com/b.js http://domain.com/c.js 主域相同,子域不同 不允许 http://www.domain1.com/a.js http://www.domain2.com/b.js 不同域名 不允许 三、9种跨域解决方案 1、JSONP跨域 jsonp的原理就是利用<script>标签没有跨域限制,通过<script>标签src属性,发送带有callback参数的GET请求,服务端将接口返回数据拼凑到callback函数中,返回给浏览器,浏览器解析执行,从而前端拿到callback函数返回的数据。 1)原生JS实现: <script> var script = document.createElement('script'); script.type = 'text/javascript'; // 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数 script.src = 'http://www.domain2.com:8080/login?user=admin&callback=handleCallback'; document.head.appendChild(script); // 回调执行函数 function handleCallback(res) { alert(JSON.stringify(res)); } </script> 服务端返回如下(返回时即执行全局函数): ...

October 1, 2024

移动端适配

导读 移动端适配,是我们在开发中经常会遇到的,这里面可能会遇到非常多的问题: 1px问题 UI图完美适配方案 iPhoneX适配方案 横屏适配 高清屏图片模糊问题 … 上面这些问题可能我们在开发中已经知道如何解决,但是问题产生的原理,以及解决方案的原理可能会模糊不清。在解决这些问题的过程中,我们往往会遇到非常多的概念:像素、分辨率、PPI、DPI、DP、DIP、DPR、视口等等,你真的能分清这些概念的意义吗? 本文将从移动端适配的基础概念出发,探究移动端适配各种问题的解决方案和实现原理。 一、英寸 一般用英寸描述屏幕的物理大小,如电脑显示器的17、22,手机显示器的4.8、5.7等使用的单位都是英寸。 需要注意,上面的尺寸都是屏幕对角线的长度: 英寸(inch,缩写为in)在荷兰语中的本意是大拇指,一英寸就是指甲底部普通人拇指的宽度。 英寸和厘米的换算:1英寸 = 2.54 厘米 二、分辨率 2.1 像素 像素即一个小方块,它具有特定的位置和颜色。 图片、电子屏幕(手机、电脑)就是由无数个具有特定颜色和特定位置的小方块拼接而成。 像素可以作为图片或电子屏幕的最小组成单位。 下面我们使用sketch打开一张图片: 将这些图片放大即可看到这些像素点: 通常我们所说的分辨率有两种,屏幕分辨率和图像分辨率。 2.2 屏幕分辨率 屏幕分辨率指一个屏幕具体由多少个像素点组成。 下面是apple的官网上对手机分辨率的描述: iPhone XS Max 和 iPhone SE的分辨率分别为2688 x 1242和1136 x 640。这表示手机分别在垂直和水平上所具有的像素点数。 当然分辨率高不代表屏幕就清晰,屏幕的清晰程度还与尺寸有关。 2.3 图像分辨率 我们通常说的图片分辨率其实是指图片含有的像素数,比如一张图片的分辨率为800 x 400。这表示图片分别在垂直和水平上所具有的像素点数为800和400。 同一尺寸的图片,分辨率越高,图片越清晰。 2.4 PPI PPI(Pixel Per Inch):每英寸包括的像素数。 PPI可以用于描述屏幕的清晰度以及一张图片的质量。 使用PPI描述图片时,PPI越高,图片质量越高,使用PPI描述屏幕时,PPI越高,屏幕越清晰。 在上面描述手机分辨率的图片中,我们可以看到:iPhone XS Max 和 iPhone SE的PPI分别为458和326,这足以证明前者的屏幕更清晰。 由于手机尺寸为手机对角线的长度,我们通常使用如下的方法计算PPI: iPhone 6的PPI为 ...

November 9, 2024

前端存储

什么是前端存储 开门见山。前端存储就是通过前端技术来存储一段信息,然后在同源下的不同页面中都可以获取到已存储信息的一种策略。 前端存储的作用 方便网页的加载,避免了在发送请求收到响应前页面的空白期 可以在非强制性要求实时更新时减少向服务端的请求,加快渲染速度 在网络不佳或无网时仍有离线数据可以查看 有哪些前端存储方案 如图,一共有5种前端存储方案。大致可以分为3类: Cookie WebStorage:LocalStorage、SessionStorage 数据库存储:IndexedDB、WebSQL 先做一个简单对比: 下面我们详细介绍。 Cookie Cookie 的工作流程: Cookie 的构成: 域、路径、失效时间和安全性都是服务器给浏览器的指示,它们不会随着请求发送给服务器,发送给服务器的只有名称与值的键值对。 Cookie 的生命周期: 如果设定了 Cookie 的过期时间,那么 Cookie 会在到期时自动失效 如果没有设定过期时间,那么 Cookie 就是 session 级别的,即浏览器关闭时 Cookie 自动消失 Cookie 的优缺点: 优点: 可以控制过期时间,不会永久有效,有一定的安全保障 可进行扩展,可跨域共享 通过加密与安全传输技术,可以减少 Cookie 被破解的可能性 有较高的兼容性 缺点: 存储大小最多4KB 存储数量根据浏览器或浏览器版本的不同而不同,并且每个域最多20条 请求头上的数据容易被拦截攻击 存储的数据只能是字符串类型 操作 Cookie: // 设置 Cookie // Cookie 值必须是字符串类型,并且不支持分号、逗号以及空格, // 所以有时需要先使用 encodeURIComponent() 进行编码,或者使用 JSON.stringify() 进行序列化 document.cookie = '键1=值1;键2=值2;键n=值n'; // 读取 Cookie // 有时需要使用 decodeURIComponent() 或者 JSON.parse() document.cookie // 修改 Cookie // 如果键不存在,就新增;否则就修改 document.cookie = '已经存储过的键=新值'; // 删除 Cookie document.cookie = '要删除的键=任意值;max-age=0'; WebStorage WebStorage 是 HTML5 新加的,WebStorage(Web 存储) 分为 LocalStorage(本地存储) 和 SessionStorage(会话存储)。 ...

October 1, 2024

优化性能

前言 随着互联网发展至今,对于网站来说,性能显的越来越重要了,CSS作为页面渲染和内容展现的重要环节,影响着用户对整个网站的第一体验。所以,我们需要重视与CSS相关的性能优化。 项目开发初期我们可能因为各种原因(很大一部分原因是因为项目工期,产品往往把项目上线时间卡的死死的,根本不听你说的什么性能优化),怎么写的舒服就怎么来,对于性能优化我们常常在项目完成时才去考虑,经常被推迟到项目的末期,甚至到暴露出严重的性能问题时才进行性能优化。 为了更多地避免这一情况,首先要重视起性能优化相关的工作,将其贯穿到整个产品设计与开发中。其次,就是了解性能相关的内容,在项目开发过程中,自然而然地进行性能优化。 css渲染规则 想要优化CSS的性能,我们首先需要了解CSS的渲染规则,CSS选择器是从右向左进行匹配的 来看个例子🌰: .nav h3 a{font-size: 14px;} 渲染过程大概是:首先找到所有的a,沿着a的父元素查找h3,然后再沿着h3,查找.nav。中途找到了符合匹配规则的节点就加入结果集。如果找到根元素html都没有匹配,则不再遍历这条路径,从下一个a开始重复这个查找匹配(只要页面上有多个最右节点为a)。 Tips:为什么CSS选择器是从右向左匹配的? CSS中更多的选择器是不会匹配的,所以在考虑性能问题时,需要考虑的是如何在选择器不匹配时提升效率。从右向左匹配就是为了达成这一目的的,通过这一策略能够使得CSS选择器在不匹配的时候效率更高。这样想来,在匹配时多耗费一些性能也能够想的通了。 内联首屏关键CSS(Critical CSS) 性能优化中有一个重要的指标——首次有效绘制(First Meaningful Paint,简称FMP)即指页面的首要内容(primary content)出现在屏幕上的时间。这一指标影响用户看到页面前所需等待的时间,而 内联首屏关键CSS(即Critical CSS,可以称之为首屏关键CSS) 能减少这一时间。 很多人都喜欢通过link标签引用外部CSS文件。但需要知道的是,将CSS直接内联到HTML文档中能使CSS更快速地下载。而使用外部CSS文件时,需要在HTML文档下载完成后才知道所要引用的CSS文件,然后才下载它们。所以说,内联CSS能够使浏览器开始页面渲染的时间提前,因为在HTML下载完成之后就能渲染了。 但是我们不应该将所有的CSS都内联在HTML文档中,因为[初始拥塞窗口]存在限制(TCP相关概念,通常是 14.6kB,压缩后大小),如果内联CSS后的文件超出了这一限制,系统就需要在服务器和浏览器之间进行更多次的往返,这样并不能提前页面渲染时间。因此,我们应当只将渲染首屏内容所需的关键CSS内联到HTML中。 ⚠️还有一点需要注意的是内联CSS没有缓存,每次都会随HTML的加载而重新下载,但我们将内联首屏关键CSS控制在 14.6kB以内,它对性能优化还是起到正向作用的。(凡事有利也有弊) 异步加载非首屏CSS 我们需要知道两点内容:(具体可以看我之前的文章:这些浏览器面试题,看看你能回答几个?) CSS不会阻塞DOM的解析,但会阻塞DOM的渲染 CSS会阻塞JS执行,但不会阻塞JS文件的下载 由于CSS会阻塞DOM的渲染,所以我们将首屏关键CSS内联后,剩余的非首屏CSS内容可以使用外部CSS,并且异步加载,防止非首屏CSS内容阻塞页面的渲染。 CSS异步加载方式 第一种方法是动态创建 // 创建link标签 const myCSS = document.createElement( "link" ); myCSS.rel = "stylesheet"; myCSS.href = "mystyles.css"; // 插入到header的最后位置 document.head.insertBefore( myCSS, document.head.childNodes[ document.head.childNodes.length - 1 ].nextSibling ); 第二种方法是将link元素的media属性设置为用户浏览器不匹配的媒体类型(或媒体查询) 对浏览器来说,如果样式表不适用于当前媒体类型,其优先级会被放低,会在不阻塞页面渲染的情况下再进行下载。在首屏文件加载完成之后,将media的值设为screen或all,从而让浏览器开始解析CSS。 <link rel="stylesheet" href="mystyles.css" media="noexist" onload="this.media='all'"> 第三种方法是通过rel属性将link元素标记为alternate可选样式表 <link rel="alternate stylesheet" href="mystyles.css" onload="this.rel='stylesheet'"> 第四种方法是使用rel=preload来异步加载CSS <link rel="preload" href="mystyles.css" as="style" onload="this.rel='stylesheet'"> 注意,as是必须的。忽略as属性,或者错误的as属性会使preload等同于XHR请求,浏览器不知道加载的是什么内容,因此此类资源加载优先级会非常低。as的可选值可以参考上述标准文档。 看起来,rel="preload"的用法和上面两种没什么区别,都是通过更改某些属性,使得浏览器异步加载CSS文件但不解析,直到加载完成并将修改还原,然后开始解析。 但是它们之间其实有一个很重要的不同点,那就是使用preload,比使用不匹配的media方法能够更早地开始加载CSS。所以尽管这一标准的支持度还不完善,仍建议优先使用该方法。 CSS文件压缩 这应该是最容易想到的一个方法了,通过压缩CSS文件大小来提高页面加载速度。现在的构建工具,如webpack、gulp/grunt、rollup等也都支持CSS压缩功能。压缩后的文件能够明显减小,可以大大降低了浏览器的加载时间。 CSS层级嵌套最好不要超过3层 一般情况下,元素的嵌套层级不能超过3级,过度的嵌套会导致代码变得臃肿,沉余,复杂。导致css文件体积变大,造成性能浪费,影响渲染的速度!而且过于依赖HTML文档结构。这样的css样式,维护起来,极度麻烦,如果以后要修改样式,可能要使用!important覆盖。尽量保持简单,不要使用嵌套过多过于复杂的选择器。 删除无用CSS代码 一般情况下,会存在这两种无用的CSS代码:一种是不同元素或者其他情况下的重复代码,一种是整个页面内没有生效的CSS代码。 对于前者,在编写的代码时候,我们应该尽可能地提取公共类,减少重复。对于后者,在不同开发者进行代码维护的过程中,总会产生不再使用的CSS的代码,当然一个人编写时也有可能出现这一问题。而这些无用的CSS代码不仅会增加浏览器的下载量,还会增加浏览器的解析时间,这对性能来说是很大的消耗。所以我们需要找到并去除这些无用代码。 那么我们如何知道哪些CSS代码是无用代码呢? 谷歌的Chrome浏览器就有这种开箱即用的功能。只需转到查看>开发人员>开发人员工具,并在最近的版本中打开Sources选项卡,然后打开命令菜单。然后,点击Coverage,在Coverage analysis窗口中高亮显示当前页面上未使用的代码。 慎用*通配符 我们有时候可能会写下面这种代码来消除一些标签的默认样式或统一浏览器对标签渲染的差异化: *{ margin:0; padding:0; } 这样虽然代码量少,但它的性能可不是最佳的,我们最好还是写对应的标签选择器: ...

October 10, 2024

WebWorker

前言 先来聊聊单线程的Javascript 众所周知,js最初设计是运行在浏览器中的,为了防止多个线程同时操作DOM,带来渲染冲突问题,所以js执行器被设计成单线程。但随着前端技术的发展,js能力远不止如此,当我们遇到需要大量计算的场景时(比如图像处理、视频解码等),js线程往往会被长时间阻塞,甚至造成页面卡顿,影响用户体验。为了解决单线程带来的这一弊端,Web Worker 应运而生。 1. Web Worker 1.1 Web Worker 是什么 Web Worker 是 HTML5 标准的一部分,这一规范定义了一套 API,允许我们在 js 主线程之外开辟新的 Worker 线程,并将一段 js 脚本运行其中,它赋予了开发者利用 js 操作多线程的能力。 因为是独立的线程,Worker 线程与 js 主线程能够同时运行,互不阻塞。所以,在我们有大量运算任务时,可以把运算任务交给 Worker 线程去处理,当 Worker 线程计算完成,再把结果返回给 js 主线程。这样,js 主线程只用专注处理业务逻辑,不用耗费过多时间去处理大量复杂计算,从而减少了阻塞时间,也提高了运行效率,页面流畅度和用户体验自然而然也提高了。 1.2 Web Worker 能干些什么 虽然 Worker 线程是在浏览器环境中被唤起,但是它与当前页面窗口运行在不同的全局上下文中,我们常用的顶层对象 window,以及 parent 对象在 Worker 线程上下文中是不可用的。另外,在 Worker 线程上下文中,操作 DOM 的行为也是不可行的,document对象也不存在。但是,location和navigator对象可以以可读方式访问。除此之外,绝大多数 Window 对象上的方法和属性,都被共享到 Worker 上下文全局对象 WorkerGlobalScope 中。同样,Worker 线程上下文也存在一个顶级对象 self。 详细信息请参考:Functions and classes available to Web Workers 2. Web Worker 使用 2.1 创建 worker 创建 worker 只需要通过 new 调用 Worker() 构造函数即可,它接收两个参数 ...

October 1, 2024

新变化

CSS 发展史 上图是20 世纪 90 年万维网刚出现时,由于当时并没有可以装饰网页的方法,原始的 Web 页面呈现的效果,随着 CSS(层叠样式表,Cascading Style Sheets)的诞生与发展,Web页面的样式有了翻天覆地的变化。 CSS的发布过程 CSS1.0 19912月W3C发布了第一个有关样式的标准CSS1.0。这个版本中,已经包含了的相关font的相关属性、颜色与背景的相关属性、文字的相关属性、box的相关属性等。 CSS2.0 1985年5月,CSS2.0正式推出。这个版本推荐的是内容和表现效果分离的方式,并开始使用样式表结构。 CSS2.1 2004年2月,CSS2.1正式推出。它在CSS2.0的基础上略微做了改动,删除了许多不被浏览器支持的属性 CSS3 从2011年开始CSS被分为多个模块单独升级。这些模块统称为CSS3 包含有: CSS 选择器level3 CSS 媒体查询level3 CSS color level3 CSS4 设计中 CSS 标准的制定大概有下面几个步骤 下面主要介绍的是与我们日常工作有关联的一些新特性,其中一些已经在主流浏览器中得到了支持,还有一些特性还在草案阶段,可能还会有一些变化。 CSS 伪类选择器 is()和where() 在编写css时,有时会需要使用很长的选择器列表来定位具有相同样式的子元素,比如对所有H元素下的span设置为block,之前会这么写: 这样写选择器看起来很长,可读性差。 此时,可以使用is()来提高易读性,同时避免使用长选择器 浏览器支持: :is() Chrome 88/ Firefox 78/ Edge 88 /Safari 14 :where() Chrome 88/ Firefox 78/ Edge 88 /Safari 14 注意: is()和where()在使用方法上和其他的伪类选择器一样,可以任意组合排列。 is()和where()的功能大体上是一样的,他们的差异性体现在选择器的权重上 where()选择器没有权重,也就是权重是0 is()选择器的权重由它的选择器列表中的最搞权重决定 规则示例: ...

November 9, 2024

Service Worker

引言 我们经常可以听到 Worker 这个名词,比如本文要介绍的 ServiceWorker 后面就带着一个 Worker 的单词,那么 Worker 实际上指的是什么呢? 在介绍 Worker 的定义之前,先来回忆一下浏览器的渲染原理。 在一个 tab 打开的时候,浏览器会给这个 tab 创建一个新的渲染进程(renderer process),如下图: 图1:Chrome 浏览器的多进程架构 图片来源:developer.chrome.com/blog/inside… 在每一个渲染进程中,都会有一个主线程(Main Thread),负责 JavaScript 的执行以及浏览器的渲染(JavaScript 的执行与 UI 渲染是一个互相阻塞的流程)。 图2:浏览器一帧的剖析 图片来源:aerotwist.com/blog/the-an… 如果 JavaScript 执行时间过久(比如超过 33.33 ms),那么一帧内留给 UI 渲染的时间不多,如果这时候网页有正在执行的动画,那么用户就会感受到卡顿。 10 FPS:能够达成基本的视觉残留(参考:定格动画至少需要 12 帧每秒以上) 30 FPS以下:让人感觉到明显的卡顿和不适感 30-50 FPS:因人而异 50-60 FPS:流畅 一个 Worker,指的是一个可以在后台独立执行 JavaScript 脚本。它存在于一个单独的 worker 线程,即使执行一些长任务也不会阻塞主线程响应用户操作(如鼠标点击、动画等)。 另外 Worker 也指使用 new Worker() 构造函数创建的一个对象,可以用在主线程与 worker 线程的通信 图3:worker 线程独立于主线程之外 图片来源:developer.chrome.com/blog/inside… Worker 与 主线程之间可以通过 postMessage 进行通信: ...

November 8, 2024

WebSocket

一、什么是WebSocket WebSocket 是一种在单个TCP连接上进行全双工通信的协议。WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。 在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接, 并进行双向数据传输。(维基百科) WebSocket本质上一种计算机网络应用层的协议,用来弥补http协议在持久通信能力上的不足。 WebSocket 协议在2008年诞生,2011年成为国际标准。现在最新版本浏览器都已经支持了。 它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。 WebSocket 的其他特点包括: (1)建立在 TCP 协议之上,服务器端的实现比较容易。 (2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。 (3)数据格式比较轻量,性能开销小,通信高效。 (4)可以发送文本,也可以发送二进制数据。 (5)没有同源限制,客户端可以与任意服务器通信。 (6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。 ws://example.com:80/some/path 为什么需要 WebSocket? 我们已经有了 HTTP 协议,为什么还需要另一个协议?它能带来什么好处? 因为 HTTP 协议有一个缺陷:通信只能由客户端发起,不具备服务器推送能力。 举例来说,我们想了解查询今天的实时数据,只能是客户端向服务器发出请求,服务器返回查询结果。HTTP 协议做不到服务器主动向客户端推送信息。 这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。我们只能使用“轮询”:每隔一段时候,就发出一个询问,了解服务器有没有新的信息。最典型的场景就是聊天室。轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。 在 WebSocket 协议出现以前,创建一个和服务端进双通道通信的 web 应用,需要依赖HTTP协议,进行不停的轮询,这会导致一些问题: 服务端被迫维持来自每个客户端的大量不同的连接 大量的轮询请求会造成高开销,比如会带上多余的header,造成了无用的数据传输。 http协议本身是没有持久通信能力的,但是我们在实际的应用中,是很需要这种能力的,所以,为了解决这些问题,WebSocket协议由此而生,于2011年被IETF定为标准RFC6455,并被RFC7936所补充规范。 并且在HTML5标准中增加了有关WebSocket协议的相关api,所以只要实现了HTML5标准的客户端,就可以与支持WebSocket协议的服务器进行全双工的持久通信了。 WebSocket 与 HTTP 的区别 WebSocket 与 HTTP的关系图: 相同点: 都是一样基于TCP的,都是可靠性传输协议。都是应用层协议。 联系: WebSocket在建立握手时,数据是通过HTTP传输的。但是建立之后,在真正传输时候是不需要HTTP协议的。 下面一张图说明了 HTTP 与 WebSocket 的主要区别: ...

November 8, 2024

设计模式

说到设计模式,大家想到的就是六大原则,23种模式。这么多模式,并非都要记住,但作为前端开发,对于前端出现率高的设计模式还是有必要了解并掌握的,浅浅掌握9种模式后,整理了这份文章。 那么,我们先了解六大原则 六大原则: 依赖倒置原则(Dependence Inversion Principle):高层(业务层)不应该直接调用底层(基础层)模块 开闭原则(Open Close Principle):单模块对拓展开放、对修改关闭 单一原则(Single Responsibility Principle):单模块负责的职责必须是单一的 迪米特法则(Law of Demeter):对外暴露接口应该简单 接口隔离原则(Interface Segregation Principle):单个接口(类)都应该按业务隔离开 里氏替换原则(Liskov Substitution Principle):子类可以替换父类 六大原则也可以用六个字替换:高内聚低耦合。 高层不直接依赖底层:依赖倒置原则 内部修改关闭,外部开放扩展:开闭原则 聚合单一功能:单一原则 低知识接口,对外接口简单:迪米特法则 耦合多个接口,不如隔离拆分:接口隔离原则 合并复用,子类可以替换父类:里氏替换原则 我们采用模式编写时,要尽可能遵守这六大原则 23 种设计模式分为“创建型”、“行为型”和“结构型” 前端九种设计模式 一、创建型 创建型从功能上来说就是创建元素,目标是规范元素创建步骤 1.构造器模式:抽象了对象实例的变与不变(变的是属性值,不变的是属性名) // 需求:给公司员工创建线上基本信息 // 单个员工创建,可以直接使用创建 const obj = { name:'张三', age:'20', department:'人力资源部门' } // 可员工的数量过于多的时候,一个个创建不可行,那么就可以使用构造器模式 class Person { constructor(obj){ this.name = obj.name this.age = obj.age this.department = obj.department } } const person1 = new Person(obj) 2. 工厂模式:为创建一组相关或相互依赖的对象提供一个接口,且无须指定它们的具体类 即隐藏创建过程、暴露共同接口。 // 需求:公司员工创建完信息后需要为每一个员工创建一个信息名片 class setPerson { constructor(obj) { this.pesonObj = obj } creatCard() { //创建信息名片 } otherFynction(){ } } class Person { constructor(obj) { return new setPerson(obj) } } const person = new Person() const card = person.creatCard({ name:'张三', age:'20', department:'人力资源部门' }) 3. 单例模式:全局只有一个实例,避免重复创建对象,优化性能 // 需求:判断一款应用的开闭状态,根据不同状态给出不同提示 class applicationStation { constructor() { this.state = 'off' } play() { if (this.state === 'on') { console.log('已打开') return } this.state = 'on' } shutdown() { if (this.state === 'off') { console.log('已关闭') return } this.state = 'off' } } window.applicationStation = new applicationStation() // applicationStation.instance = undefined // applicationStation.getInstance = function() { // return function() { // if (!applicationStation.instance) { // 如果全局没有实例再创建 // applicationStation.instance = new applicationStation() // } // return applicationStation.instance // }() // } // application1和application2拥有同一个applicationStation对象 const application1 = window.applicationStation const application2 = window.applicationStation 二、结构型 结构型从功能上来说就是给元素添加行为的,目标是优化结构的实现方式 ...

October 1, 2024

Agent 面试题

共 119 道 Agent 面试题。答案默认折叠,便于先自行作答。 1. 如何设计一个企业级 AI Agent 平台? 难度:3.5 · 类型:QA 题目要点 企业级 AI Agent 平台的设计重点不是“接一个大模型接口”,而是围绕业务任务构建完整体系:前端提供可解释、可配置、可监控的交互体验;中间层负责 Agent 编排、工具调用和知识增强;底层统一管理模型、数据和企业系统;治理层保障权限、安全、审计、评测和成本控制。真正可落地的平台,一定是在智能能力和工程确定性之间取得平衡。 参考答案 企业级 AI Agent 平台不能只设计成一个“聊天入口”,更应该设计成一个可治理、可扩展、可观测的智能任务执行平台。它的核心目标是让企业可以安全地把大模型能力接入真实业务流程,例如客服、知识检索、数据分析、代码辅助、审批流、运营自动化等场景。 从架构上看,可以分为四个关键层次:前端体验层、Agent 编排层、能力接入层和企业治理层。 前端体验层不应该只有 Chat UI,而要支持多种交互形态。对于普通用户,可以提供对话式入口、任务面板、历史记录、引用来源、执行进度和人工确认节点;对于业务管理员,需要有 Agent 配置台,可以配置角色、Prompt、工具权限、知识库、工作流、触发条件和发布版本;对于运维和安全人员,则需要看到调用链路、成本消耗、失败原因、审计日志和风险拦截记录。 Agent 编排层是平台的核心。这里不能简单地把用户输入直接发给模型,而是要经过意图识别、上下文构建、计划拆解、工具选择、执行反馈和结果汇总。复杂任务通常需要支持多 Agent 协作,例如一个 Agent 负责理解需求,一个 Agent 负责查知识库,一个 Agent 负责调用业务系统,还有一个 Agent 负责校验输出质量。对于企业场景,工作流式 Agent 和自主规划式 Agent 往往要结合使用:高风险业务用确定性流程约束,低风险探索任务可以给 Agent 更高自主度。 能力接入层主要解决模型、知识和工具的问题。模型层需要做统一网关,屏蔽不同模型厂商差异,支持模型路由、降级、限流、重试和成本统计。知识层通常采用 RAG 架构,需要处理文档解析、切片、向量化、权限过滤、召回排序和答案引用,避免 Agent 编造来源。工具层则要把企业内部系统封装成标准 Tool,例如 CRM、工单、审批、BI、代码仓库、飞书、邮件、数据库等,同时每个工具都要有清晰的输入输出 Schema、权限边界和幂等设计。 企业级平台最容易被低估的是治理能力。Agent 一旦可以调用工具,就不再只是“生成文本”,而是在执行真实操作,所以必须有权限控制、数据脱敏、敏感词策略、操作审计、人工确认和回滚机制。例如查询类工具可以自动执行,但转账、删除、发公告、修改生产配置这类操作必须进入 human-in-the-loop 流程。平台还需要支持租户隔离、部门权限、知识库权限继承和操作留痕。 前端设计上,要重点解决“可解释”和“可控”。用户不能只看到一个最终答案,而应该能看到 Agent 做了哪些步骤、用了哪些知识、调用了哪些工具、哪些地方失败了、是否需要用户确认。对于长任务,要有状态机式的任务视图,而不是让用户一直等在输入框前。对于企业用户来说,信任感往往来自透明度,而不是更拟人的回复。 另外,平台需要内置评测体系。Agent 发布前要经过离线评测,例如准确率、召回率、幻觉率、工具调用成功率、越权风险和响应时延;上线后要持续收集用户反馈、人工纠错、失败样本和成本数据,再反向优化 Prompt、知识库、工具描述和模型路由。没有评测闭环的 Agent 平台,很难在企业里长期稳定运行。 ...