杂烩6

6 阅读22分钟

一个页面是图片流,n屏,如何进行优化?

图片流是一种常见的网页设计模式,用于展示大量的图片内容,通常用于图片分享、新闻媒体、摄影作品展示等场景。如果页面包含多屏(n屏)的图片,优化工作变得尤为重要,以确保页面加载迅速、运行流畅,并提供良好的用户体验。以下是一些优化图片流页面的策略:

1. 懒加载(Lazy Loading)

实现懒加载,即只加载用户当前看到或即将看到的图片,对于屏幕外的图片则延后加载。

2. 响应式图片

使用srcset和sizes属性,让浏览器根据设备屏幕大小和分辨率加载最合适的图片版本。

3. 图片压缩

使用工具如ImageOptim、TinyPNG或在线服务压缩图片,减少文件大小而不损失太多质量。

4. CDN分发

使用内容分发网络(CDN)来加速图片的加载速度,减少服务器的负载。

在react中,怎么强制更新组件

在React中,通常推荐让React的声明式渲染和状态更新机制来自动处理组件的更新。然而,在某些罕见的情况下,你可能需要强制组件更新。这可以通过使用forceUpdate方法来实现,它是Component类的一个生命周期方法。

以下是如何在React组件中使用forceUpdate:

class MyComponent extends React.Component {
  // ...

  componentDidMount() {
    // 假设你有一些需要手动触发更新的场景
    this.forceUpdate();
  }

  // ...

  render() {
    // 渲染逻辑...
    return <div>Some content</div>;
  }
}

在componentDidMount生命周期钩子中调用forceUpdate将导致组件重新渲染,即使它的状态或props没有变化。

需要注意的是,forceUpdate跳过了组件的shouldComponentUpdate方法,并且直接触发了render方法。这意味着组件会无视任何性能优化,强制进行渲染。因此,使用forceUpdate时应该非常小心,因为它可能导致性能问题。

在React函数组件中,没有内置的方法来强制更新。如果你需要在函数组件中强制更新,你可以考虑使用useEffect钩子来实现类似的效果,但这种做法同样需要谨慎处理,以避免性能问题。

react组件更新的时机有哪些

在React中,组件更新的时机通常由以下因素触发:

  1. 状态更新(State Updates) : 当组件的setState或在Hooks中的useState更新状态时,组件会重新渲染。
  2. 属性更改(Props Changes) : 当组件接收到新的属性(props)时,如果这些属性被组件的渲染逻辑所使用,组件会重新渲染。
  3. 强制更新(Forced Updates) : 通过调用类组件的forceUpdate方法,可以强制组件重新渲染。
  4. Context变化(Context Changes) : 如果组件使用React的Context API,并且它所依赖的上下文值发生变化,组件会重新渲染。
  5. Hooks更新(Hooks Updates) : 当使用Hooks如useEffect、useMemo、useCallback等,并且它们的依赖项(dependencies)发生变化时,相关联的逻辑会执行。
  6. 错误边界(Error Boundaries) : 当组件抛出错误,并且有错误边界组件捕获到这个错误时,错误边界组件会重新渲染。
  7. 生命周期方法(Lifecycle Methods) : 在类组件中,某些生命周期方法(如componentDidMount、componentDidUpdate)的调用会导致组件重新渲染。
  8. 高阶组件(Higher-Order Components, HOCs) : 使用高阶组件时,如果高阶组件返回的组件发生变化,被包裹的组件也会重新渲染。
  9. 渲染器(Renderers) : 如果你使用了自定义渲染器,它可能会影响组件的渲染行为。
  10. 异步更新(Asynchronous Updates) : React的更新是异步的,可能会批量处理更新。即使触发了更新,组件也可能在下一事件循环的tick中重新渲染。

cors

  1. Access-Control-Allow-Origin: 这个响应头指定了哪些源可以访问资源。它可以设置为:
    • 一个具体的URI(源),表示只有这个源可以访问资源。
    • 星号(*),表示任何源都可以访问资源,但出于安全考虑,这通常不推荐。
    • 一个列表,包含多个源,表示这些源可以访问资源。
  1. Access-Control-Allow-Methods: 这个响应头列出了哪些HTTP方法(如GET、POST、PUT、DELETE等)被允许跨源访问资源。
  2. Access-Control-Allow-Headers: 这个响应头列出了哪些自定义请求头(如X-Requested-With、Content-Type等)被允许跨源访问资源。
  3. Access-Control-Allow-Credentials: 这个响应头是一个布尔值,指示浏览器是否应该将凭证(如cookies、HTTP认证、客户端SSL证书等)包含在跨源请求中。如果设置为true,则浏览器会发送凭证,但前提条件是请求必须是“非简单请求”(如使用自定义请求头或某些HTTP方法)。如果设置为false,浏览器不会发送凭证,即使请求是“非简单请求”。

这些响应头通常由服务器设置,以控制哪些源可以访问服务器上的资源,以及访问时可以使用哪些HTTP方法和请求头。正确配置这些头是确保CORS安全而有效的关键。

fetch请求后返回的是什么?

fetch() 是 JavaScript 中的一个全局函数,用于发起网络请求(例如从服务器获取资源)。当你调用 fetch() 发送一个 HTTP 请求时,它会返回一个 Promise 对象。这个 Promise 对象在成功 resolve 时,其结果是一个 Response 对象。

Response 对象代表了来自服务器的响应,包含了诸如HTTP状态码、响应头部和响应体等信息。为了提取实际的响应数据(如JSON、文本或其他类型的内容),你需要进一步调用 Response 对象上的方法:

  • 若要获取作为文本的数据,可以调用 .text() 方法,它也会返回一个 Promise,resolve 后得到的是字符串形式的响应体内容。
  • 若要获取 JSON 格式的数据,可以调用 .json() 方法,它同样返回一个 Promise,resolve 后得到的是已被解析为 JavaScript 对象的响应体内容

前端实现图片压缩的方式

1,使用字体图标代替图片

2,调整图片尺寸

  • 在上传图片前,使用 JavaScript 调整图片的尺寸。小尺寸的图片文件通常更小。

3,使用压缩算法对图片进行压缩

4,服务端处理

    • 虽然这不是前端实现,但通常更高效的做法是将图片上传到服务器,然后使用服务器端的脚本语言(如 Node.js、Python 或 PHP)进行压缩。

5,使用Canvas绘制并压缩图片

大文件上传是什么

大文件上传是指在网页应用或网络环境中上传容量较大的文件的过程,通常指单个文件大小超过几兆字节(MB)甚至达到吉字节(GB)级别的文件。由于HTTP协议本身的限制以及网络环境的不稳定性,直接上传大文件可能会遇到诸如上传失败、耗时过长、服务器内存溢出、客户端浏览器崩溃等问题。

  1. 分片上传(Chunked Uploads) : 将大文件分割成多个较小的数据块(chunk),每个数据块独立上传,服务器接收到所有数据块后再将其拼接成完整的文件。这种方式能够有效应对网络不稳定导致的上传中断问题,中断后只需从断点处继续上传剩余未成功上传的片段。
  2. 断点续传(Resumable Uploads) : 类似于分片上传,但每个数据块上传时会记录其上传进度,如果上传过程中出现异常,后续可以继续上传未完成的部分,而不需要重新上传整个文件。
  3. MD5校验或ETag验证: 使用文件哈希值(如MD5)来判断文件是否存在或是否已经完整上传。如果服务器检测到某个文件的哈希值已经存在,则无需再次上传,即所谓的“秒传”。
  4. 前端预处理: 在上传之前对文件进行压缩处理,减少文件体积,尤其对于图片或视频等媒体文件,可通过JavaScript等前端技术进行压缩。
  5. 后台优化: 后端服务器通常也需要相应的优化措施,例如使用流式处理(streaming)接收文件,避免一次性加载到内存,防止内存溢出;同时,设计合理的持久化机制,存储上传过程中产生的临时数据。
  6. 多线程上传: 部分高级的上传组件或框架支持多线程上传,利用浏览器多线程能力并发上传多个文件片段,从而加快上传速度。

关于import导入部分中间能否有JS代码

在JavaScript中,import语句是用来导入模块的,它必须位于文件的顶级作用域内,也就是说,它不能放在函数、条件语句或循环体内。按照ES6模块规范,import和export语句只能出现在模块的根级别,不能嵌套在其他作用域中。

这意味着import导入模块的语句前后不能直接包含执行逻辑的JavaScript代码,比如函数定义、变量赋值等。例如,以下代码是不允许的:

然而,有一种异步动态导入(Dynamic Import)的形式可以在运行时动态地加载模块,它确实可以用在表达式中,但仍然是作为一个单独的语句使用,而不是与其他JS代码混合在一起:

let modulePath = './someModule.js';
async function loadModule() {
  const { default: someModule } = await import(modulePath);
  someModule.doSomething();
}
loadModule();

在这个例子中,import()表达式返回一个Promise,可以在异步函数中await该Promise来获取模块的导出内容。尽管如此,import()表达式仍然保持在独立的语句或表达式中,而非与常规JS代码混杂在同一层级。



什么时候用ts中的interface, emun

interface(接口)

何时使用interface:

  1. 定义类型结构:当你需要描述一个对象应该具有的形状或者属性集合时,使用 interface。它主要用于规定对象类型的结构,包括属性、方法签名等。例如,创建一个表示用户信息的接口:

enum(枚举)

何时使用enum:

  1. 枚举值的命名空间:当你有一组固定的、有限的并且有意义的命名数值时,使用 enum 来提供一种更易读且更具表达力的方式来替代硬编码的数字。
  2. 在涉及固定状态集或选项列表的场景中非常有用,比如HTTP状态码、角色权限级别、游戏中的角色状态等。

粘性定位和固定定位的区别

总的来说,粘性定位在一定程度上结合了相对定位(relative positioning)和固定定位的特性,提供了更加灵活的定位效果,适用于某些特定的滚动交互需求,比如让导航栏在滚动到一定位置时固定在顶部。而固定定位则是一种更强硬的固定方式,无论何时何地,只要浏览器窗口大小不变,元素的位置就会始终保持不变。

做一个游戏小程序,我退出然后进来不想他被后台杀掉该怎么做

对于微信小游戏或者其他小程序平台,当用户退出小程序(离开前台)后,小程序进程会被操作系统挂起进入后台,以释放系统资源。这属于系统的正常管理机制,无法完全阻止。但是,你可以采取一些策略来优化用户体验,让用户感觉像是游戏并未完全关闭:

  1. 快速恢复游戏状态: 在用户重新打开小程序时,立即从缓存(如Storage或IndexedDB)中加载之前保存的游戏状态,使用户能迅速回到上次退出时的状态。
  2. 请求更多的后台运行时间: 针对特定情况,比如音乐播放、录音、计步器等功能,可以申请后台服务,获得更长时间的后台运行权限。但这并不适用于大部分游戏场景。
  3. 引导用户添加到桌面或收藏: 引导用户将游戏小程序添加到手机桌面,变成类似原生应用的存在,这样用户可以直接点击图标启动游戏,减少了从聊天列表或其他入口重新找到小程序的时间。
  4. 优化加载速度:提高游戏的加载速度,即使被后台杀死也能快速重启并恢复游戏,尽量减少用户的等待时间。

泛型, 类型断言

泛型(Generics)与类型断言是两种在编程语言中用于静态类型检查和确保类型安全的重要机制。

泛型: 泛型是在编译时期允许类、接口或函数操作多种数据类型的一种技术。它允许你在定义类、接口或函数时使用占位符类型(通常以大写字母T、K、V等表示),然后在实例化或调用时指定具体的类型。这样可以编写复用性更高且更安全的代码,因为编译器会在编译阶段执行类型检查。

例如,在TypeScript中,你可以定义一个泛型函数:

function identity<T>(arg: T): T {
  return arg;
}

let output = identity<string>("Hello"); // 这里“string”就是对泛型T的具体类型约束


const identity = <T>(arg: T): T => arg;

类型断言: 类型断言是编程语言中一种告诉编译器我们确切知道变量类型的机制。它允许我们在某种情况下显式地指定一个值的类型,即使编译器不能自动推断出这个类型。

在TypeScript中,有两种形式的类型断言:

  1. 箭头语法 ():
  2. as 关键字:
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length; // 断言someValue在此处为字符串类型
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length; // 同样断言someValue为字符串类型

通过类型断言,我们可以临时放宽编译器对某个表达式的类型检查,从而在保持类型安全的前提下提高代码灵活性。但在使用时应当谨慎,确保类型断言的确符合实际运行时的数据类型,否则可能会导致运行时错误。

报错

function fun<T>(arg: T): T {
  return arg * arg
}

const aa = fun<number>(3)
// 报错,虽然说传入的参数和返回的值类型一致。
但是就在前三行而言,传入的可能是一个字符串,字符串可不能相乘,所以报错



更改


function fun<T extends number>(arg: T): T {
  return arg * arg as T;
}

const aa = fun(3);

前端实现一个简易的微前端

可以选择使用 iframe、single-spa、module federation 等方案。

不同的微应用如何做JS CSS隔离?

JS 隔离:

1. 沙箱环境 (Web Components 或 Shadow DOM)

  • 使用Web Components标准,尤其是Shadow DOM技术,可以为每个微应用创建一个独立的DOM树和样式作用域,从而达到JavaScript和CSS的自然隔离。

2. iframe

  • 将每个微应用加载在独立的iframe中,这样每个iframe都有自己的全局作用域,从而隔离JavaScript和CSS。

CSS 隔离:

1. CSS命名空间

  • 为每个微应用的CSS类名添加唯一前缀,确保类名不会与其他应用冲突。

2. CSS Modules

  • 使用CSS Modules,编译时为每个CSS类名生成独一无二的局部类名,防止全局样式污染。

3. CSS-in-JS

  • 利用CSS-in-JS库(如styled-components、emotion-js等),将CSS定义在JavaScript中,并利用组件作用域来限制CSS的作用域。

懒加载是基于分页实现的吗

当涉及到大量数据或资源需要按需加载时,懒加载技术确实可以结合分页来进行实现,特别是针对长列表或文档浏览的情况。不过,懒加载本身是一个通用的概念,它的应用范围远不止于分页加载。

react hooks中出现的闭包陷阱。怎么去解决它?

react Fiber中提出react组件可以中断或者恢复, 那么什么时候中断,什么时候恢复?

React Fiber 架构中提出的“中断与恢复”指的是在渲染和更新的过程中,React有能力在执行过程中适时暂停(yield),并在合适的时机继续(resume)这个过程。这样做是为了更好地管理浏览器主线程上的工作,提升界面响应性和流畅度。

中断与恢复主要发生在以下几个上下文中:

  1. 优先级调度
    • 当React Fiber开始渲染或更新组件树时,它会根据任务的重要性以及剩余时间分配预算(time slicing)。
    • 如果当前帧(大约16毫秒)内剩余时间不足以完成全部工作,React Fiber会选择中断正在执行的低优先级任务,以便浏览器能够及时响应用户的高优先级事件,如点击、滚动等。
    • 当浏览器主线程空闲时,React Fiber会通过请求空闲回调(如requestIdleCallback,尽管React不再强制依赖此API)或者其他类似机制恢复未完成的工作。
  1. Reconciliation阶段中断
    • 在Reconciliation阶段(也称为渲染阶段),React Fiber遍历Fiber树并对各个节点进行差异计算,决定是否需要更新DOM。
    • 当遇到复杂的更新或深度嵌套的组件树时,React Fiber可以在遍历过程中判断是否有更多时间来继续当前工作,如果没有,则中断当前的reconciliation过程,等待下次空闲周期再继续。
  1. Commit阶段
    • 在Commit阶段,React会将最终确定需要更新的部分提交至DOM。虽然这个阶段通常被认为是同步执行的,但是React Fiber依然有可能在某些情况下(如批处理多个更新)采取更加智能的方式来管理这个过程,尽可能减少对用户交互的影响。

总之,React Fiber通过可中断的协调算法实现了对渲染任务的精细化管理和调度,确保了即便在大型、复杂的应用场景下也能保持良好的UI响应性和流畅体验。当系统检测到浏览器主线程需要处理更高优先级的任务或者当前帧即将结束时,就会中断正在进行的React更新工作,然后在下一个可用时间段内恢复执行。

如果是二进制流,那contet-type是什么呢?

当涉及到二进制流数据时,HTTP 请求或响应的 Content-Type 头部字段通常设置为 application/octet-stream。

nbsp在页面会展示成什么东西?

在网页中,  是一个HTML实体(HTML Entity),它代表了一个非换行空格(Non-breaking Space)。当你在HTML文档中使用   时,浏览器在渲染页面时会将其解析为一个不可分割的空格,也就是说即使在页面换行排版时,这个空格也不会被断开,始终保持在同一行内。

知道什么叫html entity吗

当然,HTML Entity(HTML 实体)是指在HTML文档中用来表示特殊字符或不可见符号的一种编码形式。这些特殊字符可能是浏览器本身用于解析HTML语法的保留字,或者是ASCII字符集中不容易直接输入的字符,比如特殊符号、非英文字符等。

例如:

  • < 表示 <,左尖括号,用于在HTML文本中显示 "<" 符号,而不是作为标签的起始符号。
  • > 表示 >,右尖括号,用于显示 ">" 符号。
  •   表示非换行空格,它会在页面上显示一个不可分割的空格。

keep-alive底层实现原理

  1. TCP连接的复用: 当HTTP请求头中包含了Connection: keep-alive时,客户端和服务端都同意在完成一个HTTP事务后,继续保留TCP连接打开状态。这样,后续的HTTP请求就可以在同一个TCP连接上传输,减少了三次握手和四次挥手的时间开销,提高了网络效率。
  2. 闲置超时管理: 为了防止长时间不活动的TCP连接占用资源,TCP/IP协议栈通常会有一个保活(Keepalive)机制。这通常由操作系统层面提供,可以设置一个超时值(如两小时),超过这个时间如果没有数据传输,TCP会发送一个Keepalive探测包来检查连接是否仍然可用。如果对方回应了,连接继续保持;如果没有回应,连接会被认为已断开并关闭。
  3. HTTP/1.1中的管道化(pipelining) : 在HTTP/1.1中,虽然Keep-Alive允许复用TCP连接,但是请求必须按顺序响应,即第一个请求的响应返回之前,第二个请求不能开始传输响应数据,尽管请求可以连续发送(pipeline)。这就是所谓的HTTP/1.1的管道化特性,但它并不能解决所有并发问题。

总的来说,HTTP Keep-Alive的核心在于维持TCP连接状态,减少建立新连接的成本,而具体实现细节则涉及到了网络层(TCP)的保活机制,以及应用层(HTTP)的连接管理策略。在现代Web服务器和客户端实现中,这些功能通常已经被集成和优化,开发者只需要配置相应的参数即可实现高效的连接复用。

react18以前,定时器里面有三个setState,会导致页面重新渲染三次。这个处理过程是同步还是异步的?

因此,在React 18之前的版本中,即便在定时器中连续调用了三次setState,也不一定一定会导致页面重新渲染三次。React会尽可能地合并这些状态改变并在下一个合适的时机进行一次有效的重新渲染,而非每次都立即触发渲染。具体是否渲染三次取决于React内部对状态更新的批处理方式以及状态变化的具体内容。但如果这三个状态变化相互独立且影响了组件的不同部分,则可能会触发多次渲染。从性能优化的角度来看,React的设计倾向于减少不必要的渲染次数。

JS执行上下文

JavaScript 执行上下文(Execution Context)是JavaScript引擎在执行JavaScript代码时所维护的一个抽象概念,它包含了当前执行代码的相关信息,如变量环境、作用域链、this指向等。执行上下文对于理解JavaScript的变量查找、作用域规则以及this关键字的行为至关重要。

以下是关于执行上下文关键点的简要概述:

  1. 执行上下文类型
    • 全局执行上下文:这是当JavaScript脚本首次执行时创建的第一个上下文,它只有一个,包含全局变量和函数声明,同时this指向全局对象(在浏览器中是window对象)。
    • 函数执行上下文:每当函数被调用时,都会为其创建一个新的执行上下文,并将其压入执行栈中。函数执行上下文拥有自己的变量对象(Activation Object/AO,在ES6+规范中改为LexicalEnvironment),并继承自父执行上下文的作用域链,此外,函数上下文中的this值取决于函数调用方式。
    • Eval函数执行上下文(现代JavaScript中较少使用):当使用eval()函数执行代码时创建的执行上下文。

为什么会出现命名提升

命名提升(Name Hoisting)这一现象出现在JavaScript中,主要是因为JavaScript引擎的解析和执行机制。在ECMAScript规范中,JavaScript代码在执行前有一个预编译(compilation)阶段,在这个阶段,JavaScript引擎会对代码进行扫描,识别出所有的变量和函数声明,并提前绑定到当前作用域的相应位置。这个行为就表现为所谓的“提升”。

具体而言:

  1. 变量声明提升
    • 对于使用var声明的变量,JavaScript引擎会将声明部分提升到当前作用域(通常是函数作用域或全局作用域)的顶部,但变量赋值的操作并不提升。这意味着你可以在一个变量声明之前使用这个变量,但它的值将是undefined,直到到达实际赋值语句为止。
console.log(a); // 输出 undefined
var a = 5;
  1. 函数声明提升
    • 函数声明也会被提升到当前作用域的顶部,并在整个作用域内可见。因此,你可以在函数声明之前调用该函数。
foo(); // 正常执行,输出 "Hello"
function foo() {
  console.log("Hello");
}
  1. let 和 const 声明
    • 在ES6中引入了let和const关键字,它们的声明同样有提升效果,但被称为“暂时性死区(Temporal Dead Zone, TDZ)”。声明本身被提升,但在声明语句被执行之前,无法访问这些变量,试图访问会导致ReferenceError。
console.log(bar); // ReferenceError: bar is not defined
let bar = "Hello";

这种设计的原因在于JavaScript的设计决策,使得它可以进行动态的变量和函数绑定,并允许代码以看似非线性的顺序执行,但它也可能带来潜在的陷阱和混淆,特别是在不了解变量提升特性的开发者中间。因此,在编写JavaScript代码时,通常建议将变量和函数声明放在作用域的开头,以避免因变量提升带来的意料之外的行为。

Canvas

Canvas 是 HTML5 中的一个元素,它可以用来通过使用 JavaScript 在网页上绘制图形。Canvas 提供了一个二维绘图 API,允许开发者绘制线条、形状、文本、图像以及其他对象。以下是一些关于 Canvas 的关键点: