乐闻世界logo
搜索文章和话题

服务端面试题手册

Cookie 的 Domain 和 Path 属性有什么作用?如何正确设置?

Cookie 的 Domain 和 Path 属性用于控制 Cookie 的作用范围,正确设置这些属性对于安全性和功能实现都很重要。Domain 属性作用指定 Cookie 有效的域名控制哪些子域名可以访问该 Cookie设置规则// 当前域名为 www.example.com// 1. 不设置 Domain(默认)document.cookie = "token=abc";// 只有 www.example.com 可以访问// 2. 设置为当前域名document.cookie = "token=abc; Domain=www.example.com";// 只有 www.example.com 可以访问// 3. 设置为父域名(带点前缀)document.cookie = "token=abc; Domain=.example.com";// 所有子域名都可以访问(www.example.com, api.example.com 等)// 4. 错误示例:设置为其他域名document.cookie = "token=abc; Domain=other.com";// 浏览器会忽略此设置注意事项Domain 必须是当前域名的父域名或相同域名设置父域名时需要带点前缀(.example.com)不能设置为完全不同的域名Path 属性作用指定 Cookie 有效的路径控制哪些 URL 路径可以访问该 Cookie设置规则// 当前域名为 www.example.com// 1. 不设置 Path(默认)document.cookie = "token=abc";// 只有当前路径及其子路径可以访问// 2. 设置为根路径document.cookie = "token=abc; Path=/";// 整个域名下的所有路径都可以访问// 3. 设置为特定路径document.cookie = "token=abc; Path=/api";// 只有 /api 及其子路径可以访问(/api/users, /api/data 等)// 4. 设置为父路径document.cookie = "token=abc; Path=/admin";// 只有 /admin 及其子路径可以访问匹配规则Cookie 只在指定的路径及其子路径下发送路径匹配是前缀匹配更具体的路径优先级更高组合使用示例// 场景 1:全站通用 Cookiedocument.cookie = "theme=dark; Domain=.example.com; Path=/";// 场景 2:仅 API 使用的 Cookiedocument.cookie = "apiToken=xyz; Domain=.example.com; Path=/api";// 场景 3:管理后台专用 Cookiedocument.cookie = "adminToken=123; Domain=admin.example.com; Path=/admin";安全考虑最小权限原则// 不推荐:过于宽泛document.cookie = "token=abc; Domain=.example.com; Path=/";// 推荐:限制范围document.cookie = "token=abc; Domain=api.example.com; Path=/api/v1";防止 Cookie 泄露敏感 Cookie 应限制在特定路径避免在静态资源路径设置 Cookie不同功能的 Cookie 使用不同路径实际应用场景单点登录(SSO)// 在认证域名设置document.cookie = "ssoToken=xyz; Domain=.example.com; Path=/";// 所有子域名共享登录状态多环境隔离// 开发环境document.cookie = "token=dev; Domain=.dev.example.com; Path=/";// 生产环境document.cookie = "token=prod; Domain=.example.com; Path=/";功能模块隔离// 用户模块document.cookie = "userToken=abc; Path=/user";// 支付模块document.cookie = "paymentToken=xyz; Path=/payment";
阅读 0·3月7日 12:25

WebRTC如何与其他技术集成?例如与Socket.io、Node.js等后端技术的结合。

WebRTC可以与多种技术集成,以构建完整的实时通信应用:WebRTC与Socket.io集成:使用Socket.io作为信令服务器: // 客户端 const socket = io('https://your-signaling-server.com'); // 发送信令消息 socket.emit('offer', { offer, roomId }); // 接收信令消息 socket.on('answer', (data) => { peerConnection.setRemoteDescription(new RTCSessionDescription(data.answer)); }); socket.on('ice-candidate', (data) => { peerConnection.addIceCandidate(new RTCIceCandidate(data.candidate)); });服务端实现: // Node.js服务端 const io = require('socket.io')(server); io.on('connection', (socket) => { socket.on('join-room', (roomId) => { socket.join(roomId); // 通知房间内其他用户 socket.to(roomId).emit('user-joined', socket.id); }); socket.on('offer', (data) => { socket.to(data.roomId).emit('offer', { offer: data.offer, from: socket.id }); }); socket.on('answer', (data) => { socket.to(data.roomId).emit('answer', { answer: data.answer, from: socket.id }); }); socket.on('ice-candidate', (data) => { socket.to(data.roomId).emit('ice-candidate', { candidate: data.candidate, from: socket.id }); }); });WebRTC与Node.js集成:使用Node.js构建信令服务器:Express + Socket.io:快速构建信令服务Koa + WebSocket:轻量级信令服务使用Node.js构建TURN服务器:coturn:开源的TURN服务器实现可以通过Node.js的child_process模块管理coturn进程使用Node.js处理媒体服务器功能:mediasoup:支持SFU(选择性转发单元)架构的媒体服务器Janus:功能丰富的WebRTC网关WebRTC与其他技术的集成:与React/Vue/Angular集成:使用组件化思想封装WebRTC逻辑状态管理库(如Redux/Vuex)管理连接状态与移动应用集成:WebRTC Mobile SDK:iOS和Android原生实现React Native + WebRTC:跨平台移动应用与云服务集成:AWS Chime:基于WebRTC的会议服务Google Meet:使用WebRTC技术Azure Communication Services:提供WebRTC集成集成的最佳实践:模块化设计:将WebRTC逻辑与业务逻辑分离错误处理:实现完善的错误处理机制监控与日志:记录连接状态和性能指标可扩展性:设计支持多用户、多房间的架构
阅读 0·3月7日 12:24

WebView中如何拦截和修改网络请求?

WebView中的网络请求拦截可以通过以下方式实现:Android实现方式:shouldInterceptRequest方法: webView.setWebViewClient(new WebViewClient() { @Override public WebResourceResponse shouldInterceptRequest(WebView view, String url) { // 拦截请求,返回自定义响应 return new WebResourceResponse("text/html", "utf-8", inputStream); } @Override public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { // 更详细的请求信息 String url = request.getUrl().toString(); String method = request.getMethod(); Map<String, String> headers = request.getRequestHeaders(); // 处理请求... } });iOS实现方式:WKNavigationDelegate: func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { // 拦截导航请求 if let url = navigationAction.request.url { // 处理URL... } decisionHandler(.allow) }URLProtocol(已废弃):可以拦截所有网络请求需要注册自定义URLProtocol注意:WKWebView不支持URLProtocol应用场景:资源替换:替换本地资源,减少网络请求请求修改:修改请求头、请求参数响应修改:修改响应内容缓存控制:实现自定义缓存策略安全过滤:拦截恶意请求数据统计:统计网络请求Mock数据:开发阶段使用Mock数据注意事项:性能影响:拦截会增加请求处理时间内存管理:注意及时释放资源线程安全:在正确的线程处理请求错误处理:妥善处理异常情况HTTPS证书:处理自签名证书等问题高级用法:使用OkHttp拦截器(Android)实现请求重试机制实现请求优先级控制实现请求去重实现请求限流
阅读 0·3月7日 12:24

Cypress 如何处理 iframe 和多窗口测试?

在现代Web应用开发中,iframe嵌套和多窗口场景是常见的复杂页面结构。Cypress,作为一款流行的端到端测试框架,提供了强大的工具来处理这些挑战。然而,许多测试工程师在实践中常因未正确处理iframe或窗口切换而导致测试失败。本文将深入解析Cypress如何高效管理iframe和多窗口测试,结合实际案例提供可操作的解决方案,帮助提升自动化测试的可靠性和覆盖率。为什么需要处理iframe和多窗口iframe是HTML中用于嵌入其他网页的容器,常用于第三方服务集成(如视频播放器或登录弹窗)。多窗口场景则涉及浏览器中的新标签页或弹出窗口。若测试脚本未正确处理这些元素,会导致定位失效、操作超时或测试误判。例如,尝试直接操作iframe内的元素会抛出cannot read property 'type' of undefined错误,而窗口切换不当可能遗漏关键交互。根据Cypress官方文档,约30%的自动化测试失败源于此类问题,因此掌握处理技巧至关重要。Cypress处理iframe的方法Cypress通过原生API和链式调用无缝集成iframe操作,核心在于内容文档(contentDocument)的访问。使用cy.get()选择iframe首先,需定位iframe元素。Cypress支持标准CSS选择器,但需注意iframe可能动态加载。// 正确选择iframe(示例:使用id属性)// 注意:确保选择器精确匹配iframe元素const iframe = cy.get('#my-iframe');// 通过链式调用获取iframe内容iframe.its('0.contentDocument.body').should('be.visible');关键点:its('0.contentDocument.body') 通过0索引获取第一个iframe的contentDocument,避免元素未加载时的异常。实践建议:使用cy.get()后添加.should('be.visible')确保iframe已渲染,防止测试跳过加载阶段。操作iframe内的元素一旦获取到contentDocument,即可操作其内部元素。常见操作包括定位、输入和断言。// 操作iframe内的输入框(示例)// 确保iframe已加载,否则需使用重试机制iframe.its('0.contentDocument.body').then(($body) => { cy.wrap($body).find('input[type="text"]').type('test');});// 断言iframe内容iframe.its('0.contentDocument.body').should('contain.text', 'Welcome');注意事项:直接操作iframe内容时,Cypress会自动处理跨域限制(若同源则无需额外配置),但需确保测试环境支持。常见问题:若iframe内容异步加载,建议添加cy.wait()或使用.then()回调处理。常见问题与解决方案| 问题 | 解决方案 || --------- | ------------------------------------------------------------------------- || iframe未加载 | 使用cy.get('iframe').its('0.contentDocument.body').should('be.visible')验证 || 元素定位失效 | 通过cy.wrap()封装contentDocument,确保上下文正确 || 多iframe场景 | 使用索引(如0)或属性过滤(如[src="https://example.com"])指定目标 | 专业见解:Cypress的its()方法是处理iframe的核心,它避免了手动DOM操作。在实践中,推荐将iframe操作封装为自定义命令(如cy.withIframe('id', () => { ... })),以提升可维护性。Cypress处理多窗口的方法多窗口测试涉及浏览器窗口切换,Cypress提供cy.window()和cy.wrap()等API,但需谨慎处理窗口生命周期。获取当前窗口Cypress默认操作当前窗口,但需显式获取其他窗口。常用方法包括:// 获取当前窗口对象const currentWindow = cy.window();// 通过事件监听新窗口(示例:处理弹出窗口)cy.on('window:load', (window) => { // 窗口加载时执行操作 cy.wrap(window).its('location.href').should('include', '/login');});关键点:cy.window()返回当前窗口的JavaScript对象,允许访问document、location等属性。实践建议:在测试前使用cy.get('a[href="#login"]').click()触发新窗口时,确保Cypress能捕获事件(需启用--disable-web-security参数)。切换窗口切换窗口需使用cy.window()结合索引或标题过滤。// 切换到新标签页(示例)// 步骤:1. 触发新窗口 2. 获取窗口列表 3. 切换到目标cy.get('a[href="#new-tab"]').click();// 等待新窗口加载(推荐使用cy.get())cy.window().then((win) => { const newWindow = win.parentWindow; // 通过parentWindow访问新窗口 cy.wrap(newWindow).its('location.href').should('be.equal', 'https://example.com');});注意事项:Cypress自动处理窗口切换,但需确保测试环境配置--disable-web-security以避免安全限制。常见错误:直接操作window.open()时,需添加cy.wait()防止测试卡顿。交互与断言窗口测试常涉及跨窗口操作,例如点击按钮后验证新窗口内容。// 示例:点击按钮后验证新窗口// 步骤:1. 点击触发 2. 切换窗口 3. 断言内容cy.get('#open-window-btn').click();// 获取新窗口并断言cy.get('[data-testid="new-window"]', { timeout: 10000 }).should('exist');专业技巧:使用cy.get()的{ timeout }参数避免超时,结合cy.wait()确保异步操作完成。实践建议预处理测试数据:在测试前加载iframe资源,减少等待时间(例如使用cy.intercept()模拟API响应)。封装常用模式:创建自定义命令如cy.switchWindow('title')简化窗口切换,提升代码复用性。调试技巧:使用Cypress.run()在浏览器中调试,通过console.log()检查iframe内容。避免常见陷阱:不要在测试中使用window.open(),而是通过Cypress事件监听(如window:load)管理窗口。性能优化:对动态iframe使用cy.get('iframe').its('0.contentDocument.body').should('be.visible')替代cy.get(), 以减少执行时间。 权威参考:Cypress官方文档明确指出,处理iframe时应始终优先使用its()方法,而非直接DOM操作。测试工具链推荐搭配cypress-iframe插件,它提供额外的简化API,如cy.iframe().find('input')。结论Cypress通过其原生API为iframe和多窗口测试提供了高效解决方案,但需结合最佳实践以避免常见陷阱。本文详细解析了处理流程、代码示例和实用建议,旨在帮助开发者构建更可靠的自动化测试。记住:测试前验证元素加载状态、封装操作逻辑、并持续监控测试报告,是成功处理复杂页面结构的关键。随着Web应用复杂度提升,掌握这些技术将显著提升测试覆盖率和效率。​
阅读 0·3月7日 12:16

pnpm 的 shamefully-hoist 配置是什么?什么时候需要使用?

shamefully-hoist 是 pnpm 的一个配置选项,用于创建类似 npm/Yarn 的扁平化 node_modules 结构。默认行为 vs shamefully-hoist:# 默认 pnpm 结构(严格)node_modules/├── .pnpm/│ └── lodash@4.17.21/└── lodash -> .pnpm/lodash@4.17.21/node_modules/lodash# 只能访问声明的依赖const lodash = require('lodash'); // ✅ 正常const debug = require('debug'); // ❌ 错误(未声明)# shamefully-hoist=true 结构(扁平化)node_modules/├── .pnpm/├── lodash/├── debug/ # 被提升上来└── ...# 可以访问所有依赖const lodash = require('lodash'); // ✅ 正常const debug = require('debug'); // ✅ 可以访问(幽灵依赖)配置方法:# .npmrcshamefully-hoist=true# 或只提升特定包shamefully-hoist-pattern[]=webpackshamefully-hoist-pattern[]=*types*使用场景:遗留项目迁移# 项目依赖幽灵依赖,暂时无法修改# .npmrcshamefully-hoist=true# 迁移完成后应关闭# shamefully-hoist=false特定工具兼容# 某些工具需要扁平化结构# 如某些 webpack 插件、IDE 等shamefully-hoist=true混合使用# 只提升特定包public-hoist-pattern[]=*eslint*public-hoist-pattern[]=*prettier*public-hoist-pattern[]=*types*相关配置对比:| 配置 | 作用 | 推荐度 ||------|------|--------|| shamefully-hoist | 完全扁平化 | ⭐⭐ || public-hoist-pattern | 部分扁平化 | ⭐⭐⭐⭐⭐ || hoist-pattern | 内部扁平化 | ⭐⭐⭐⭐ |public-hoist-pattern 推荐配置:# .npmrc# 提升类型定义包public-hoist-pattern[]=*types*# 提升构建工具public-hoist-pattern[]=*eslint*public-hoist-pattern[]=*prettier*public-hoist-pattern[]=*webpack*# 提升测试工具public-hoist-pattern[]=*jest*public-hoist-pattern[]=*vitest*为什么不推荐完全扁平化:// shamefully-hoist=true 的问题// 1. 幽灵依赖const someDep = require('some-dep');// 实际未在 package.json 中声明// 可能导致 CI/CD 失败// 2. 版本冲突// 不同包依赖同一包的不同版本// 扁平化后只能有一个版本// 3. 失去 pnpm 的优势// 磁盘空间节省减少// 依赖管理不严格最佳实践:# 推荐配置# .npmrc# 默认不扁平化shamefully-hoist=false# 只提升必要的包public-hoist-pattern[]=*eslint*public-hoist-pattern[]=*prettier*public-hoist-pattern[]=*types*# 严格模式strict-peer-dependencies=true迁移策略:# 1. 先启用 shamefully-hoist 迁移# .npmrcshamefully-hoist=true# 2. 运行项目,检查是否有问题pnpm installpnpm buildpnpm test# 3. 逐步修复幽灵依赖# 找到所有未声明的依赖pnpm ls --depth=10# 4. 添加到 package.jsonpnpm add missing-dep# 5. 关闭 shamefully-hoist# .npmrcshamefully-hoist=false检查幽灵依赖:# 使用 depcheck 检查cd projectnpx depcheck# 或使用 pnpm 检查pnpm ls --depth=0总结:shamefully-hoist 是迁移过渡方案长期使用应关闭,保持 pnpm 的严格性使用 public-hoist-pattern 替代完全扁平化逐步修复幽灵依赖,回归标准模式
阅读 0·3月7日 12:16

Nuxt.js 应用如何部署和托管?有哪些推荐的部署方案?

Nuxt.js 应用的部署和托管是开发过程中的重要环节,选择合适的部署方案可以确保应用的稳定性和性能。以下是 Nuxt.js 应用的部署和托管方案。部署方式:服务器端渲染 (SSR) 部署需要:Node.js 服务器环境构建命令:nuxt build启动命令:nuxt start适用场景:需要实时数据和服务器端渲染的应用静态站点生成 (SSG) 部署需要:静态文件服务器构建命令:nuxt generate部署产物:dist 目录中的静态文件适用场景:内容变化不频繁的网站,如博客、企业官网单页应用 (SPA) 部署需要:静态文件服务器构建命令:nuxt build --spa部署产物:dist 目录中的静态文件适用场景:不需要 SEO 的内部应用或管理系统托管平台:云服务提供商Vercel:Nuxt.js 官方推荐,支持自动部署和预览Netlify:支持静态站点和 SSR 部署,集成 CI/CDHeroku:支持 Node.js 应用部署AWS:提供 EC2、S3、Lambda 等多种部署选项Google Cloud Platform:提供 App Engine、Cloud Functions 等服务Azure:提供 App Service、Static Web Apps 等服务传统服务器Nginx + Node.js:使用 Nginx 作为反向代理,Node.js 运行 Nuxt 应用PM2:用于管理 Node.js 进程,提供负载均衡和自动重启容器化部署Docker:将应用打包为容器,便于部署和扩展Kubernetes:用于管理容器化应用的编排系统部署步骤:SSR 部署步骤:构建应用:npm run build准备环境变量和配置文件部署到服务器启动应用:npm run start配置反向代理(如 Nginx)设置域名和 SSLSSG 部署步骤:构建应用:npm run generate部署 dist 目录到静态文件服务器配置 CDN 加速(可选)设置域名和 SSL最佳实践:环境配置使用 .env 文件管理环境变量区分开发、测试和生产环境敏感信息不要硬编码在代码中CI/CD 集成配置 GitHub Actions、GitLab CI 等 CI/CD 工具实现自动化构建和部署集成测试和代码质量检查性能优化启用 gzip 或 brotli 压缩使用 CDN 分发静态资源配置合理的缓存策略监控和日志集成应用性能监控工具配置日志收集和分析设置错误报警机制安全措施使用 HTTPS配置 CORS 和 CSP定期更新依赖实施安全扫描常见部署问题及解决方案:端口占用检查是否有其他进程占用端口修改配置文件中的端口设置环境变量未生效确保环境变量正确设置检查构建过程中是否正确加载环境变量静态资源 404检查 publicPath 配置确保静态资源路径正确服务器端错误查看服务器日志获取详细错误信息检查依赖是否正确安装部署后页面空白检查路由配置查看浏览器控制台错误信息部署工具推荐:Vercel:最简单的 Nuxt.js 部署方案Netlify:适合静态站点和 SSR 部署PM2:用于管理 Node.js 进程Docker:容器化部署GitHub Actions:自动化 CI/CD成本考虑:静态站点:部署成本低,可使用免费静态托管服务SSR 应用:需要运行 Node.js 服务器,成本较高CDN 费用:根据流量收费,需合理规划扩展性考虑:水平扩展:使用负载均衡,增加服务器实例垂直扩展:增加服务器资源(CPU、内存)缓存策略:使用 Redis 等缓存服务减少服务器负载
阅读 0·3月7日 12:15

Nuxt.js 中如何处理错误和进行调试?

在 Nuxt.js 应用开发中,有效的错误处理和调试是确保应用稳定性和开发效率的关键。以下是 Nuxt.js 中错误处理和调试的方法。错误处理策略:页面级错误处理错误页面:创建 layouts/error.vue 组件处理全局错误错误布局:自定义错误页面的样式和内容示例:<!-- layouts/error.vue --><template> <div class="error-page"> <h1 v-if="error.statusCode === 404">页面不存在</h1> <h1 v-else>服务器错误</h1> <p>{{ error.message }}</p> <nuxt-link to="/">返回首页</nuxt-link> </div></template><script>export default { props: ['error'], layout: 'blank' // 使用空白布局}</script>数据获取错误处理asyncData 错误:使用 try-catch 捕获错误fetch 错误:使用 try-catch 捕获错误示例:export default { async asyncData({ params, $axios, error }) { try { const data = await $axios.$get(`/api/users/${params.id}`) return { user: data } } catch (err) { error({ statusCode: 500, message: '获取用户数据失败' }) } }}中间件错误处理在中间件中捕获和处理错误可以重定向到错误页面或登录页面插件错误处理在插件中添加错误处理逻辑避免插件错误导致整个应用崩溃全局错误处理使用 window.onerror 捕获客户端错误使用 process.on('unhandledRejection') 捕获未处理的 Promise 错误调试方法:开发工具Vue DevTools:用于调试 Vue 组件和状态Chrome DevTools:用于网络请求、性能分析等Nuxt DevTools:Nuxt 专用的开发工具日志记录服务器端日志:使用 console.log 或专业日志库客户端日志:使用 console.log 或前端日志库错误监控:集成 Sentry 等错误监控服务调试模式使用 nuxt dev 启动开发服务器启用 source maps 便于调试配置 nuxt.config.js 中的 debug 选项环境变量使用 .env 文件管理环境变量区分开发、测试和生产环境示例:// nuxt.config.jsrequire('dotenv').config()export default { env: { API_BASE_URL: process.env.API_BASE_URL || 'http://localhost:3000/api' }}性能调试使用 Lighthouse 分析性能使用 Chrome DevTools 的 Performance 面板使用 nuxt build --analyze 分析构建产物最佳实践:错误处理为所有异步操作添加错误处理提供友好的错误提示给用户记录错误信息便于排查调试技巧使用 console.log 和 console.debug 输出调试信息使用断点调试复杂逻辑利用 Vue DevTools 检查组件状态错误监控集成 Sentry 等错误监控服务设置错误报警机制定期分析错误日志开发环境配置启用详细的错误信息配置热重载提升开发效率使用 ESLint 和 Prettier 保持代码质量常见错误及解决方案:404 错误检查路由配置是否正确确保页面文件存在检查动态路由参数是否有效500 错误检查服务器端代码是否有错误检查 API 请求是否正常查看服务器日志获取详细错误信息数据获取错误检查 API 地址是否正确检查网络连接是否正常检查权限是否足够构建错误检查依赖是否正确安装检查代码是否有语法错误检查 webpack 配置是否正确调试工具推荐:Vue DevTools:调试 Vue 组件和状态Sentry:错误监控和跟踪Lighthouse:性能分析和优化建议Chrome DevTools:网络请求和性能分析Nuxt DevTools:Nuxt 专用开发工具
阅读 0·3月7日 12:15

区块链扩容方案有哪些?详解 Layer 2、分片技术和侧链原理

区块链不可能三角(Blockchain Trilemma):去中心化(Decentralization)安全性(Security)可扩展性(Scalability)三者无法同时最大化,扩容方案旨在平衡这三者。扩容方案分类扩容方案├── Layer 1(链上扩容)│ ├── 增大区块大小│ ├── 缩短出块时间│ └── 分片技术(Sharding)│└── Layer 2(链下扩容) ├── 状态通道(State Channels) ├── 侧链(Sidechains) ├── Plasma ├── Rollups │ ├── Optimistic Rollups │ └── ZK Rollups └── ValidiumLayer 1 扩容方案1. 分片技术(Sharding)原理:将网络分割成多个并行运行的子网络(分片),每个分片独立处理交易。传统区块链: 分片区块链:┌─────────────┐ ┌─────┬─────┬─────┐│ 单一链 │ │ 分片1│ 分片2│ 分片3││ 处理所有 │ → │处理 │处理 │处理 ││ 交易 │ │交易A│交易B│交易C││ TPS: 15 │ └─────┴─────┴─────┘└─────────────┘ TPS: 15×3=45以太坊 2.0 分片设计:信标链(Beacon Chain):协调各分片64 个数据分片:并行处理交易交联(Crosslinks):分片间通信优点:✅ 线性提升吞吐量✅ 保持去中心化缺点:❌ 跨分片交易复杂❌ 实现难度大Layer 2 扩容方案1. 状态通道(State Channels)原理:链下建立通道进行多次交易,只在开启和关闭时与主链交互。状态通道流程:1. 开启通道 Alice ──锁定 10 ETH──→ 智能合约 ←──锁定 10 ETH── Bob ↓ 链上交易2. 链下交易(多次,零 Gas) Alice ──签署状态──→ Bob Bob ──签署状态──→ Alice (每次更新余额分配)3. 关闭通道 提交最终状态到链上,合约按最终状态分配资金代表项目:闪电网络(Bitcoin)、雷电网络(Ethereum)适用场景:小额高频支付2. Rollups(卷叠)原理:在链下执行交易,将交易数据压缩后提交到主链。Rollup 架构:链下执行层 链上验证层┌───────────┐ ┌───────────┐│ 排序器 │ │ Rollup ││ (Sequencer)│ │ 合约 ││ │ │ ││ • 接收交易 │ ──→ │ • 存储压缩 ││ • 执行交易 │ │ 交易数据 ││ • 生成证明 │ │ • 验证状态 ││ • 压缩数据 │ │ 根 │└───────────┘ └───────────┘ ↑ ↑ 高 TPS 以太坊安全性 低成本Optimistic Rollups(乐观卷叠)原理:假设交易有效,通过欺诈证明(Fraud Proof)机制挑战无效交易。Optimistic Rollup 流程:1. 排序器打包交易,提交到 L12. 7 天挑战期(Withdrawal Period)3. 期间任何人可提交欺诈证明4. 无挑战则交易最终确认代表项目:Arbitrum、Optimism优点:✅ EVM 兼容性好✅ 开发迁移成本低缺点:❌ 7 天提款延迟❌ 需要信任假设ZK Rollups(零知识卷叠)原理:使用零知识证明(ZK-SNARKs/STARKs)验证交易有效性。ZK Rollup 流程:1. 链下执行大量交易2. 生成有效性证明(Validity Proof)3. 提交证明和状态根到 L14. 智能合约验证证明5. 立即确认,无需等待期代表项目:zkSync、StarkNet、Polygon zkEVM优点:✅ 即时最终性✅ 更高的安全性(密码学保证)✅ 更快的提款速度缺点:❌ 开发复杂度高❌ 计算成本较高3. 侧链(Sidechains)原理:独立的区块链,通过双向锚定与主链交互。侧链架构:┌─────────────┐ 双向锚定 ┌─────────────┐│ 以太坊 │ ←────────────────────→ │ 侧链 ││ 主链 │ • 资产锁定/释放 │ (Polygon/ ││ │ • 状态验证 │ xDai) ││ 高安全性 │ │ 高吞吐量 ││ 低 TPS │ │ 低安全性 │└─────────────┘ └─────────────┘代表项目:Polygon PoS、xDai与 Rollup 的区别:侧链有自己的共识机制不继承主链安全性验证者集较小扩容方案对比| 方案 | TPS | 安全性 | 提款时间 | EVM 兼容 | 代表项目 || ----------------- | ------ | ----- | ---- | ------ | --------- || 以太坊主网 | 15 | ⭐⭐⭐⭐⭐ | - | ✅ | Ethereum || 状态通道 | 无限 | ⭐⭐⭐⭐ | 即时 | ❌ | Lightning || Optimistic Rollup | 2K-4K | ⭐⭐⭐⭐ | 7 天 | ✅ | Arbitrum || ZK Rollup | 2K-10K | ⭐⭐⭐⭐⭐ | 分钟级 | ✅/❌ | zkSync || 侧链 | 7K+ | ⭐⭐⭐ | 分钟级 | ✅ | Polygon || 分片(未来) | 100K+ | ⭐⭐⭐⭐⭐ | - | ✅ | ETH 2.0 |面试要点理解区块链不可能三角的含义掌握 Layer 1 和 Layer 2 的区别能够解释 Optimistic Rollup 和 ZK Rollup 的核心差异了解状态通道的适用场景理解分片技术的挑战知道不同方案的权衡取舍
阅读 0·3月7日 12:14

Android中Handler机制的工作原理是什么?

Handler是Android中实现线程间通信的核心机制,用于在工作线程和主线程(UI线程)之间传递消息。Handler机制的核心组件1. Handler(处理器)作用:发送和处理消息特点:绑定到创建它的线程的Looper负责发送Message到MessageQueue处理Looper分发的消息2. Looper(循环器)作用:消息循环,不断从MessageQueue取出消息特点:每个线程只能有一个Looper主线程默认已创建Looper子线程需要手动调用Looper.prepare()和Looper.loop()3. MessageQueue(消息队列)作用:存储消息的队列特点:先进先出(FIFO)按时间排序(when字段)底层使用单链表实现4. Message(消息)作用:携带数据和信息的载体属性:what:消息标识arg1/arg2:整型参数obj:对象参数callback:Runnable回调Handler工作流程发送消息流程:Handler.sendMessage() → MessageQueue.enqueueMessage() → 消息入队处理消息流程:Looper.loop() → MessageQueue.next() → Handler.dispatchMessage() → Handler.handleMessage()代码示例// 主线程HandlerHandler mainHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { // 处理消息,更新UI }};// 子线程Handlernew Thread(() -> { Looper.prepare(); Handler threadHandler = new Handler() { @Override public void handleMessage(Message msg) { // 处理消息 } }; Looper.loop();}).start();Handler内存泄漏问题原因:非静态内部类持有外部Activity引用延迟消息导致Activity无法被回收解决方案:使用静态内部类 + WeakReference在Activity销毁时移除所有消息使用Handler的removeCallbacksAndMessages(null)面试要点主线程Looper在ActivityThread.main()中创建Handler.post()和sendMessage()的区别Message.obtain()复用消息对象,避免内存分配IdleHandler在消息队列空闲时执行
阅读 0·3月7日 12:13

Android中Jetpack组件有哪些,它们的作用是什么?

Jetpack是Google推出的一套Android开发组件库,旨在帮助开发者遵循最佳实践、减少样板代码,并编写可在各种Android版本和设备上一致运行的代码。Jetpack组件分类1. 架构组件(Architecture Components)ViewModelclass MyViewModel : ViewModel() { private val _data = MutableLiveData<String>() val data: LiveData<String> = _data fun loadData() { // 配置变更时数据不会丢失 _data.value = "Loaded" }}作用:管理UI相关的数据,生命周期感知特点:配置变更(如旋转屏幕)时数据保留LiveDataviewModel.data.observe(this) { value -> // 自动处理生命周期,避免内存泄漏 textView.text = value}作用:可观察的数据持有者,生命周期感知特点:自动清理,避免内存泄漏Room@Entitydata class User(@PrimaryKey val id: Int, val name: String)@Daointerface UserDao { @Query("SELECT * FROM user") fun getAll(): LiveData<List<User>> @Insert fun insert(user: User)}作用:SQLite的抽象层,简化数据库操作特点:编译时SQL检查,支持LiveData返回DataBinding<TextView android:text="@{viewModel.userName}" />作用:将布局中的UI组件与数据源绑定特点:减少findViewById,自动更新UINavigation// 管理Fragment跳转和返回栈findNavController().navigate(R.id.action_detail)作用:管理应用内导航特点:可视化导航图,支持Deep LinkWorkManagerval workRequest = OneTimeWorkRequestBuilder<MyWorker>().build()WorkManager.getInstance(context).enqueue(workRequest)作用:管理可延迟的后台任务特点:保证任务执行,支持约束条件2. UI组件Fragment作用:模块化的UI组件特点:与Activity解耦,复用性强RecyclerView作用:高效显示大量数据列表特点:ViewHolder模式,四级缓存ViewPager2作用:页面滑动切换特点:基于RecyclerView,支持垂直滑动3. 基础组件AppCompat作用:向后兼容支持特点:使用新特性同时兼容旧版本Android KTX// Kotlin扩展函数sharedPreferences.edit { putString("key", "value")}作用:Kotlin友好的API扩展特点:简化代码,更符合Kotlin习惯Multidex作用:突破65536方法数限制特点:自动分包处理4. 行为组件DownloadManager作用:管理长时间下载任务特点:系统级服务,断点续传Media & Media3作用:音频视频播放特点:统一的媒体播放APINotifications作用:创建和管理通知特点:兼容各版本通知特性Permissions// 简化权限请求requestPermissionLauncher.launch(Manifest.permission.CAMERA)作用:简化运行时权限处理Jetpack优势| 优势 | 说明 || ---------- | ---------------- || 向后兼容 | 组件支持Android 5.0+ || 生命周期感知 | 自动管理生命周期,避免内存泄漏 || 减少样板代码 | 简化常见开发任务 || 一致性 | 统一的API设计 || 测试友好 | 组件可独立测试 |MVVM架构实践View (Activity/Fragment) ↓ 观察ViewModel ↓ 调用Repository ↓ 获取数据Data Source (Room/Network)面试要点理解ViewModel的生命周期范围掌握LiveData的转换操作(map、switchMap)了解Room的数据库迁移理解DataBinding的双向绑定掌握Navigation的Deep Link使用
阅读 0·3月7日 12:13