面试题手册

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

服务端阅读 05月29日 00:51

DevSecOps 的关键实践有哪些?如何将安全左移?

DevSecOps 是将安全内嵌到 DevOps 全流程的实践,核心理念是"安全左移"——在编码阶段而非上线后才做安全检查。关键实践包括:在 CI 流水线中集成 SAST(静态代码扫描)和 SCA(依赖漏洞扫描),构建阶段做容器镜像扫描(Trivy),部署前用 DAST 对运行时做动态测试,运行时通过 Falco 做入侵检测。此外还包括用 Vault 管理密钥与凭证轮换、RBAC 最小权限控制、基础设施即代码的安全扫描(tfsec),以及用 SLSA 框架保障软件供应链完整性。本质是把安全从"门卫"变成"内嵌检查点"。追问SAST 和 DAST 分别能发现什么类型的漏洞?为什么两者必须互补?容器镜像扫描应该扫基础镜像还是应用层?Trivy 的工作原理是什么?HashiCorp Vault 如何实现密钥自动轮换而不中断服务?SLSA 框架的四个级别分别保证什么?达到 Level 3 需要哪些前提?如何处理安全扫描的大量误报而不拖慢 CI 流水线?写段代码# CI 流水线内嵌安全扫描stages: - security - testsast: stage: security script: semgrep ci --config autodep-scan: stage: security script: snyk test --severity-threshold=highcontainer-scan: stage: test script: - docker build -t app:$CI_SHA . - trivy image --exit-code 1 app:$CI_SHA
服务端阅读 05月29日 00:51

如何开发 Cheerio 插件?有哪些实用插件模式?

Cheerio 插件的核心模式是扩展 cheerio.prototype(即 $.fn),给选择器结果集添加自定义方法。基本写法:module.exports = function(cheerio) { cheerio.prototype.myMethod = function() { return this; } },然后 cheerio.use(pluginFn) 加载。插件方法内部通过 this 访问当前选中的元素集合,用 cheerio(el) 包装后即可调用所有原生方法。返回 this 支持链式调用,返回 .get() 则输出普通数组。追问插件方法怎么访问当前选中的元素?方法内 this 就是 cheerio 对象,this.each((i, el) => ...) 遍历,cheerio(el) 包装单个元素。this.length 获取匹配数量,this[i] 直接取原生节点。怎么让插件支持配置参数?导出一个工厂函数而非直接函数:module.exports = (options) => (cheerio) => { ... },调用时 cheerio.use(myPlugin({ trim: true }))。内部用默认参数合并:const opts = { trim: true, ...options }。有哪些实用的插件场景?最常见三类:①文本清洗——批量 removeTags/cleanText,去掉 script/style/注释;②结构化提取——tableToArray 把表格转二维数组,tableToObjects 按表头生成对象数组;③URL 标准化——resolveUrls 把相对路径转绝对路径,处理懒加载 data-src。插件方法和独立工具函数怎么选?需要操作 DOM 节点、支持链式调用、依赖当前选择器上下文时用插件;纯数据转换、不涉及 DOM 操作时用普通函数。插件的优势是和 cheerio API 风格统一,缺点是污染原型。开发插件要注意什么?方法名加业务前缀避免冲突(如 seo_extractLinks 而非 extractLinks);返回 this 保持链式;用 try-catch 包裹核心逻辑防止异常中断;在 peerDependencies 声明 cheerio 版本。写段代码// 表格转对象数组插件module.exports = function(cheerio) { cheerio.prototype.tableToObjects = function() { const headers = this.find('th').map((i, el) => cheerio(el).text().trim()).get(); return this.find('tbody tr').map((i, tr) => { const obj = {}; cheerio(tr).find('td').each((j, td) => { obj[headers[j]] = cheerio(td).text().trim(); }); return obj; }).get(); };};
服务端阅读 05月29日 00:51

Elasticsearch 如何实现地理空间搜索?

ES 通过 geopoint 和 geoshape 两种类型支持地理搜索。geopoint 存储经纬度坐标点,支持 geodistance(圆形范围)、geoboundingbox(矩形范围)、geopolygon(多边形范围)查询;geoshape 存储复杂几何形状(线、多边形),支持相交、包含等空间关系查询。底层使用 geohash 编码将二维坐标映射为一维字符串,利用 BKD tree 索引加速范围检索。查询时先通过 geohash 前缀粗筛,再计算精确距离过滤。追问geopoint 和 geoshape 怎么选?存储门店位置等点数据用 geopoint,存储配送区域等面数据用 geoshape。geopoint 查询更快,geoshape 支持更复杂的空间关系但索引开销更大。geo_distance 查询性能如何优化?先用 geoboundingbox 缩小候选集,再在结果上做精确距离计算。也可设置 geopoint 的 geohashprecision 控制索引精度。经纬度顺序容易搞混怎么办?ES 的 geo_point 支持多种格式:字符串 "lat,lon"、数组 [lon,lat](GeoJSON 标准)、对象 {lat,lon}。数组格式是 lon 在前,容易出错,推荐用对象格式避免歧义。geohash 精度怎么选?精度越高定位越准但索引越大。常见选择:1km 精度用 geohash_precision=5(约 4.9km 边长),100m 用 6,10m 用 7。业务精度需求决定精度设置。geo 查询能和普通查询组合吗?可以,放在 bool query 的 filter 子句中。geo 查询不计算评分,适合做过滤条件配合全文检索使用。写段代码PUT /stores{ "mappings": { "properties": { "name": { "type": "keyword" }, "location": { "type": "geo_point" } } }}GET /stores/_search{ "query": { "bool": { "filter": { "geo_distance": { "distance": "5km", "location": { "lat": 39.9, "lon": 116.4 } } } } }}
服务端阅读 05月29日 00:51

持续集成、持续交付和持续部署有什么区别?

三者是递进关系:持续集成(CI)解决"代码能否合入"的问题——每次提交自动触发构建和测试,确保主分支始终可构建;持续交付(Continuous Delivery)解决"代码能否随时上线"的问题——在 CI 基础上自动化部署到预发布环境,但推送到生产环境需要人工审批门控;持续部署(Continuous Deployment)解决"代码能否自动上线"的问题——通过所有测试的变更直接部署到生产,无需人工干预。核心区别就在一个门:生产环境部署前是否有手动批准环节。选哪个取决于业务风险容忍度和测试覆盖成熟度。追问CI 中"每次提交都触发构建"在高频提交时如何避免流水线排队?持续交付中手动审批门控应该设在哪个环节?审批人需要关注什么?从持续交付升级到持续部署,测试覆盖率要达到什么水平才安全?蓝绿部署和金丝雀发布在持续部署中各起什么作用?CI/CD 流水线中安全扫描(SAST/DAST)应该放在哪个阶段?写段代码# GitHub Actions:CI + 持续交付流水线on: [push]jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm ci && npm test deploy-staging: needs: test runs-on: ubuntu-latest environment: staging # 自动部署到预发布 steps: - run: npx deploy-to staging
服务端阅读 05月29日 00:51

Cheerio 爬取动态页面为什么拿不到数据?怎么解决?

Cheerio 只是 HTML 解析器,不执行 JavaScript,所以通过 JS 动态渲染的内容(SPA、AJAX 加载、无限滚动)用 Cheerio 直接解析会拿到空壳 HTML。解决方案有三条路径:1) 用 Puppeteer/Playwright 先渲染页面拿到完整 HTML 再交给 Cheerio 解析;2) 拦截网络请求直接找到数据 API 端点,用 axios 请求 JSON;3) 分析页面源码中的内联数据(如 window.INITIAL_STATE)直接提取。追问方案 2(直接请求 API)相比方案 1 有什么优势?速度快、资源消耗低、无需启动浏览器。缺点是 API 可能有签名/鉴权,需要逆向分析请求参数。Puppeteer 和 Playwright 选哪个?Playwright API 更现代,原生支持多浏览器,自动等待机制更智能;Puppeteer 生态更成熟、Chrome 专项优化更好。新项目推荐 Playwright。如何优化 Puppeteer + Cheerio 方案的性能?拦截非必要资源(图片/字体/CSS)不加载,复用浏览器实例而非每次新建,设置合理的 waitForSelector 超时而非固定等待。怎么判断页面是动态渲染还是静态 HTML?curl 获取 HTML 后检查目标元素是否存在,如果 curl 拿不到但浏览器能看到,就是动态渲染。写段代码const puppeteer = require('puppeteer');const cheerio = require('cheerio');async function scrape(url) { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(url, { waitUntil: 'networkidle2' }); const $ = cheerio.load(await page.content()); await browser.close(); return $('.item').map((i, el) => $(el).text()).get();}
服务端阅读 05月29日 00:51

Cheerio 有哪些 DOM 操作方法?修改和读取分别怎么用?

Cheerio 的 DOM 操作分两大类:读取和修改。读取用 .text() .html() .attr() .val() 获取内容;修改用 .append() .prepend() .after() .before() 插入节点,.remove() .empty() 删除,.replaceWith() 替换。关键是区分内部插入(append/prepend,插入子节点)和外部插入(after/before,插入兄弟节点)。遍历用 .find() .children() .parent() .closest() .siblings() 在节点树中移动。追问append 和 after 有什么区别?.append() 是内部插入,把新节点加为目标元素的最后一个子节点;.after() 是外部插入,把新节点放在目标元素后面作为兄弟节点。记忆方式:append 是往肚子里塞,after 是站在旁边。remove 和 empty 有什么区别?.remove() 连元素本身一起删除;.empty() 只清空内容,元素标签保留。爬虫场景中 .empty() 常用于清空某个容器再重新填入清洗后的数据。怎么批量提取数据并转成数组?用 .map() + .get() 组合:$('.item').map((i, el) => $(el).text()).get()。.map() 返回 cheerio 对象,.get() 转为普通数组,否则无法用 Array 方法。.find() 和 .children() 有什么区别?.find() 搜索所有后代元素(含孙子节点),.children() 只找直接子元素。性能上 .children() 更快,层级确定时优先使用。怎么克隆一个节点并插入到其他位置?.clone() 深拷贝节点,返回独立的 cheerio 对象,可链式调用 .appendTo() 或 .insertAfter() 插入。注意 clone 不会复制 data 和事件绑定。写段代码const $ = cheerio.load(html);// 读取:提取所有文章标题和链接const articles = $('.post').map((i, el) => ({ title: $(el).find('h2').text(), link: $(el).find('a').attr('href')})).get();// 修改:给外链加 target 并移除广告$('a[href^="http"]').attr('target', '_blank');$('.ad').remove();
服务端阅读 05月29日 00:51

Elasticsearch 的 fielddata 和 doc_values 有什么区别?

fielddata 是基于 JVM 堆内存的倒排索引补充结构,仅在搜索时按需加载到内存,专用于 text 字段的聚合和排序,内存消耗不可控,易引发 OOM;docvalues 是基于磁盘的列式存储,索引时随文档写入持久化,默认对 keyword 和数值类型启用,不占堆内存,ES 5.x 后成为默认方案。核心区别:存储位置(堆 vs 磁盘)、加载时机(搜索时 vs 索引时)、内存风险(高 vs 低)、适用类型(text vs keyword/numeric)。生产环境中应优先用 docvalues,若字段不需聚合/排序可关闭 doc_values 节省磁盘。追问为什么 fielddata 容易 OOM?fielddata 将字段的全部唯一词项加载到 JVM 堆,高基数字段(如 UUID)可能占用数 GB。且被 LRU 缓存持有,不会主动释放,直到触发 circuit breaker。text 字段需要聚合怎么办?推荐用 multi-fields:主字段 text 做全文检索,子字段 keyword 启用 doc_values 做聚合。避免在 text 上开启 fielddata: true。关闭 doc_values 有什么影响?该字段将无法用于聚合、排序和脚本访问。如果字段只做全文检索不需要这些操作,关闭可节省约 10-15% 磁盘空间。doc_values 的列式存储如何加速聚合?列式存储按字段值连续排列,对同一字段的遍历只需顺序读磁盘,CPU 缓存命中率高。行式存储需跳行读取,I/O 散乱。ES 7.x 后还有必要了解 fielddata 吗?需要。排查旧索引的内存问题时仍需检查 fielddata 占用,GET _nodes/stats/indices?fielddata 可定位高消耗字段。写段代码PUT /products{ "mappings": { "properties": { "title": { "type": "text", "fields": { "keyword": { "type": "keyword", "doc_values": true } } }, "status": { "type": "keyword", "doc_values": true } } }}
服务端阅读 05月29日 00:50

DevOps 的核心概念和关键原则是什么?

DevOps 不是工具或职位,而是一种将开发与运维深度融合的文化实践。其核心可用 CAMLS 概括:Culture(打破部门壁垒、共担责任)、Automation(构建/测试/部署/监控全链路自动化)、Measurement(用 MTTR、部署频率等指标驱动改进)、Sharing(跨团队知识共享与无指责复盘)。关键原则包括基础设施即代码(IaC)保证环境一致性、CI/CD 流水线实现快速可靠交付、监控与可观测性保障生产稳定性,以及通过 blameless postmortem 从故障中学习而非追责。追问DevOps 中的"无指责复盘"(blameless postmortem)怎么做?如何避免流于形式?基础设施即代码(IaC)和传统运维脚本有什么本质区别?Terraform 和 Ansible 各自适用什么场景?如何度量 DevOps 转型的成效?DORA 四项关键指标分别是什么?小团队没有专职运维,如何落地 DevOps 实践?监控(Monitoring)和可观测性(Observability)有什么区别?写段代码# GitLab CI/CD 最小流水线示例stages: - test - deploytest: stage: test script: - npm install && npm testdeploy: stage: deploy script: - npx netlify deploy --prod only: - main
服务端阅读 05月29日 00:50

Cheerio 中如何高效使用选择器提取数据?

Cheerio 支持 CSS 选择器 + jQuery 扩展伪类,核心选择器分四类:基本(元素/ID/类)、层级(后代/子元素/兄弟)、属性(= ^= $= *=)、伪类(:first/:eq(n)/:contains()/:has())。高效用法的关键是缩小搜索范围:先选父容器再 .find(),缓存 $ 变量避免重复查找,用具体选择器替代通配符。追问.find() 和层级选择器 $('div p') 哪个更快?性能差异极小,但 .find() 可读性更好且支持链式调用,推荐优先使用。已缓存的父元素上 .find() 还能跳过全局搜索。如何获取元素的文本、属性和 HTML?.text() 取文本(含子元素文本拼接),.attr('href') 取属性值,.html() 取内部 HTML,.val() 取表单值。注意 .text() 会递归合并所有子节点文本。.each() 和 .map() 有什么区别?.each() 遍历不返回值(适合副作用操作),.map() 返回数组(适合数据提取)。.map() 需链式调用 .get() 才能得到原生数组。:contains() 和 .filter() 如何选择?:contains('text') 在选择器阶段过滤,写法简洁但不支持正则;.filter(fn) 在回调中可写任意判断逻辑,更灵活。写段代码const $ = cheerio.load(html);const $list = $('#article-list');const titles = $list.find('.title').map((i, el) => $(el).text().trim()).get();
服务端阅读 05月29日 00:50

Elasticsearch 如何优化写入性能?

核心思路是减少刷新频率、批量提交、降低副本开销。具体操作:将 refreshinterval 设为 -1 禁用自动刷新,写入完成后手动 refresh;使用 Bulk API 批量提交文档(建议 5-15MB 一批);写入期间将 numberofreplicas 设为 0,写完后恢复;调大 translog.flushthreshold_size 减少 flush 次数;合理路由使热点数据集中写入少数分片,避免跨节点协调开销。追问bulk 请求多大合适?建议单次 bulk 请求体 5-15MB,文档数不超 10000。过大易触发 GC 甚至 OOM,过小则网络开销占比高。用 BulkProcessor 可自动攒批提交。refresh_interval=-1 写完忘改回来怎么办?新写入文档对搜索不可见,但不丢数据。生产中可在索引模板中设置正常值,仅写入任务启动时临时覆盖。副本数为 0 风险多大?单节点故障会丢分片数据。建议仅在大批量初始导入时使用,写完立即恢复副本并等待 allocation 完成。translog 配置怎么调?将 translog.durability 改为 async,translog.syncinterval 设为 30s,flushthreshold_size 从默认 512MB 调到 1GB,减少磁盘 fsync 次数。冷热数据如何隔离写入?用 ILM 策略将新数据写入热节点(SSD),rollover 后迁移到冷节点(HDD)。hot 阶段设短 refresh_interval,cold 阶段恢复默认。写段代码PUT /logs-write{ "settings": { "refresh_interval": "-1", "number_of_replicas": 0, "translog": { "durability": "async", "sync_interval": "30s" } }}
服务端阅读 05月29日 00:50

Cheerio 使用中有哪些常见坑?怎么解决?

最常见的五大坑:①中文乱码——需要用 iconv-lite 按实际编码解码,别依赖 axios 自动处理;②选择器找不到元素——先 $.html() 检查 HTML 是否完整加载,再逐步缩小选择器范围调试;③拿不到动态内容——Cheerio 不执行 JS,要么换 Puppeteer 要么直接调后端 API;④大文件内存溢出——分批处理或流式解析,别一次全 load 进来;⑤XML 解析报错——必须加 { xmlMode: true } 选项,否则自闭合标签和命名空间都会出问题。追问中文乱码具体怎么处理?axios 设置 responseType: 'arraybuffer' 拿到原始字节,先从 Content-Type 或 meta 标签检测编码,再用 iconv.decode(buf, charset) 转码。GBK 页面不转码必乱。选择器没匹配到元素,排查步骤是什么?第一步 console.log($.html()) 看 HTML 是否完整;第二步从最外层选择器开始逐步缩小;第三步用 :contains() 按文本内容定位;第四步确认元素不在 iframe 或 shadow DOM 中。处理大文件怎么避免内存泄漏?分批 .slice(i, i+batchSize) 处理,每批处理完置空引用;更优方案是用 stream 按 </item> 分割,逐块 cheerio.load 解析,内存恒定。XML 模式和 HTML 模式有什么区别?xmlMode: true 会保留大小写(HTML 模式全部转小写)、保留自闭合标签、不补全缺失的闭合标签。解析 RSS/SVG/配置文件必须开启,否则数据丢失。提取的文本满是空白字符怎么办?.text() 结果链式调用 .replace(/\s+/g, ' ').trim(),或在 load 时加 { normalizeWhitespace: true },但后者会把换行也合并成空格,按需选择。写段代码// 正确处理 GBK 编码页面const resp = await axios.get(url, { responseType: 'arraybuffer' });const charset = resp.headers['content-type']?.match(/charset=([^;]+)/i)?.[1] || 'utf-8';const html = charset.toLowerCase() === 'utf-8' ? resp.data.toString() : iconv.decode(Buffer.from(resp.data), charset);const $ = cheerio.load(html);
服务端阅读 05月29日 00:50

Cheerio 加载 HTML 有哪几种方式?

核心方法是 cheerio.load(html, options),支持三种输入源:HTML 字符串、fs.readFileSync 读取的文件内容、Buffer。第二个参数控制解析行为:xmlMode 用于 XML 文档,decodeEntities 控制是否解码 HTML 实体(如 & → &),withDomLvl1 决定是否遵循 DOM Level1 规范。对大文件可用流式读取拼接后再 load。追问xmlMode 什么时候必须开启?解析 XML/RSS/Atom feed 时必须开启,否则标签大小写会被统一转小写,XML 的自闭合标签也会被错误处理。decodeEntities 设为 false 有什么用?保留原始 HTML 实体不解码,适合需要原样输出 HTML 的场景,如内容编辑器。默认 true 会把 & 转成 &。能直接加载 URL 吗?不能。Cheerio 是纯解析库,没有网络能力。需先用 axios/fetch 获取 HTML 字符串,再传入 load()。cheerio.load() 返回的 $ 是什么?返回一个函数,既可当选择器 $(selector) 使用,也挂载了 .html()、.xml() 等静态方法,本质是包装后的 DOM 操作入口。Buffer 输入和字符串输入有区别吗?功能一致,Buffer 会被内部自动转字符串。用 Buffer 可避免编码问题,尤其处理非 UTF-8 内容时更安全。写段代码const cheerio = require('cheerio');const $ = cheerio.load('<root><item>值</item></root>', { xmlMode: true, decodeEntities: false});console.log($('item').text()); // 值
服务端阅读 05月29日 00:50

如何用 Cheerio 实现网页爬虫和数据抓取?

核心流程三步走:用 axios/fetch 获取 HTML → cheerio.load() 加载 → 选择器提取数据。Cheerio 只负责解析,HTTP 请求需搭配其他库。关键要注意设置 User-Agent、处理相对 URL 转绝对路径、以及加入请求延迟避免被封禁。追问如何处理分页爬取?循环拼接页码参数逐页请求,每页之间加 1-2 秒延迟,收集数据后统一存储。注意检测是否有下一页(如"下一页"按钮或总页数标识)来决定终止条件。爬取的数据如何清洗?用 .trim() 去空白,正则移除 HTML 标签和特殊字符,parseFloat/parseInt 转数字类型,过滤空值和无效条目。如何实现请求重试机制?封装 fetchWithRetry 函数,失败后指数退避等待重试(1s→2s→4s),超过最大次数抛出异常。并发爬取如何控制?用 p-limit 等并发控制库限制同时请求数,或手动实现队列,避免瞬间大量请求导致 IP 被封。写段代码const axios = require('axios');const cheerio = require('cheerio');async function scrape(url) { const { data } = await axios.get(url); const $ = cheerio.load(data); const items = []; $('.item').each((i, el) => { items.push({ title: $(el).find('h2').text().trim() }); }); return items;}
服务端阅读 05月29日 00:50

Cheerio 和 Puppeteer 有什么区别?爬虫场景怎么选?

Cheerio 是纯 HTML 解析器,不执行 JavaScript,解析速度比 Puppeteer 快 100 倍以上,内存占用极低;Puppeteer 启动真实 Chromium,能执行 JS、处理动态渲染、模拟用户交互,但资源消耗大、速度慢。选择依据很简单:目标页面内容是否由 JS 动态生成——查看网页源码(Ctrl+U)能看到数据就用 Cheerio,看不到就用 Puppeteer。追问Puppeteer 能不能用 Cheerio 解析页面?可以,这是常见的混合模式:Puppeteer 负责加载动态页面拿到渲染后的 HTML,再传给 Cheerio 做数据提取,兼顾动态能力与解析效率。怎么判断一个网站是否需要 Puppeteer?右键查看网页源码,搜索目标数据。如果源码中没有,说明是 JS 动态加载,需要 Puppeteer;源码中已有,直接 Cheerio 即可。也可以用 curl 请求看返回的原始 HTML。Cheerio 能否处理需要登录的页面?Cheerio 本身只做解析,登录请求通过 axios/fetch 带 cookie 发送即可,不需要 Puppeteer。只有登录过程涉及 JS 渲染或验证码交互时才需要浏览器。两者的资源消耗差异有多大?Cheerio 解析一个页面通常 <10ms、内存 <50MB;Puppeteer 启动浏览器就要 500ms+、内存 100-300MB。批量爬取时差异是数量级的,100 个页面 Cheerio 几秒搞定,Puppeteer 可能要几分钟。写段代码// Puppeteer 拿动态页面 + Cheerio 解析const browser = await puppeteer.launch();const page = await browser.newPage();await page.goto(url, { waitUntil: 'networkidle2' });const html = await page.content();await browser.close();const $ = cheerio.load(html);$('.item').each((i, el) => { console.log($(el).text());});
服务端阅读 05月29日 00:50

Cheerio 和 jQuery 有什么区别?

Cheerio 是 Node.js 端的 HTML 解析库,实现了 jQuery 核心 API,但剥离了浏览器依赖——不渲染 DOM、不处理 CSS 布局、不执行 JavaScript,因此比 jQuery 轻量且解析速度快 8 倍以上。jQuery 运行在浏览器中操作真实 DOM,Cheerio 则在服务端解析 HTML 字符串构建虚拟 DOM 树,只做数据提取与结构操作。追问Cheerio 为什么比 jsdom 快?jsdom 会构建完整的浏览器环境(CSSOM、布局计算等),Cheerio 仅构建 DOM 树,跳过了渲染管线,所以解析速度快一个数量级。Cheerio 能替代 jQuery 做前端开发吗?不能。Cheerio 没有 render 树,修改 DOM 后不会触发页面重绘,也无法绑定事件,它只适合服务端数据提取场景。Cheerio 的底层解析器是什么?默认使用 htmlparser2,也可切换到 parse5。htmlparser2 速度更快,parse5 则更严格遵循 HTML 规范。什么场景下必须用 jQuery 而非 Cheerio?需要事件监听、CSS 动画、视口计算、用户交互等浏览器原生能力时,必须用 jQuery。写段代码const cheerio = require('cheerio');const $ = cheerio.load('<ul><li>A</li><li>B</li></ul>');$('li').each((i, el) => console.log($(el).text()));
前端阅读 05月29日 00:26

Bun 在 I/O 性能方面做了哪些优化?

Bun用Zig语言编写,I/O优化的核心是绕过libuv,直接走系统调用。具体优化:文件I/O使用Bun.file()API,底层通过mmap内存映射和直接syscall读写,Linux上利用io_uring实现异步,避免Node.js经libuv线程池的开销;HTTP服务用Bun.serve()构建,基于零拷贝响应和原生HTTP解析器(不用Node的http-parser),基准吞吐量高出Node.js约3倍;SQLite内置驱动直接编译进运行时,省去FFI调用开销;启动优化通过原生TypeScript执行(无需tsc编译步骤)和全局缓存减少冷启动I/O。总结:Node.js的I/O路径是JS→V8→libuv→OS,Bun是JS→JavaScriptCore→Zig syscall→OS,少了中间层。追问io_uring在Linux上的性能优势具体来自哪里?macOS上Bun用什么替代方案?Bun.serve的零拷贝是如何实现的?Response对象哪些场景下会触发拷贝?Bun.file()和Node.js的fs.promises.readFile在处理大文件时的内存占用差多少?内置SQLite驱动和better-sqlite3相比,性能差距主要在哪?Bun的HTTP客户端(Bun.fetch)也做了类似的零拷贝优化吗?写段代码// Bun: 零拷贝文件服务const file = Bun.file('./data.json');Bun.serve({ fetch: (req) => new Response(file), // 直接返回文件流,无需读取到内存 port: 3000});
前端阅读 05月29日 00:25

Dify 支持哪些类型的输入输出格式?如何自定义数据处理逻辑?

Dify工作流的输入支持:文本(短文本/段落)、结构化数据(下拉选择/数字/复选框/JSON)、文件上传(PDF/Word/TXT/Markdown,也支持图片和音频)。输出默认为LLM生成的文本,可配置为结构化JSON(通过JSON Schema约束)。自定义数据处理有三种方式:代码节点直接写Python/Node.js脚本做数据转换、JSON拼接、算术运算;模板节点用Jinja2语法灵活格式化输出文本;变量赋值节点对字符串/数字/数组进行覆盖、追加、扩展等操作。此外参数提取器能用LLM从自然语言中推理出结构化参数,迭代节点支持对数组批量处理。自定义工具还可通过OpenAPI/Swagger规范接入外部API。追问代码节点和模板节点分别适合什么场景?性能差异大吗?参数提取器如何保证提取结果的结构化可靠性?提示词怎么写?文件上传后Dify内部怎么处理?PDF解析用的是哪个库?迭代节点处理大数组时有没有并发或超时限制?自定义工具的OpenAPI Schema最大能定义多少个接口?写段代码# Dify代码节点:提取关键字段并格式化import jsondef main(input_text: str) -> dict: result = { "length": len(input_text), "summary": input_text[:100] } return {"result": json.dumps(result, ensure_ascii=False)}
前端阅读 05月29日 00:25

FFmpeg支持哪些常见的音视频格式?

FFmpeg支持100+容器格式和200+编解码器,需区分容器和编码两层。容器层主流格式:MP4(兼容性最广)、MKV(多音轨/字幕)、WebM(Web优化)、MOV(Apple生态)、FLV(直播推流)、TS(HLS切片)。视频编码:H.264/AVC(最通用)、H.265/HEVC(同质量体积减半)、VP9(WebM默认)、AV1(开源下一代,FFmpeg 5.0+支持)。音频编码:AAC(流媒体标配)、Opus(低延迟实时通信最优)、MP3(兼容旧设备)、FLAC(无损)。关键认知:容器决定封装结构,编码决定压缩算法,同一容器可装不同编码(如MP4可装H.264或H.265)。用ffmpeg -encoders查看本地支持的编码器列表。追问H.264和H.265在FFmpeg中用什么编码器?libx264和h264_nvenc性能差多少?MP4容器能装VP9视频吗?为什么WebM比MP4更适合Web场景?Opus相比AAC在延迟和码率上有什么优势?为什么WebRTC选Opus?FLV容器为什么逐步被淘汰?HLS的TS切片方案解决了FLV的什么问题?ffmpeg -codecs和ffmpeg -encoders输出有什么区别?写段代码# 查看本地支持的所有H.265编码器ffmpeg -encoders 2>/dev/null | grep 265# MP4转WebM(VP9+Opus)ffmpeg -i in.mp4 -c:v libvpx-vp9 -crf 30 -b:v 0 -c:a libopus out.webm
服务端阅读 05月29日 00:25

Cypress 中如何实现数据驱动测试?

数据驱动测试将测试数据与逻辑分离,Cypress 通过 cy.fixture() 加载 cypress/fixtures/ 下的 JSON 文件驱动测试。核心流程:在 fixtures 目录建数据文件,测试中用 cy.fixture() 加载后遍历执行,实现一组逻辑跑多组数据。更简洁的方式是结合 cy.each() 或原生 forEach 迭代数据,避免为每组数据写重复测试。外部数据文件适合管理多环境配置和边界值数据集,fixtures 适合静态模拟数据。追问cy.fixture() 和直接 import JSON 有什么区别?cy.fixture() 走 Cypress 管道,支持超时重试和命令日志;import 是编译时加载,不经过 Cypress 命令链,无法在报告中追踪。如何用 fixtures 实现参数化测试?用 cy.fixture() 加载数组数据,配合 cy.each() 或 forEach 遍历,每组数据生成独立 it 用例,失败时能精确定位是哪组数据有问题。fixtures 数据在不同测试间会互相影响吗?Cypress 默认每个测试前重置 fixtures 状态;但如果在 before 中修改 fixture 返回的对象,会影响后续测试,建议每次加载用深拷贝。大量测试数据该怎么管理?按模块分目录(fixtures/auth/、fixtures/products/),公共数据放 fixtures/common/;环境相关数据用 cypress.env.json + CYPRESS_ 环境变量区分。写段代码// fixtures/users.json: [{"name":"Alice","role":"admin"},{"name":"Bob","role":"user"}]describe('数据驱动权限测试', () => { let users; before(() => { cy.fixture('users').then(data => users = data); }); users.forEach((user, i) => { it(`用户 ${user.name} 角色为 ${user.role}`, () => { cy.login(user.name, 'pass'); cy.get('[data-testid=role]').should('contain', user.role); }); });});
前端阅读 05月29日 00:25

Dify 的部署方式有哪些?分别适用于哪些场景?

Dify提供三种主流部署方式:云服务(SaaS)即开即用,适合快速验证和中小团队,无需运维但数据存于Dify服务器;Docker自托管通过docker compose up一键拉起,适合需要数据自主可控的企业,是最常用的生产部署方案;Kubernetes集群部署基于Helm Chart编排,适合高可用、弹性伸缩和大规模并发场景。此外还有社区版源码部署,适合需要深度定制或二次开发的场景。选择依据:数据敏感选自托管,快速上手选云服务,企业级生产选K8s。Dify自托管依赖PostgreSQL、Redis和Nginx,Docker Compose方案已包含全部依赖。追问Docker自托管和K8s部署在资源开销上差多少?10人团队该选哪个?云服务版的数据隔离机制是什么?多租户场景下知识库数据是否会交叉?自托管升级版本时如何做到零停机?数据库迁移脚本谁负责执行?Dify的API扩展点和插件系统在不同部署方式下有差异吗?混合部署(敏感数据本地+推理云端)在Dify中如何实现?写段代码# docker-compose 自托管最小配置services: api: image: langgenius/dify-api:latest environment: DB_USERNAME: postgres DB_PASSWORD: ${DB_PW} REDIS_HOST: redis web: image: langgenius/dify-web:latest ports: ["80:80"]