新变化

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

PCHelper

背景介绍 腾讯体育的个人直播业务中,主播开启比赛陪看直播一直仅支持移动端开播。然而,移动端开播存在屏幕尺寸过小、摄像头拍摄角度受限等问题,严重影响了主播的解说体验。为了提升主播在比赛解说场景下的使用体验,同时降低使用 OBS 进行运营的人力成本,我们决定在腾讯视频 PC 开播助手的基础上进行迭代开发,新增对腾讯体育比赛陪看功能的支持。 选择陪看比赛界面: 陪看解说画面: 从零开发一款 PC 开播软件的成本较高。幸运的是,腾讯视频已经拥有一款成熟的 PC 开播助手,用于支持主播或艺人在 PC 端进行直播。虽然当时该产品尚不支持陪看直播功能,但我们可以在其基础上进行二次开发:接入腾讯体育的账号体系,并新增比赛陪看能力,从而以最小的开发成本实现业务需求。 整体架构概览 腾讯视频 PC 开播助手基于 Electron 框架开发,推流等核心功能则依赖 OBS Node(obs-studio-node)实现,前端部分采用 Vue.js 技术栈。 OBS Studio 是一款知名的开源直播与录屏工具,其客户端使用 Qt 框架绘制界面。OBS Node 是将 OBS Studio 的核心功能模块进行封装,使其能够被 Node.js 直接调用的扩展库。 OBS 原生支持多种推流协议,但腾讯视频直播中台采用的是 TRTC(Tencent Real-Time Communication)协议。在此基础上,视频 PC 开播助手对 OBS Node 进行了以下扩展: TRTC 协议支持:实现基于 TRTC 的推流和连麦功能; 移动设备开播支持:支持使用手机等移动设备作为视频源进行开播。 整体技术架构如下图所示: 图 1|整体技术架构 Renderer 进程Vue UI ⇄ IPC ⇄ Main 进程Node + Native → OBS Node + TRTC SDK 腾讯体育后台服务(账号/房间/陪看控制) → Main 进程 虚拟主播(比赛点播转直播) → TRTC SDK 主播推流 → TRTC 云端混流 → 观众端 多房间架构设计(SubRoom) 陪看场景最大的技术约束是:主播需要同时处于主直播间和虚拟陪看房间。为此我们在 TRTC 单房间模型之上封装了 ITRTCSubRoom,并通过 SubRoomService 统一管理子房间生命周期。 ...

June 1, 2026

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 平台,很难在企业里长期稳定运行。 ...

AI相关 面试题

共 43 道 AI相关 面试题。答案默认折叠,便于先自行作答。 1. 如何实现 ChatGPT 类似的流式输出? 难度:1.5 · 类型:QA 题目要点 流式输出的核心是服务端通过 SSE、fetch stream 或 WebSocket 持续返回增量内容,前端通过 ReadableStream 边读边渲染。实现时要注意网络 chunk 不等于完整消息,需要做缓冲和协议解析。前端渲染要做节流,避免频繁状态更新导致卡顿。Agent 场景下建议使用结构化事件协议,把文本增量、工具调用、状态、错误、完成信号分开处理。最后还要支持取消、错误恢复、最终 Markdown 校正和滚动体验,才能达到接近 ChatGPT 的交互质量。 参考答案 类似 ChatGPT 的流式输出,本质是:服务端不要等完整答案生成后再返回,而是把模型生成的 token 或文本片段持续写入 HTTP 响应;前端边读边渲染,并在完成、取消、异常时维护好状态。 实际项目里最常见的是 SSE 或 fetch + ReadableStream。如果只是服务端向前端单向推送,SSE 足够稳定;如果需要双向实时交互,比如语音、多人协作、实时 Agent 控制,可以用 WebSocket。在 ChatGPT 这类文本生成场景中,fetch 读取流也很常用,因为它支持 POST、请求体、鉴权、AbortController,控制能力比原生 EventSource 更强。 前端核心代码大致是这样: async function streamChat(messages: any[], onDelta: (text: string) => void) { const controller = new AbortController(); const response = await fetch('/api/chat/stream', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ messages }), signal: controller.signal, }); if (!response.ok || !response.body) { throw new Error('stream request failed'); } const reader = response.body.getReader(); const decoder = new TextDecoder('utf-8'); let buffer = ''; while (true) { const { value, done } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); const events = buffer.split('\n\n'); buffer = events.pop() || ''; for (const event of events) { const line = event .split('\n') .find((item) => item.startsWith('data:')); if (!line) continue; const data = line.replace(/^data:\s*/, ''); if (data === '[DONE]') { return; } const payload = JSON.parse(data); if (payload.type === 'delta') { onDelta(payload.content); } } } controller.abort(); } 服务端返回时通常使用类似这样的协议: ...

CSS 面试题

共 114 道 CSS 面试题。答案默认折叠,便于先自行作答。 1. Tailwind 的响应式断点(如 md:)底层如何实现? 难度:2 · 类型:QA 题目要点 Tailwind 的响应式断点基于构建期的变体系统实现。md: 在编译阶段被解析为 screen 变体,并根据 screens 配置生成对应的 @media (min-width) 包裹规则。所有响应式逻辑依赖原生 CSS media query,遵循 mobile-first 原则,通过规则生成管线支持多变体组合,不涉及运行时计算。 参考答案 这个问题的关键在于理解: md: 不是运行时逻辑,而是 构建期的变体(variant)展开机制。 在 Tailwind CSS 中,响应式系统建立在三个核心之上: screens 配置映射 变体解析机制 JIT 构建阶段规则生成 一、md: 本质是什么? 当写: <div class="md:text-red-500"> Tailwind 在构建阶段会将其解析为: variant:md utility:text-red-500 然后生成如下 CSS: @media (min-width: 768px) { .md\:text-red-500 { color: #ef4444; } } 关键点: md: 只是一个前缀标记 构建阶段转换成 @media 冒号会被转义为合法 CSS 选择器 二、断点从哪里来? 断点定义在配置文件中: ...

ES6 面试题

共 51 道 ES6 面试题。答案默认折叠,便于先自行作答。 1. 说说 Class 语法糖的底层实现 难度:3 · 类型:QA 题目要点 class 是基于函数构造器和原型链的语法封装,实例方法本质挂载在 prototype 上,继承通过 Object.create 构建原型链,同时通过构造函数原型链继承静态方法。super 依赖内部 [[HomeObject]] 机制定位父类方法。class 并未引入新的对象模型,而是在原型继承基础上增加语义约束与语法增强。 参考答案 这个问题的关键在于理解:class 并不是一种新的面向对象机制,而是对 原型继承(prototype-based inheritance) 的语法封装。底层仍然是函数构造器 + 原型链。 如果把 class 当作 Java 那种基于类的模型去理解,就会产生误解。 先看一个简单例子: class Person { constructor(name) { this.name = name; } say() { console.log(this.name); } static create(name) { return new Person(name); } } 它在底层大致等价于: function Person(name) { this.name = name; } Person.prototype.say = function () { console.log(this.name); }; Person.create = function (name) { return new Person(name); }; 一、实例方法的实现 ...

HTML 面试题

共 77 道 HTML 面试题。答案默认折叠,便于先自行作答。 1. SPA和MPA的区别? 难度:1 · 类型:QA 题目要点 SPA 通过前端路由在单一 HTML 中完成页面切换,交互流畅但首屏和 SEO 成本较高;MPA 每次导航请求新页面,SEO 友好且隔离性强,但交互体验有限;两者本质差异在于前后端渲染与路由职责的划分;现代项目常通过 SSR、SSG 等方式在两种模式之间折中。 参考答案 SPA 和 MPA 的区别,表面看是“页面跳转方式不同”,本质上是前端与服务器在渲染职责、路由控制以及资源加载策略上的分工差异。理解这一点,才能在具体业务中做出合理选择。 SPA(Single Page Application)只有一个 HTML 入口,首屏加载时会拉取完整的 JavaScript 应用骨架,之后的页面切换通过前端路由完成。URL 的变化并不会触发浏览器重新请求文档,而是由前端框架根据路由状态切换视图并复用已有资源。这种模式的优势在于页面切换流畅、状态可以在客户端长期保留,复杂交互体验更接近原生应用,非常适合后台系统、强交互工具型产品。但代价也同样明显:首屏资源压力大,SEO 先天不足,对 JS 执行和异常处理的依赖极高,一旦主 bundle 出现问题,整个应用都无法正常工作。 MPA(Multi Page Application)则以“页面”为核心单元。每一次导航都会向服务器请求一个新的 HTML 文档,路由和渲染主要由服务端控制。页面之间天然隔离,资源和状态会随页面刷新而重置。这种模式的优势在于首屏直观、SEO 友好、容错性强,适合内容型站点、营销页和对搜索引擎依赖较高的业务场景。但在交互复杂度上,MPA 往往需要付出更多成本,例如重复加载公共资源、跨页面状态共享困难,整体体验也更接近传统 Web。 从工程角度看,SPA 和 MPA 的差异还体现在构建和部署方式上。SPA 更强调前端工程化和运行时能力,构建目标是一个可长期运行的客户端应用;MPA 更强调页面级拆分和服务端模板渲染,前端更多是增强层而非控制中心。在现代实践中,两者并非对立关系,常见的折中方案包括 SSR、SSG 以及“多入口 SPA”,试图在交互体验和首屏性能、SEO 之间取得平衡。 因此,选择 SPA 还是 MPA,并不存在绝对优劣,关键在于业务目标:是以复杂交互和状态管理为核心,还是以内容分发和搜索可见性为核心。 2. 常见的H5标签有哪些 ,你是怎么用的 难度:1 · 类型:QA 题目要点 常见 H5 标签包括结构语义标签、文本语义标签、表单标签和媒体标签;实际使用中优先考虑语义表达和可访问性,而非样式;充分利用表单和媒体标签的原生能力以减少 JS 复杂度;canvas 与 svg 需根据场景选择;合理使用语义化标签能显著提升页面质量和长期可维护性。 ...