diff --git a/config/date/202406/http-interceptor.md b/config/date/202406/http-interceptor.md index c16bc2e..d4ae1ef 100644 --- a/config/date/202406/http-interceptor.md +++ b/config/date/202406/http-interceptor.md @@ -22,7 +22,7 @@ head: - window.navigator.sendBeacon - new Image -针对这几种不同的场景,分别拦截处理它的URL和请求参数 +针对这几种不同的场景,分别拦截处理它的 URL 和请求参数 ## (一)设计 @@ -51,7 +51,7 @@ XHR 的核心思路: ```ts class CustomXhr extends NativeXhr { private _method!: string; - private _src = '' as K; + private _src = "" as K; private _async!: boolean; private _username?: string | null; private _password?: string | null; @@ -76,7 +76,7 @@ class CustomXhr extends NativeXhr { } send(body?: T) { - let url = '' as K; + let url = "" as K; let data = body; if (!_this.useNative) { @@ -84,13 +84,7 @@ class CustomXhr extends NativeXhr { } // Open - super.open( - this._method, - url, - this._async, - this._username, - this._password - ); + super.open(this._method, url, this._async, this._username, this._password); // 设置请请求头 Object.keys(this._headers).forEach((key) => { @@ -161,7 +155,7 @@ window.navigator.sendBeacon = (url: K, data: T) => { 核心代码实现: ```ts - const NativeImage = window.Image; +const NativeImage = window.Image; class CustomImage extends NativeImage { private _src!: K; @@ -173,7 +167,7 @@ class CustomImage extends NativeImage { } this._src = _this.newSetHandler(value); - this.setAttribute('src', this._src); + this.setAttribute("src", this._src); } get src() { @@ -186,4 +180,10 @@ window.Image = CustomImage; ## (三)源码 -[源代码: https://github.com/swlws/http-interceptor](https://github.com/swlws/http-interceptor) +### install + +```bash +npm i @swl/http-interceptor +``` + +[源代码: https://github.com/swlws/http-interceptor](https://github.com/swlws/http-interceptor) diff --git "a/config/interview/\350\231\276\347\232\256-\344\270\200\351\235\242-\350\213\217\346\240\274\346\213\211\345\272\225\347\234\213\347\203\237\350\212\261.md" "b/config/interview/\350\231\276\347\232\256-\344\270\200\351\235\242-\350\213\217\346\240\274\346\213\211\345\272\225\347\234\213\347\203\237\350\212\261.md" new file mode 100644 index 0000000..43976d1 --- /dev/null +++ "b/config/interview/\350\231\276\347\232\256-\344\270\200\351\235\242-\350\213\217\346\240\274\346\213\211\345\272\225\347\234\213\347\203\237\350\212\261.md" @@ -0,0 +1,25 @@ +# 虾皮(一面)- 苏格拉底看烟花 + +## 对 React 和 Vue 框架的理解 + +## 前端性能优化的方法 + +## 构建工具 webpack、rollup + +## 什么是单页应用 + +## 有哪些前端自动化测试的经验 + +## GIT 的使用经验 + +## eslint 的使用 + +## 前端调试工具,比如:Chrome 开发者工具 + +## 对前端状态管理的理解 + +## 前端跨域问题的处理方法 + +## JS 异步的原理 + +JavaScript 是单线程的,这意味着在同一时间只能执行一个任务。为了实现异步操作,JavaScript 使用了事件循环(Event Loop)、任务队列(Task Queue)、回调函数(Callbacks)、Promises 和 async/await 等机制 diff --git "a/config/interview/\351\207\221\345\261\261\345\212\236\345\205\254-\345\211\215\347\253\257\345\274\200\345\217\221\347\232\204\351\235\242\350\257\225\344\275\223\351\252\214-\344\272\214\351\235\242-\345\212\263\344\276\235\344\276\235.md" "b/config/interview/\351\207\221\345\261\261\345\212\236\345\205\254-\345\211\215\347\253\257\345\274\200\345\217\221\347\232\204\351\235\242\350\257\225\344\275\223\351\252\214-\344\272\214\351\235\242-\345\212\263\344\276\235\344\276\235.md" new file mode 100644 index 0000000..dd4974a --- /dev/null +++ "b/config/interview/\351\207\221\345\261\261\345\212\236\345\205\254-\345\211\215\347\253\257\345\274\200\345\217\221\347\232\204\351\235\242\350\257\225\344\275\223\351\252\214-\344\272\214\351\235\242-\345\212\263\344\276\235\344\276\235.md" @@ -0,0 +1,362 @@ +# 金山办公 - 前端开发的面试体验(二面)- 劳依依 + +## vue2 什么情况下修改数据检测不到 + +1. 直接设置数组的元素 + +```js +this.items[index] = newValue; +``` + +解决方法:使用 Vue.set 或者数组的 splice 方法: + +```js +// 使用 Vue.set +Vue.set(this.items, index, newValue); + +// 使用 splice +this.items.splice(index, 1, newValue); +``` + +2. 直接修改对象的新属性 + +Vue 2 无法检测到给对象添加的新属性。例如: + +```js +this.obj.newProp = newValue; +``` + +解决方法: +使用 Vue.set 方法添加新属性: + +```javascript +Vue.set(this.obj, "newProp", newValue); +``` + +3. 修改数组的长度 + +Vue 2 无法检测到直接修改数组长度的变化。 + +```javascript +this.items.length = newLength; +``` + +解决方法: + +使用数组的 splice 方法: + +```javascript +this.items.splice(newLength); +``` + +4. 直接修改对象的嵌套属性 + +对于深层嵌套的对象,直接修改嵌套属性可能不会被检测到。例如: + +```javascript +this.obj.nestedProp.anotherNestedProp = newValue; +``` + +解决方法: +确保嵌套属性在初始化时已经存在。对于深层次的嵌套,可以使用 Vue.set 方法: + +```javascript +Vue.set(this.obj.nestedProp, "anotherNestedProp", newValue); +``` + +或者在创建对象时,确保所有可能的嵌套属性都被定义。 + +5. 替换整个对象或数组 + +换整个对象或数组时,Vue 的响应式系统会检测到变化,但在某些情况下可能需要确保引用的变更。例如: + +```javascript +this.items = [...this.items, newItem]; // 新数组 +this.obj = { ...this.obj, newProp: newValue }; // 新对象 +``` + +解决方法: + +这种情况下不需要特殊处理,因为 Vue 会检测到对象或数组的引用变更。但是需要确保在视图中绑定的是这个新对象或数组。 + +## vue2 如何让重新让一个组件重新渲染 + +1. 使用 key 属性 + +通过更改组件的 key 属性值,可以强制 Vue 重新渲染组件。当 key 发生变化时,Vue 会销毁旧的组件实例并创建一个新的实例。 + +2. 条件渲染 + +通过条件渲染 (v-if) 可以销毁并重新创建组件实例 + +3. 手动触发更新 + +使用 Vue 实例的 `$forceUpdate` 方法可以强制更新组件及其子组件。注意,这种方法不建议频繁使用,因为它会强制更新整个组件树,可能会影响性能。 + +4. 触发状态变化 + +确保状态变化能被 Vue 监听到,比如使用 Vue.set 或确保对象属性已存在。 + +## 如何评价一个前端网页的性能 + +1. 关键性能指标 (KPIs) + +页面加载时间 + +- First Contentful Paint (FCP): 从页面开始加载到任何内容绘制到屏幕上的时间。 +- Largest Contentful Paint (LCP): 从页面开始加载到主要内容绘制到屏幕上的时间,理想情况下应在 2.5 秒内完成。 +- Time to Interactive (TTI): 页面完全互动所需的时间。 +- Total Blocking Time (TBT): FCP 和 TTI 之间的时间量。 +- Cumulative Layout Shift (CLS): 页面视觉稳定性,衡量页面内容意外位移的频率。 + +资源加载时间 + +- HTTP 请求数: 页面加载过程中发送的请求数量,越少越好。 +- 资源大小: 页面加载的资源总大小,尽量减少未使用的 CSS 和 JavaScript。 + +响应时间 + +- 服务器响应时间: 从浏览器发出请求到服务器开始响应的时间。 +- TTFB (Time to First Byte): 从浏览器发出请求到接收到第一个字节的时间。 + +2. 工具 + +Google Lighthouse + +- 描述: 提供详细的性能报告,包括上述关键性能指标。 +- 使用方法: 在 Chrome 浏览器的开发者工具中运行,或者通过 PageSpeed Insights 在线使用。 + +WebPageTest + +- 描述: 提供详细的性能分析,包括渲染时间、内容加载时间和瀑布图等。 +- 使用方法: 访问 WebPageTest,输入 URL 并运行测试。 + +Chrome DevTools + +- 描述: 提供实时性能分析工具,包括 Network、Performance 和 Coverage 等面板。 +- 使用方法: 在 Chrome 浏览器中按 F12 打开开发者工具,使用 Network 面板查看请求详情,使用 Performance 面板进行性能分析。 + +GTmetrix + +- 描述: 提供综合的性能分析报告,包括 Lighthouse 评分、资源加载时间和建议。 +- 使用方法: 访问 GTmetrix 输入 URL 并运行测试。 + +3. 优化建议 + +减少 HTTP 请求数 + +- 合并 CSS 和 JavaScript 文件。 +- 使用 CSS sprites 合并图像。 + +优化资源加载 + +- 压缩 CSS、JavaScript 和图像文件。 +- 使用现代格式如 WebP 替代传统图像格式。 +- 启用 Gzip 或 Brotli 压缩。 + +延迟和异步加载资源 + +- 对非关键 CSS 和 JavaScript 使用 async 或 defer 属性。 +- 使用按需加载技术(lazy loading)加载图像和视频。 + +使用 CDN + +- 将静态资源托管在内容分发网络(CDN)上,缩短资源传输时间。 + +减少 DOM 数量 + +- 简化 HTML 结构,减少 DOM 节点数量。 + +优化关键渲染路径 + +- 确保关键 CSS 和 JavaScript 尽早加载,减少渲染阻塞资源。 + +4. 持续监测 + +- 定期使用上述工具进行性能测试。 +- 使用性能监测服务如 New Relic 或 Google Analytics 的性能报告功能,持续监测和优化。 + +## display 为 none 会进行页面渲染吗 + +当一个元素的 CSS display 属性设置为 none 时,该元素不会参与页面的渲染。 + +1. 元素不占据空间:display: none 会使元素从文档流中完全移除,因此它不占据任何空间。这意味着页面布局会重新计算,不会为该元素保留任何位置。 +2. 子元素不渲染:任何在 display: none 元素内部的子元素同样不会被渲染,不会触发其内部的 CSS 和 JavaScript。 +3. 样式和计算:浏览器不会计算或应用 display: none 元素的样式,除了当 display 属性改变时需要重新计算。 +4. DOM 操作:尽管 display: none 的元素不会被渲染,它们仍然存在于 DOM 中,可以通过 JavaScript 操作(如 document.querySelector 和 getElementById)进行访问和修改。 + +具体影响 + +1. 性能方面 + +- 渲染性能:由于不需要绘制或布局 display: none 的元素,浏览器在初始渲染和后续重绘时的开销会降低。 +- 内存使用:虽然这些元素不会被渲染,但它们仍然存在于 DOM 中,并消耗内存。如果一个页面包含大量使用 display: none 的元素,可能会增加 DOM 大小和内存占用。 + +2. 交互方面 + +- 事件处理:对于 display: none 的元素,事件不会被触发。即使绑定了事件处理器,也不会响应用户交互。 +- 动画和过渡:任何定义在 display: none 元素上的 CSS 动画或过渡效果不会运行,直到该元素的 display 属性被修改。 + +## cookie 有哪些属性 + +- name +- value +- domain +- path +- expires 和 max-age +- secure + - 作用: 指定 cookie 仅通过 HTTPS 传输。 +- httonly + - 作用: 指定 cookie 不能通过 JavaScript 的 Document.cookie 访问,增加安全性。 +- samesite + - 作用: 防止跨站请求伪造 (CSRF) 攻击。 + +示例: + +```http header +Set-Cookie: sessionId=abc123; Domain=example.com; Path=/; Expires=Wed, 21 Oct 2025 07:28:00 GMT; Max-Age=3600; Secure; HttpOnly; SameSite=Strict +``` + +## js 触发隐式类型转换有哪些 + +1. 字符串拼接(String Concatenation) + +```javascript +let a = 10; +let b = "20"; +let result = a + b; // '1020' +``` + +2. 数学运算(Mathematical Operations) + +```javascript +let a = 10; +let b = "5"; +let result = a - b; // 5 +``` + +3. 比较操作符(Comparison Operators) + +```javascript +let a = 10; +let b = "10"; +if (a == b) { + console.log("Equal"); +} +``` + +4. 逻辑运算符(Logical Operators) + +```javascript +let a = "hello"; +if (a) { + console.log("Truthy"); +} +``` + +5. 自定义对象的 toString 方法 + +```javascript +let obj = { + valueOf: function () { + return 10; + }, + toString: function () { + return "hello"; + }, +}; +let result = obj + 5; // 'hello5' +``` + +## 如何利用 js 给 dom 增加自定义事件 + +利用 CustomEvent 和 dispatchEvent 方法,可以给 DOM 元素增加自定义事件。 + +```javascript +document.addEventListener("DOMContentLoaded", function () { + // 创建自定义事件对象 + let customEvent = new CustomEvent("myCustomEvent", { + detail: { foo: "bar" }, + }); + + // 获取目标 DOM 元素 + let element = document.getElementById("myElement"); + + // 监听自定义事件 + element.addEventListener("myCustomEvent", function (event) { + console.log("Custom event triggered:", event); + console.log("Custom event detail:", event.detail); + }); + + // 分派自定义事件 + element.dispatchEvent(customEvent); +}); +``` + +## 如何判断元素在视口内 + +1. 使用 getBoundingClientRect() + +```javascript +function isElementInViewport(element) { + const rect = element.getBoundingClientRect(); + return ( + rect.top >= 0 && + rect.left >= 0 && + rect.bottom <= + (window.innerHeight || document.documentElement.clientHeight) && + rect.right <= (window.innerWidth || document.documentElement.clientWidth) + ); +} + +const element = document.getElementById("myElement"); +if (isElementInViewport(element)) { + console.log("Element is fully in the viewport"); +} else { + console.log("Element is not fully in the viewport"); +} +``` + +2. 使用 Intersection Observer API + +```javascript +// 创建一个 Intersection Observer +const observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + console.log("Element is in the viewport"); + // 可以在这里添加其他逻辑,例如加载图片 + } else { + console.log("Element is not in the viewport"); + } + }); + }, + { + threshold: [0, 0.5, 1], // 可选,定义触发的阈值,可以是单个值或数组 + } +); + +// 选择要观察的元素 +const element = document.getElementById("myElement"); +observer.observe(element); +``` + +## vueUse 内部如何实现的 + +内部的实现是基于 Vue 3 的组合式 API 和各种 JavaScript 特性来实现的 + +## 如何实现 URL 变动而页面不刷新 + +方法一:使用 HTML5 的 History API + +HTML5 的 History API 提供了 history.pushState 和 history.replaceState 方法,这些方法允许你在不刷新页面的情况下改变浏览器的 URL。 + +1. history.pushState 和 history.replaceState + +- pushState(state, title, url): 添加一个新的历史记录条目。 +- replaceState(state, title, url): 修改当前的历史记录条目。 + +方法二:使用 hash + +hash 方法比较简单,它通过 URL 的 # 部分来实现页面切换。这种方法不改变 URL 的实际路径,只是修改了 URL 的哈希值。