前端面试核心:JavaScript原理、框架设计与工程化实战解析

前端面试核心:JavaScript原理、框架设计与工程化实战解析
前端面试的竞争从未像今天这样激烈。随着各大厂招聘门槛的不断提升单纯掌握基础语法已经远远不够。字节跳动等一线互联网公司对前端工程师的要求已经从会写代码升级到了理解原理、掌握工程、具备架构思维的层面。很多求职者陷入了一个误区认为面试就是背诵八股文。但实际上字节面试官真正考察的是候选人对前端知识体系的深度理解和实际应用能力。那些能够清晰解释JavaScript事件循环机制、深入分析Vue/React框架设计思想、熟练运用工程化工具解决实际问题的候选人往往能够在面试中脱颖而出。本文基于最新的字节前端面试真题和考察趋势整理了100个核心问题及其深度解析。不同于简单的问答式八股文每个问题都配有原理分析、代码示例和实际应用场景帮助你在7天内构建完整的前端知识体系真正具备冲击大厂offer的实力。1. JavaScript核心原理深度解析1.1 数据类型与内存管理JavaScript的数据类型看似简单但其中的细节往往成为面试的考察重点。理解数据类型的本质是掌握JavaScript语言基础的关键。基本数据类型与引用类型的本质区别基本数据类型Primitive types包括Undefined、Null、Boolean、Number、String、SymbolES6、BigIntES2020。这些类型的值直接存储在栈内存中访问时按值访问。// 基本数据类型示例 let a 10; let b a; // b是a的值副本 b 20; console.log(a); // 10 - a的值不变引用类型Object types包括Object、Array、Function、Date等。这些类型的值存储在堆内存中栈内存中存储的是指向堆内存的地址指针。// 引用类型示例 let obj1 { name: Alice }; let obj2 obj1; // obj2和obj1指向同一个内存地址 obj2.name Bob; console.log(obj1.name); // Bob - 原对象被修改深度拷贝的实践方案在实际开发中我们经常需要创建对象的独立副本。浅拷贝无法满足深层嵌套对象的复制需求这时候就需要深度拷贝。// 简单的深拷贝实现有局限性 function deepClone(obj) { if (obj null || typeof obj ! object) return obj; if (obj instanceof Date) return new Date(obj.getTime()); if (obj instanceof Array) return obj.map(item deepClone(item)); const cloned {}; Object.keys(obj).forEach(key { cloned[key] deepClone(obj[key]); }); return cloned; } // 使用示例 const original { name: Alice, profile: { age: 25, hobbies: [reading, coding] }, createdAt: new Date() }; const cloned deepClone(original); cloned.profile.age 26; console.log(original.profile.age); // 25 - 原对象未被修改对于更复杂的深拷贝需求建议使用成熟的库如lodash的_.cloneDeep方法或者使用JSON.parse(JSON.stringify(obj))但这种方法会丢失函数、undefined等特殊值。1.2 作用域与闭包实战应用作用域和闭包是JavaScript中最重要的概念之一理解它们对于编写高质量代码至关重要。变量提升与暂时性死区var声明的变量存在变量提升hoisting而let/const则存在暂时性死区Temporal Dead Zone。// var的变量提升 console.log(a); // undefined不会报错 var a 10; // let的暂时性死区 console.log(b); // ReferenceError: Cannot access b before initialization let b 20;闭包的实用场景闭包是指有权访问另一个函数作用域中变量的函数。闭包在实际开发中有很多实用场景// 1. 模块模式 - 创建私有变量 const counterModule (function() { let count 0; // 私有变量 return { increment: function() { count; return count; }, decrement: function() { count--; return count; }, getCount: function() { return count; } }; })(); console.log(counterModule.increment()); // 1 console.log(counterModule.increment()); // 2 // count变量在外部无法直接访问实现了封装 // 2. 函数工厂 - 创建特定配置的函数 function createMultiplier(factor) { return function(x) { return x * factor; }; } const double createMultiplier(2); const triple createMultiplier(3); console.log(double(5)); // 10 console.log(triple(5)); // 151.3 原型与继承体系深度剖析JavaScript使用基于原型的继承机制这与传统的基于类的继承有本质区别。原型链的完整解析每个JavaScript对象都有一个内部链接指向另一个对象这个对象就是原型。当访问对象的属性时如果对象本身没有该属性JavaScript会沿着原型链向上查找。function Person(name) { this.name name; } Person.prototype.sayHello function() { return Hello, Im ${this.name}; }; function Student(name, grade) { Person.call(this, name); // 调用父类构造函数 this.grade grade; } // 设置原型链 Student.prototype Object.create(Person.prototype); Student.prototype.constructor Student; Student.prototype.study function() { return ${this.name} is studying in grade ${this.grade}; }; const student new Student(Alice, 5); console.log(student.sayHello()); // Hello, Im Alice console.log(student.study()); // Alice is studying in grade 5ES6 Class语法糖的本质ES6引入的class语法让继承写法更接近传统面向对象语言但其本质仍然是基于原型的继承。class Person { constructor(name) { this.name name; } sayHello() { return Hello, Im ${this.name}; } } class Student extends Person { constructor(name, grade) { super(name); // 调用父类构造函数 this.grade grade; } study() { return ${this.name} is studying in grade ${this.grade}; } } // 使用方式与构造函数模式相同 const student new Student(Bob, 6); console.log(student.sayHello()); // Hello, Im Bob2. 异步编程与事件循环机制2.1 Promise深度解析与实战Promise是现代JavaScript异步编程的基石深入理解Promise对于处理复杂异步流程至关重要。Promise的三种状态与转换Promise有三种状态pending进行中、fulfilled已成功、rejected已失败。状态一旦改变就不会再变。// Promise基础使用 const promise new Promise((resolve, reject) { setTimeout(() { const random Math.random(); if (random 0.5) { resolve(Success: ${random}); } else { reject(Error: ${random}); } }, 1000); }); promise .then(result console.log(result)) .catch(error console.error(error)) .finally(() console.log(Promise settled)); // Promise链式调用 function fetchUserData(userId) { return fetch(/api/users/${userId}) .then(response { if (!response.ok) throw new Error(Network response was not ok); return response.json(); }) .then(user { console.log(User data:, user); return user; }); } // Promise.all与Promise.race的实际应用 const apiCalls [ fetch(/api/users/1), fetch(/api/users/2), fetch(/api/users/3) ]; // 所有请求都成功时才继续 Promise.all(apiCalls) .then(responses Promise.all(responses.map(r r.json()))) .then(users console.log(All users:, users)) .catch(error console.error(One request failed:, error)); // 第一个完成的请求决定结果 Promise.race(apiCalls) .then(response response.json()) .then(user console.log(First user:, user));2.2 async/await最佳实践async/await是基于Promise的语法糖让异步代码看起来像同步代码提高了代码的可读性。// 基本的async/await使用 async function getUserData(userId) { try { const response await fetch(/api/users/${userId}); if (!response.ok) throw new Error(Network response was not ok); const user await response.json(); return user; } catch (error) { console.error(Failed to fetch user:, error); throw error; // 重新抛出错误以便外部处理 } } // 并行处理多个异步操作 async function fetchMultipleUsers(userIds) { try { // 使用Promise.all实现并行请求 const userPromises userIds.map(id getUserData(id)); const users await Promise.all(userPromises); return users; } catch (error) { console.error(Failed to fetch users:, error); return []; } } // 在实际项目中的典型应用 class UserService { async login(username, password) { const response await fetch(/api/login, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ username, password }) }); if (!response.ok) { throw new Error(Login failed); } const data await response.json(); localStorage.setItem(token, data.token); return data.user; } async getProfile() { const token localStorage.getItem(token); const response await fetch(/api/profile, { headers: { Authorization: Bearer ${token} } }); if (response.status 401) { // Token过期尝试刷新 await this.refreshToken(); return this.getProfile(); // 递归调用 } if (!response.ok) throw new Error(Failed to get profile); return response.json(); } }2.3 事件循环与微任务/宏任务理解事件循环机制是掌握JavaScript异步编程的关键特别是在处理复杂异步流程时。事件循环的执行顺序console.log(Script start); // 同步任务 setTimeout(() { console.log(setTimeout); // 宏任务 }, 0); Promise.resolve() .then(() { console.log(Promise 1); // 微任务 }) .then(() { console.log(Promise 2); // 微任务 }); console.log(Script end); // 同步任务 // 输出顺序 // Script start // Script end // Promise 1 // Promise 2 // setTimeout实际开发中的事件循环应用// 避免阻塞主线程的长任务 function processLargeData(data) { // 将大数据处理分解为小块使用setTimeout避免阻塞 return new Promise((resolve) { let result []; let index 0; function processChunk() { const chunk data.slice(index, index 1000); result result.concat(processChunkData(chunk)); index 1000; if (index data.length) { // 使用setTimeout让出主线程控制权 setTimeout(processChunk, 0); } else { resolve(result); } } processChunk(); }); } // 使用MessageChannel实现优先级调度 function scheduleTask(task, priority normal) { const channel new MessageChannel(); const port channel.port2; if (priority high) { // 高优先级任务使用微任务 Promise.resolve().then(task); } else { // 普通优先级任务使用宏任务 port.postMessage(null); channel.port1.onmessage task; } }3. HTML5与CSS3现代特性3.1 语义化标签与可访问性现代前端开发强调语义化和可访问性这不仅有助于SEO也提升了用户体验。语义化标签的最佳实践!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title现代网页结构示例/title /head body header rolebanner nav aria-label主导航 ul lia href#home首页/a/li lia href#about关于/a/li lia href#contact联系/a/li /ul /nav /header main rolemain article header h1文章标题/h1 time datetime2024-01-152024年1月15日/time /header section aria-labelledbysection1 h2 idsection1第一部分/h2 p这是文章的第一部分内容。/p figure img srcimage.jpg alt描述图片内容 figcaption图片说明文字/figcaption /figure /section aside aria-label相关链接 h3相关内容/h3 ul lia href#相关文章1/a/li lia href#相关文章2/a/li /ul /aside /article /main footer rolecontentinfo pcopy; 2024 公司名称. 所有权利保留./p /footer /body /html3.2 CSS Grid与Flexbox布局系统现代CSS布局系统让复杂布局变得简单直观掌握它们对于前端开发至关重要。Flexbox实战应用/* 导航栏布局 */ .navbar { display: flex; justify-content: space-between; align-items: center; padding: 1rem 2rem; background-color: #333; } .nav-logo { color: white; font-size: 1.5rem; font-weight: bold; } .nav-menu { display: flex; list-style: none; gap: 2rem; } .nav-menu a { color: white; text-decoration: none; transition: color 0.3s; } .nav-menu a:hover { color: #4CAF50; } /* 卡片布局 */ .card-container { display: flex; flex-wrap: wrap; gap: 1rem; justify-content: center; } .card { flex: 1 1 300px; /* 基础宽度300px可伸缩 */ max-width: 400px; background: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); padding: 1.5rem; } /* 垂直居中经典方案 */ .centered-container { display: flex; justify-content: center; align-items: center; min-height: 100vh; }CSS Grid复杂布局/* 网页整体布局 */ .layout { display: grid; grid-template-areas: header header header sidebar main aside footer footer footer; grid-template-rows: auto 1fr auto; grid-template-columns: 250px 1fr 200px; min-height: 100vh; gap: 1rem; } .header { grid-area: header; } .sidebar { grid-area: sidebar; } .main { grid-area: main; } .aside { grid-area: aside; } .footer { grid-area: footer; } /* 响应式网格布局 */ .photo-gallery { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1rem; padding: 1rem; } .photo-item { position: relative; overflow: hidden; border-radius: 8px; } .photo-item img { width: 100%; height: 200px; object-fit: cover; transition: transform 0.3s; } .photo-item:hover img { transform: scale(1.05); } /* 媒体查询实现响应式 */ media (max-width: 768px) { .layout { grid-template-areas: header main sidebar aside footer; grid-template-columns: 1fr; grid-template-rows: auto 1fr auto auto auto; } .photo-gallery { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); } }4. 浏览器原理与性能优化4.1 渲染原理与重绘重排理解浏览器渲染原理是进行性能优化的基础特别是对于复杂交互的Web应用。浏览器渲染流程深度解析// 使用Performance API监控渲染性能 function monitorRenderingPerformance() { const observer new PerformanceObserver((list) { for (const entry of list.getEntries()) { if (entry.entryType paint) { console.log(${entry.name}: ${entry.startTime}ms); } } }); observer.observe({entryTypes: [paint, largest-contentful-paint]}); } // 减少重排的实践技巧 class LayoutOptimizer { // 批量DOM操作 static batchDOMUpdates(updates) { // 使用文档片段进行批量插入 const fragment document.createDocumentFragment(); updates.forEach(item { const element document.createElement(item.tag); element.textContent item.text; fragment.appendChild(element); }); document.getElementById(container).appendChild(fragment); } // 使用transform代替top/left动画 static animateWithTransform(element, targetX, targetY) { // 使用transform避免重排 element.style.transform translate(${targetX}px, ${targetY}px); // 而不是这样会引起重排 // element.style.left targetX px; // element.style.top targetY px; } // 读写分离优化 static optimizedLayoutReads() { // 错误的做法交替读写 // const width element.offsetWidth; // 读 // element.style.width (width 10) px; // 写 // const height element.offsetHeight; // 读 // element.style.height (height 10) px; // 写 // 正确的做法先读后写 const width element.offsetWidth; const height element.offsetHeight; element.style.width (width 10) px; element.style.height (height 10) px; } }4.2 内存管理与垃圾回收JavaScript的自动内存管理虽然方便但不合理的使用仍会导致内存泄漏。常见内存泄漏场景与解决方案// 1. 意外的全局变量 function createLeak() { leak 这是一个全局变量; // 忘记使用var/let/const } // 解决方案严格模式 use strict; function safeFunction() { let safeVar 这是局部变量; // 必须声明 } // 2. 定时器未清理 class TimerManager { constructor() { this.timers new Set(); } setInterval(callback, delay) { const timer setInterval(callback, delay); this.timers.add(timer); return timer; } clearAll() { this.timers.forEach(timer clearInterval(timer)); this.timers.clear(); } } // 3. DOM引用未释放 class DOMReferenceManager { constructor() { this.elements new WeakMap(); // 使用WeakMap避免内存泄漏 } registerElement(key, element) { this.elements.set(key, element); } // WeakMap中的引用不会阻止垃圾回收 } // 4. 闭包中的循环引用 function createClosureLeak() { const largeData new Array(1000000).fill(data); return function() { console.log(闭包仍然引用largeData); // largeData不会被释放即使外部不再需要 }; } // 解决方案及时释放引用 function createSafeClosure() { let largeData new Array(1000000).fill(data); const usefulFunction function() { // 使用数据的逻辑 }; // 使用完后主动释放 largeData null; return usefulFunction; }5. 前端工程化与构建工具5.1 Webpack配置与优化策略Webpack是现代前端工程化的核心工具合理的配置可以显著提升构建性能和代码质量。Webpack生产环境配置// webpack.config.js const path require(path); const TerserPlugin require(terser-webpack-plugin); const MiniCssExtractPlugin require(mini-css-extract-plugin); const CssMinimizerPlugin require(css-minimizer-webpack-plugin); module.exports { mode: production, entry: { main: ./src/index.js, vendor: ./src/vendor.js }, output: { path: path.resolve(__dirname, dist), filename: [name].[contenthash].js, clean: true }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: babel-loader, options: { presets: [babel/preset-env] } } }, { test: /\.css$/, use: [ MiniCssExtractPlugin.loader, css-loader, postcss-loader ] }, { test: /\.(png|jpg|gif)$/, type: asset, parser: { dataUrlCondition: { maxSize: 8 * 1024 // 8KB以下转为base64 } } } ] }, optimization: { minimizer: [ new TerserPlugin({ parallel: true, terserOptions: { compress: { drop_console: true // 生产环境移除console } } }), new CssMinimizerPlugin() ], splitChunks: { chunks: all, cacheGroups: { vendor: { test: /[\\/]node_modules[\\/]/, name: vendors, chunks: all } } } }, plugins: [ new MiniCssExtractPlugin({ filename: [name].[contenthash].css }) ] };5.2 代码分割与懒加载实战合理的代码分割可以显著提升应用的首屏加载速度。// 路由级别的代码分割 import { lazy, Suspense } from react; const Home lazy(() import(./components/Home)); const About lazy(() import(./components/About)); const Contact lazy(() import(./components/Contact)); function App() { return ( Router Suspense fallback{divLoading.../div} Routes Route path/ element{Home /} / Route path/about element{About /} / Route path/contact element{Contact /} / /Routes /Suspense /Router ); } // 基于条件的动态导入 class ImageLoader { static async loadImage(url) { try { // 动态导入图片处理库 const { default: imageProcessor } await import( /* webpackChunkName: image-processor */ ./utils/imageProcessor ); return await imageProcessor.load(url); } catch (error) { console.error(Failed to load image processor:, error); // 降级方案 return this.fallbackLoad(url); } } static fallbackLoad(url) { return new Promise((resolve) { const img new Image(); img.onload () resolve(img); img.src url; }); } } // Webpack的魔法注释优化 const HeavyComponent lazy(() import( /* webpackChunkName: heavy-component */ /* webpackPrefetch: true */ ./components/HeavyComponent ));6. 前端安全与网络知识6.1 XSS与CSRF防护实战前端安全是Web开发中不可忽视的重要环节特别是对于用户数据敏感的应用。XSS防护完整方案// 1. 输入验证与过滤 class XSSFilter { static sanitizeHTML(input) { const div document.createElement(div); div.textContent input; // 使用textContent避免HTML解析 return div.innerHTML; } static validateInput(input, rules) { // 移除危险标签和属性 const cleanInput input.replace(/script\b[^]*(?:(?!\/script)[^]*)*\/script/gi, ) .replace(/on\w[^]*/gi, ) .replace(/javascript:/gi, ); // 自定义规则验证 if (rules.maxLength cleanInput.length rules.maxLength) { throw new Error(Input too long); } if (rules.pattern !rules.pattern.test(cleanInput)) { throw new Error(Invalid input format); } return cleanInput; } } // 2. CSP内容安全策略配置 // 在HTTP头中设置 // Content-Security-Policy: default-src self; script-src self https://trusted.cdn.com // 3. HTTP-only Cookie保护敏感信息 // 服务器设置Cookie时添加HttpOnly标志 document.cookie sessionIdabc123; HttpOnly; Secure; SameSiteStrict; // CSRF防护实践 class CSRFProtection { static generateToken() { const token crypto.randomUUID(); // 或使用其他随机生成方法 // 将token存储在session或cookie中 document.cookie csrfToken${token}; SameSiteStrict; return token; } static validateToken(requestToken) { const cookieToken this.getCookie(csrfToken); return cookieToken cookieToken requestToken; } static getCookie(name) { const value ; ${document.cookie}; const parts value.split(; ${name}); if (parts.length 2) return parts.pop().split(;).shift(); } } // 在表单中使用CSRF token form action/api/submit methodPOST input typehidden namecsrfToken value% csrfToken % !-- 其他表单字段 -- /form6.2 HTTPS与网络安全现代Web应用必须使用HTTPS来保证数据传输的安全。// 1. 强制HTTPS重定向 if (location.protocol ! https: location.hostname ! localhost) { location.href https: window.location.href.substring(window.location.protocol.length); } // 2. 使用Service Worker进行安全拦截 class SecureServiceWorker { static async register() { if (serviceWorker in navigator) { try { const registration await navigator.serviceWorker.register(/sw.js, { scope: / }); console.log(ServiceWorker注册成功); } catch (error) { console.error(ServiceWorker注册失败:, error); } } } } // sw.js - Service Worker文件 self.addEventListener(fetch, (event) { // 只允许HTTPS请求开发环境除外 if (event.request.url.startsWith(http:) !event.request.url.includes(localhost)) { event.respondWith( new Response(必须使用HTTPS, { status: 403 }) ); return; } // 其他缓存策略... }); // 3. 安全头部设置 // 在服务器配置中添加安全头部 /* Strict-Transport-Security: max-age31536000; includeSubDomains X-Content-Type-Options: nosniff X-Frame-Options: DENY X-XSS-Protection: 1; modeblock */7. 框架原理与最佳实践7.1 React Hooks深度解析React Hooks是现代React开发的核心理解其原理对于编写高质量React应用至关重要。自定义Hooks实战import { useState, useEffect, useCallback } from react; // 1. 数据获取Hook function useApi(url, options {}) { const [data, setData] useState(null); const [loading, setLoading] useState(true); const [error, setError] useState(null); const fetchData useCallback(async () { try { setLoading(true); setError(null); const response await fetch(url, options); if (!response.ok) throw new Error(Network response was not ok); const result await response.json(); setData(result); } catch (err) { setError(err.message); } finally { setLoading(false); } }, [url, JSON.stringify(options)]); useEffect(() { fetchData(); }, [fetchData]); return { data, loading, error, refetch: fetchData }; } // 2. 本地存储Hook function useLocalStorage(key, initialValue) { const [storedValue, setStoredValue] useState(() { try { const item window.localStorage.getItem(key); return item ? JSON.parse(item) : initialValue; } catch (error) { console.error(Error reading localStorage key ${key}:, error); return initialValue; } }); const setValue useCallback((value) { try { const valueToStore value instanceof Function ? value(storedValue) : value; setStoredValue(valueToStore); window.localStorage.setItem(key, JSON.stringify(valueToStore)); } catch (error) { console.error(Error setting localStorage key ${key}:, error); } }, [key, storedValue]); return [storedValue, setValue]; } // 3. 防抖Hook function useDebounce(value, delay) { const [debouncedValue, setDebouncedValue] useState(value); useEffect(() { const handler setTimeout(() { setDebouncedValue(value); }, delay); return () { clearTimeout(handler); }; }, [value, delay]); return debouncedValue; } // 使用示例 function SearchComponent() { const [query, setQuery] useState(); const debouncedQuery useDebounce(query, 300); const { data, loading, error } useApi(/api/search?q${debouncedQuery}); return ( div input typetext value{query} onChange{(e) setQuery(e.target.value)} placeholder搜索... / {loading div加载中.../div} {error div错误: {error}/div} {data SearchResults results{data} /} /div ); }7.2 Vue3 Composition API实战Vue3的Composition API提供了更灵活的逻辑组织方式。import { ref, reactive, computed, watch, onMounted } from vue; // 1. 组合式函数 export function useCounter(initialValue 0) { const count ref(initialValue); const increment () count.value; const decrement () count.value--; const reset () count.value initialValue; const isEven computed(() count.value % 2 0); watch(count, (newValue, oldValue) { console.log(Count changed from ${oldValue} to ${newValue}); }); return { count, increment, decrement, reset, isEven }; } // 2. 数据获取组合 export function useApi(url, options {}) { const data ref(null); const loading ref(true); const error ref(null); const fetchData async () { try { loading.value true; error.value null; const response await fetch(url, options); if (!response.ok) throw new Error(Network error); data.value await response.json(); } catch (err) { error.value err.message; } finally { loading.value false; } }; onMounted(() { fetchData(); }); return { data, loading, error, refetch: fetchData }; } // 在组件中使用 export default { setup() { const { count, increment, isEven } useCounter(0); const { data: userData, loading } useApi(/api/user); return { count, increment, isEven, userData, loading }; } };8. 性能监控与错误追踪8.1 性能指标监控体系建立完整的性能监控体系是保证应用质量的关键。// 性能监控类 class PerformanceMonitor { constructor() { this.metrics new Map(); this.init(); } init() { // 监听关键性能指标 this.observeLargestContentfulPaint(); this.observeFirstInputDelay(); this.observeCumulativeLayoutShift(); this.monitorResourceTiming(); } observeLargestContentfulPaint() { const observer new PerformanceObserver((list) { const entries list.getEntries(); const lastEntry entries[entries.length - 1]; this.reportMetric(LCP, lastEntry.startTime); }); observer.observe({entryTypes: [largest-contentful-paint]}); } observeFirstInputDelay() { const observer new PerformanceObserver((list) { list.getEntries().forEach(entry { const delay entry.processingStart - entry.startTime; this.reportMetric(FID, delay); }); }); observer.observe({type: first-input, buffered: true}); } observeCumulativeLayoutShift() { let cls 0; const observer new PerformanceObserver((list) { list.getEntries().forEach(entry { if (!entry.hadRecentInput) { cls entry.value; this.reportMetric(CLS, cls); } }); }); observer.observe({type: layout-shift, buffered: true}); } monitorResourceTiming() { // 监控资源加载性能 const resources performance.getEntriesByType(resource); resources.forEach(resource { this.reportMetric(Resource, resource.duration, { name: resource.name, type: resource

最新新闻

日新闻

周新闻

月新闻