场景
大文件上传的实现
在售后服务这块需要上传资料,可能包含有一些视频文件几百兆会比较大。
- 断点续传(网络断开,之前传的没了):
- 断开重连重传(网络波动,传的没了):
- 切片上传(关机开机之后,继续传):
步骤:
在售后服务,file类型是支持分段的可以用slice切片,需要把file按照切片块大小切出来,存到切片栈里,然后把所有的切片循环上传到后台处理,等所有的切片都上传之后,在发送一个合并通知后端,后面还加了一个断电续传,就是用spark-md5通过文件内容生成hash值作为文件的唯一标识,存到浏览器缓存里,普通的localstorage虽然能持久缓存,但是内存太小了。用来一个localforage存,它底层是结合了indexdb实现的,就能多存一些,后来发现db里它可能传到一半,后面他也不重新上传,导致db里会有遗留的数据,所以后面设计的更合理一点就是,上来先查db有数据,先查看看时间戳是多长的,超过1天就给他删了。如果还在有效期,就给他弹个提示。等下次上传就先从本地存储捞数据,把匹配上的都过滤掉,再重新传没上传的。
大图片加载优化
- 背景:业务提供的零件图里包含有很多精密的细节,导出之后给到我们图片非常大,常规加载渲染图片发现有的图片体积比较大10多m,就会导致图片加载卡顿。然后查了一下解决办法,主要是图片懒加载,预加载,但是我们这个是单图渲染,而且支持点击切换图片,就决定用图片分割切一下大图
- 实践:主要把大图片通过canvas画布,把大图片按照高度一块块的切割绘制到画布上,切割完成之后直接转成图片挂载到节点上
- 网页加载时,大图片文件如何分片加载,有示例代码。_tx.drawimage(img, 0, y, width, chunkheight, 0, y,、-CSDN博客
怎么实现截图功能
- 背景:零件图上有精密的零件节点,需要根据库存量还有异常件。业务就要求实现截图功能,保存到本地。一开始是打算用html2canvas通过dom节点来整块的截图,但是后续沟通业务想要更加高效一些,要实现局部截图。但是这个截的是canvas的图,所以就考虑用canvas实现截图的功能
- 实践:主要是通过计算鼠标点击和【drawimage】【fillText】【toDataURL】得原始位置和移动过程中的距离计算距离差,得到截图矩形块的点位信息;利用canvas的api【globalCompositeOperation】能够配置canvas绘制新图形的合成类型,给原始canvas叠一层背景遮罩和绘制过程中的矩形,然后再绘制一个新的截图区域矩形,最后把截取到的区域转成url通过a标签下载下来。如果需要水印也可以先加一层水印再下载
如何通过aggrid优化复杂网格
- 背景:在零件信息有一个网格包含输入框、下拉框、单选框二三十个需要输入的内容,还要联动后台接口渲染数据,业务场景比较复杂,因为是涉及多部门流转要操作审核的,不仅要控制禁用填写,还需要计算多个单元格之间会有计算规范和逻辑。本来就已经很杂了,因为element的table单元格变化要绑定很多个change事件,但是aggrid在配置列表的时候,就能把getter函数写上,等数据渲染的时候就能自己触发,就省心很多。
怎么通过dompurify净化html
- 背景:在售后服务信息数据流转过程中,要实现一个编辑器。编辑器一些常规的功能都需要有就写加粗各种样式还有上传图片之类的,在这个过程中就给业务任意编辑,为了避免XSS攻击就额外给编辑器的html净化一次
- 实践:主要是通过dompurify工具实现,通过配置白名单和黑名单控制住异常标签,像是script和iframe需要拉进黑名单,还有一些onclick之类的属性也拉黑
统计前端请求耗时
- axios:请求拦截器和响应拦截器中截取时间戳计算
- performance api:通过performance.getEntriesByType("resource"),获取所有请求耗时;也可以通过performance timing获取第一条请求耗时
- web worker:new一个worker,独立出浏览器进程,避免js线程阻塞
如何保证项目质量?
- 需求分析阶段:和各方干方系人(产品经理、项目经理、开发团队成员)充分沟通,确定目标需求、性能需求、技术需求等。落地切实的需求分析报告;明确项目周期,里程碑
- 开发阶段:前期搭建项目框架,确保可拓展性和可维护性;统一开发团队开发规范,包含命名规范、代码规范、注释规范等;有计划安排code review
性能优化
如何优化网页加载速度
- 减少资源大小
- 压缩css、js和图片文件:使用工具uglifyjs、cssNano
- 使用现代图片格式:webP,提供更好压缩率
- 减少请求次数
- 合并css和js文件,减少http请求数量
- 使用cdn:通过将资源分布到多个服务器,减少资源加载时间
- 浏览器缓存:利用http缓存控制,存储静态资源
- 优化渲染路径:
- 消除阻塞渲染的css和js:异步加载非关键css和js
- 关键css内联:首屏必要的css直接写在html
js放在head和body里有什么区别
- 加载顺序:放head会在页面加载之前执行js,放body会在页面加载完成后再执行
- 代码依赖:如果js要操作页面dom,需要放在body里确保元素已经加载
- 全局变量和函数:放在head里加载的js的全局函数和变量,在页面整个生命周期都能用
- 页面渲染:如果js执行会影响布局和样式,放在head里可能会导致页面渲染延迟
开放问题
前端的难点有哪些?
工程化:
- 代码规范和一致性:成员编码风格不一样,命名不规范、提交不规范
- 模构建和部署
性能优化:
- 首屏性能优化
- 多端适配
介绍技术挑战比较高的项目?
介绍一下业务复杂性比较高的项目
大屏优化都做了哪些操作?
-
代码分割和懒加载:降低首屏加载时间
- 动态导入文件
- 路由懒加载
- vite手动分包,会对动态导入的模块默认分包
- 预加载高频依赖
-
防抖节流:优化交互
-
使用web worker进行复杂计算
-
按需引入第三方库lodash、echarts
-
使用websocket通信
-
仅更新变动数据
TS问题
什么是 TypeScript?为什么使用它比普通 JavaScript 更有优势?
TypeScript 是 JavaScript 的静态类型超集,可以编译为纯 JavaScript。通过引入静态类型,它允许开发人员在编译时而不是运行时捕获与类型相关的错误。这可以减少错误,提高代码可读性,并通过增强的工具(例如自动完成和代码导航)提供更高效的开发体验
TypeScript 中的 any 类型和 unknown 类型有何不同?
any 和 unknown 都代表 TypeScript 中的任何值。但有一个关键的区别:any 绕过了编译器的类型检查,本质上关闭了 TypeScript 对该变量的好处。另一方面,unknown 保持类型检查完整,确保在对变量执行操作之前断言或缩小变量的类型
联合类型(Union Types)与交叉类型(Intersection Types)有何区别?
联合类型是一种表示一个值可以属于多种类型之一的方式。例如,如果函数接受字符串和数字作为参数,则可以将其键入为 function example(arg: string | number)。交叉类型表示变量同时具备多个类型的属性,例如 A & B
vue3中的泛型你怎么理解
ts中的泛型,是为了在声明函数、接口、类的时候还不确定数据类型,先使用的一个占位符。等到实际调用再声明类型 常规开发过程中比较常见的是函数的泛型和接口的泛型
JS问题
js的数据类型有哪些
基本数据类型:
- Number
- String
- Undefined
- Null
- Symbol
- Boolean
引用数据类型:
- Object
- Array
- Function
js数组常用的方法都有哪些
增:
- push:尾巴加--->length
- unshift:头部加--->length
- splice:splice(开始下标,删除个数,新增数据)--->[]
- concat:a.concat(b)--->新数组
删:
- pop:尾巴删--->删除item
- shift:头部删--->删除item
- splice:splice(开始下标,删除个数,新增数组)--->删除item
- slice:slice(开始下标,结束下标)--->新数组,不会影响原数组
改:
- splice:splice(开始,删除个数,新增数组)
查:
- indexOf:是否存在,在返回index,不在返回-1
- includes:true/false
- find:匹配到第一个元素
排序方法:
- reverse:数组反转
- sort:数组排序
迭代方法:
- some:任意一个满足条件,true/false
- every:每一个满足条件,true
- forEach:没有返回值
- filter:满足条件,返回一个新数组
- map:返回函数调用后的结果数组
说说foreach和map的区别
- 返回值:foreach没有返回值,map返回一个新数组
- 链式调用:foreach不支持,map可以
- 修改原数组:foreach自己不会修改原数组,map不修改原数组
- 用途不同:foreach用于执行有副作用函数操作,map用于生产新数组
什么是原型链
就是一种机制,用来实现对象属性和方法的继承,每个对象都会有一个prototype属性,如果自身上没有需要的属性和方法,就会通过prototype溯源原型对象查找。
遍历对象的所有属性
for……in、Object.keys()、object.values()、Object.entries()
说说深拷贝和浅拷贝,如何实现
共同点:深拷贝和浅拷贝都是把数据复制一份
区别:对于引用地址的数据,浅拷贝只会拷贝引用指针,深拷贝会把指针指向的数据完全复制
深拷贝的实现:
- JSON.stringfy(),缺点:对于undefined、函数数据的拷贝会有遗漏
- Loadash的cloneDeep方法
- 手写递归拷贝
浅拷贝的实现:
- Object.assign()
- Array.prototype.concat()
- 拓展运算符复制
递归的定义,用到的场景有哪些
定义:函数自己调用自己
应用场景:递归菜单列表、深拷贝递归、角色页面权限递归
解释一下闭包
是什么: 外部函数返回一个内部函数,内部函数引用外部函数作用域变量,外部函数运行完,但是内部函数任然引用变量,变量不得回收
如果调用外部函数10次,会产生多少个闭包?
会产生10个闭包,每次调用都会开辟一个新的作用域,隔离变量单独存储。不会互相影响
箭头函数可以用作构造函数吗?
不行,因为箭头函数没有自己的this和prototype属性,不能通过new一个实例对象。
js为什么会有浮点精度问题?怎么解决
为什么: 因为遵循的计算标准没有办法把十进制小数转换成二进制,导致运算误差
解决办法:
- 使用高精度计算库 bignumber
- 使用toFixed固定小数位数
- 先乘系数扩大计算后再还原
说说对事件循环的理解
为什么需要事件循环:JS是单线程语言,在JS引擎中同一个时间只能执行一个函数,为了避免竞争阻塞就需要事件循环来调配。
是什么:在事件循环中把js代码执行区分成同步任务和异步任务(宏任务、微任务)。
执行顺序:同步任务-->微任务-->宏任务
宏任务:
- 定时器
- UI渲染
- I/O操作
微任务:
- promise.then
- async/await
例子:
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
console.log('script start')
setTimeout(function () {
console.log('settimeout')
})
async1()
new Promise(function (resolve) {
console.log('promise1')
resolve()
}).then(function () {
console.log('promise2')
})
console.log('script end')
输出
script start、async1 start、async2、promise1、script end、async1 end、promise2、settimeout
有一个 setTimeout(()=>{}, 1000),这个回调是什么时候进入到队列里排队
在settimeout被调用时,会被添加到定时器任务队列,等计时时间到了,会把回调函数推到任务队列中等待栈空执行
解释一下链表
是什么:一种存数据的数据结构,包含节点(数据域、指针域),头指针(节点为null)、尾指针(尾节点为null)
类型: 单向链表、双向链表、循环链表
说说对promise的理解
是什么: promise是es6中提出的新语法,主要是用来解决js回调函数地狱的问题 包含状态: 主要包含三种状态,一经确定就不会再更改
- pending:初始状态
- fulfilled:成功,resolve
- rejected:失败
包含的实例方法: promise回调函数提供了三种实例方法用于日常开发
- then:用于做成功操作
- catch:异常场景操作,ps如果是使用promise.all方法的catch会被内层的catch影响,只触发一次
- finally:无论成功失败都会执行的操作
常用实例方法:
- promise.all([p1,p2,p3]):返回的状态受入参影响,只有所有的promise实例返回fulfilled才会返回fulfilled
- promise.race([p1,p2]):入参的promise实例哪个响应的快,就返回哪个
- promise.allSettled:等所有的都状态都改变,才结束
- promise.any:只要有一个fulfilled就返回,全部rejected才返回rejected
浏览器问题
说说编程里进程和线程的概念
进程: 操作系统中对资源和内存分配的基本单位
特点: 每个进程会分配独立空间和资源,各个进程之间相互隔离;创建和销毁需要的资源多;生命周期分为:创建、就绪、运行、堵塞、终止等
线程: 进程的执行最小单位,一个进程会有多个线程
特点: 线程可以共享进程资源,线程没有独立空间,一个线程错误崩溃可能导致整个进程崩溃
浏览器环境下,js执行在哪些进程哪些线程?
进程: 渲染进程:负责页面内容解析,js执行和页面的渲染。每个单独的页签都是一个单独的进程,互不影响。
线程:
- JS引擎线程:负责解析和执行js代码
- GUI渲染线程:解析html和css,负责渲染页面,和js线程互斥。js执行时会挂起等待
- 定时器线程:管理定时器任务。
- HTTP请求线程:处理ajax和websoket通信任务
浏览器为什么会有跨域限制?怎么解决
跨域限制:
- 安全性:避免跨站脚本注入(XXS)、跨站请求伪造(CRSF)网络攻击
- 防止数据窃取:防止恶意网站获取非法信息
- 遵守同源策略:要求同一站点的脚本只能获取同一站点的资源。同源:协议、域名、端口相同
解决办法:
- JSONP:在script标签里发送GET请求,数据量有限
- 代理服务器:本地代理-->vite、webpack代理/服务器代理-->nginx代理
- CORS:服务端配置允许跨域,http请求增加请求头
DNS协议是什么?说说DNS查询的过程
是什么: DNS是互联网的一种机制,把访问地址的域名查询转换成实际的IP地址
查询方式:
- 递归查询:发送请求给接受者,一定会收到目标地址
- 迭代查询:发送请求给接收者,会返回给新的线索;发送者继续发送新请求
域名缓存:
- 浏览器缓存:计算机会在浏览器中保存域名和ip地址的映射表
- 操作系统缓存:用户自己配置的host文件
查询过程:
- 查浏览器缓存:得到一个域名地址,先查浏览器的DNS映射表
- 查操作系统缓存:如果没命中,就采用递归查询继续查计算机里的映射表
- 迭代查询:如果没有命中,就发送请求给根服务器迭代,得到二级域名继续请求,得到三级域名继续请求。直到拿到ip地址
- 缓存操作系统映射表、浏览器映射表
浏览器从输入 URL 到页面展示的整个过程
主要过程为:
- 解析URL
- DNS查询
- TCP三次握手连接
- HTTP请求
- 响应请求
- 页面渲染
详细过程:
- 解析URL:协议、ip地址或域名、端口、路径、查询字符
- DNS查询:查询目标服务器的ip地址
- TCP连接:与目标服务器建立连接
- HTTP请求:发送http请求,带上请求头、请求体
- 响应请求:得到服务器的响应数据,状态行、响应头、响应体
- 页面渲染:html解析dom树,css解析css树;把两者合成render渲染树;布局渲染树dom尺寸和位置;绘制渲染树dom样式;浏览器把数据发送给GPU,绘制到屏幕上
HTTP问题
websoket的安全问题
websoket支持客户端和服务端的双向通信,可以及时的推送消息。
信息泄漏:通信不加密,数据被第三方窃取。例如用户信息,聊天信息等
劫持和篡改:攻击者劫持篡改通讯信息
应用场景:
- 没有加密的连接:使用ws协议而非wss协议容易被中间人攻击,特别是在公共wifi下,容易被拦截未加密信息
- 身份验证不健全:websoket是通过http协议建立的,如果身份验证不健全,容易被攻击者跳过用户验证连接并访问服务器
- 跨站websoket劫持
解决办法
- 使用wss加密协议
- 带token,对身份信息验证
- 防止跨站websoket劫持,用cookie的samesite属性,确保浏览器仅在同一站点携带cookie、设置orgin请求头确保请求来自信任的域
- 消息加密,服务端解密
网络安全:(二十)Vue 项目中的 WebSocket 安全:防止信息泄漏与劫持_vue令牌防止窃取-CSDN博客
websocket的心跳重连是什么?
原因:网络不稳定或长时间不进行通信时保持活跃的一种手段
方法:定时发送心跳包,等待后台的响应。发现消息响应异常后主动关闭连接,重新发起长连接请求。需要设定等待时间,避免多度频繁的请求接口
网络攻击常见的方式
跨站脚本攻击(XSS):注入恶意js脚本,截取用户信息执行非法操作
跨站请求访问(CSRF):诱导用户执行非自愿的请求
SQL注入:表单数据提交不做校验
网页中的图片资源为什么要放在不同的域名下?
因为浏览器对同一个域名发起的请求有数量限制,http请求限制6个,如果需要获取大量图片,发起过多请求可能会出现阻塞的情况。所以放在不同的域名下,可以避免请求堵塞,加速获取速度
浏览器与服务器建立一个tcp连接后,完成一个http请求是否会关闭?
- http1.0:建立一个TCP连接只能进行一次HTTP请求就会断开
- http1.1:升级之后,建立一次TCP连接,可以进行多次HTTP请求,但是请求需要按顺序返回,也会出现等待的情况
- http2.0:升级之后,建立一次TCP连接,可以多路复用,多次请求乱序返回
HTTP2.0和HTTP1.0有哪些差异?
HTTP1.0:建议一次TCP请求可以多次通信,但是每次发送完请求需要等前次请求完成,才能得到新的响应数据。
例如:加载html文件的同时,客户端还在给服务端发送请求。可能客户端已经发送了三四个新的请求,但是服务端响应时需要按顺序响应。
HTTP2.0:
- 服务器推送:请求一个html文件,会把相关的css文件一起返回
- 多路复用:客户端发送的多个请求,服务端不需要等待,可以乱序返回
浏览器http请求的并发性体现在哪里?并发请求数量有没有限制?
体现在: 浏览器可以和服务器建立多个TCP连接,在TCP连接中进行多个HTTP请求
chrome浏览器对同个域名最多支持建立6个TCP连接。
说说HTTP的请求头都有哪些
1.通用信息类
- Accept:指定接受的内容类型,accept:text/plain,tconnectionext/html
- Aceept-Charest:指定接受的字符编码集,iso-8859-5
- Aceept-Language:指定接受的语言
- Aceept-Encoding:指定浏览器接受的内容压缩编码类型
- connection:指定浏览器是否需要持久连接,Keep-alive,close
- Date:请求发出的时间和日期
2.认证授权类
- Authorization:授权信息
3.缓存控制类
- Cache-control:用于指定请求和响应遵循的缓存机制。常见:no-cache 不缓存,max-age 最大缓存时间,
4.内容相关类
- Content-length:请求消息正文长度
- Content-Type:请求和对应实体信息
5.其他类
- cookie:http发送请求会把,保存在该域名下所有cookie一起发送给服务器
- Host:指定的服务器和端口
- If-Mondified-Since:配合协商缓存的请求头,当请求内容超过有效期又被修改会返回更新,否则是304 not Mondified应答
- If-None-Match:
说说缓存机制
缓存机制涉及: 客户端缓存和服务端缓存
缓存的作用: 减少网络请求、加快页面加载速度、降低服务器负载
缓存类型:
- 强缓存:浏览器发送请求前,先检查本地资源是否有效,如果有效直接用,不需要给服务器发送请求。通过cache-control和expires请求头实现,expires基于服务器指定过期时间
- 协商缓存:浏览器发送请求结合修改时间给服务器,如果数据没过期浏览器返回304,使用本地缓存;如果数据过期了,服务器返回最新数据和最新更新时间。通过Etag和last-modified实现
- service worker缓存:是一种运行在浏览器后台的脚本
强缓存和协商缓存的具体实现
强缓存: 主要通过请求头expires和cache-control实现,cache-control优先级比expires高,且expires取客户端时间,可能客户端和服务器时间会有差异,会导致混乱
1.webpack配置中配置文件【contenthash】的hash值,通过文件hash值判断是否文件改变
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/',
assetModuleFilename: '[name][contenthash][ext]',
},
};
2.设置nginx代理的强缓存和过期时间
alias /path/to/static/files;
expires 30d; # 设置缓存时间为30天
}
协商缓存: 主要通过设置请求头Etag和last-modified实现
1.Etag搭配请求头if-None-Mondified使用 2.last-modified搭配请求头if-Modified-Since使用 3.etag优先级比last-modified高
webpack
module.exports = {
output: {
filename: 'bundle.[contenthash].js',
path: path.resolve(__dirname, 'dist'),
},
};
nginx
location /static/ {
alias /path/to/static/files;
if_modified_since exact;
add_header ETag "your_etag_value";
}
前端项目中,强缓存和协商缓存的配置_如何开启强缓存-CSDN博客
webpack和vite问题
webpack 打包流程、优化策略
- 识别入口文件,从入口文件开始解析依赖
- 模块解析,处理module里配置的不同loader,按顺序解析生成依赖关系图
- 模块转换,把非js模块转成js模块
- 代码优化,通过tree shaking移除未使用代码,通过splitchunks提取公共代码
- 输出打包文件
优化策略:
- 动态导入、按需加载
- 代码分割、预加载
- cdn引用
webpack两个文件入口怎么办
-
需要配置两个入口
-
使用splitChunks提取公共模块,避免重复打包
-
需要为每个入口生成对应的html文件
const path = require('path');
module.exports = {
entry: {
main: './src/main.js',
admin: './src/admin.js'
},
output: {
filename: '[name].[contenthash].js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(png|jpg)$/,
type: 'asset/resource'
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/main.html',
filename: 'main.html',
chunks: ['main', 'vendors', 'common']
}),
new HtmlWebpackPlugin({
template: './src/admin.html',
filename: 'admin.html',
chunks: ['admin', 'vendors', 'common']
})
],
optimization: {
splitChunks: {
chunks: 'all',
minSize: 10000
}
}
};
Webpack 怎么建立模块间的依赖关系
- 入口:先识别入口文件,从入口文件开始解析所有的引入,构建依赖树
- 模块识别:通过loader的配置,识别非js模块文件并转化成js代码
- 依赖解析:然后解析别名、模块路径等
- 代码分割和树摇减少冗余代码
对loader和plugin的理解,以及常用的loader、plugin
loader:是用来在webpack对js以外的文件处理的加载器,把非js模块转成js语言识别的代码
常用的有:css-loader、style-loader、eslint-loader、sass-loader、babel-loader
plugin:是给webpack增加插件功能
常用的有:HtmlWebpackPlugin、TerserWebpackPlugin(压缩 JavaScript 代码(生产环境必备))、CleanWebpackPlugin(构建前清理输出目录,避免旧文件残留)
如何优化首屏加载速度?
- import动态导入,利用tree-shaking移除冗余代码
- 代码分割
- 预加载关键资源,link rel=“preload”
- 使用CDN加速静态资源
vite的性能优化
- 代码分割(分包):将不常变动的第三方库分割出来,修改文件时浏览器不会冗余请求
- treeshaking(树摇):vue3默认开启树摇,不需要额外配置,但是需要是ES6模块才行
- CDN加速:可以配置需要加速的资源
说说webpack和vite的区别
- webpack是基于模块打包,vite开发环境无需打包
- vite冷启动更快,热更新更高效
CSS问题
移动端怎么适配不同的屏幕?
- 正确配置viewport,控制缩放比
- 使用flex弹性布局
- 使用css媒体查询,根据不同宽度适配不同样式
- 使用vw/rem单位
怎么根据设计稿的尺寸计算出 rem
1、需要根据设计稿先计算出根字体大小,例如750px/10,1rem就是75px,也可以把基准值设定的更好计算如100px
2、使用工具插件计算转换后的数值,如postCss插件,可以直接把px单位转换成rem
用css创建一个三角形原理
给元素的宽高设为0,通过边框绘制出来
width: 0;
height: 0;
border-top: 40px solid transparent;
border-left: 40px solid transparent;
border-right: 40px solid transparent;
border-bottom: 40px solid #ff0000;
display:none和visibility:hidden有什么区别
- display:none:在文档布局中不再分配空间,触发回流+重绘
- visibility:hidden:在文档流中保留原有的空间,触发重绘
块级格式化上下文的理解
块级格式化上下文(BFC),是说页面上一个独立的容器,容器里的子元素不受外面元素的干扰,是一个独立的块。 BFC内部块怎么布局
- 内部的box会根据垂直方向按顺序放置
- 同一个BFC里相邻的box的margin会发生重叠
- 每个元素的margin box的左边,会和包含块border box 的左边相接触
- BFC是一个独立容器,与容器外不会互相影响
- 计算BFC高度时,浮动元素也会计算在内 开启方式:
- display:inline-block
- overflow不为visible(默认)
- position为absolute和fixed
- float不为none开启(默认)
CSS 定位有哪几种
静态定位:static(默认定位方式)
非静态定位:
relative:相对定位,不脱离文档流,参考点在自身原始位置
absolute:绝对定位,脱离文档流,参考点在非静态定位祖先
fixed:固定定位,脱离文档流,参考点在浏览器视口
sticky:粘性定位,不脱离文档流,参考点在浏览器视口边缘
说说flex
flex是一个弹性布局方式
- display:flex:一个flex容器
- flex-direction:定义主轴的方向
- justify-content:主轴上的对齐方式
- align-items:交叉轴上对齐方式
- flex-wrap:容器内不足放下所有项目的情况
- flex-shrink:收缩比例
- flex-grow:扩展比例
说说transform
css的transform属性能够对元素平移、缩放、旋转和倾斜 transform属性搭配各种函数
- 平移:translate(x,y)
- 缩放:scale(x,y)
- 旋转:rotate(90deg)
transform默认以元素中点为原点,可以通过transform-origin设置原点
九宫格怎么实现
用flex布局实现
display:flex;
flex-wrap:wrap;
css中的百分比都是相对于谁的?
//1、width、height的10%相对于父元素
width:10%;
height:10%;
//2、top、left、bottom、right是相对于父元素的宽高
只有非static元素才能设置
position:relative;
top:10%;
left:10%
如果是absolute定位则都是相对于非static的父元素的宽高
position:absolute;
top:10%;
left:10%;
width:10%;
height:10%
如果是固定定位fixed,则是相对于视图窗口宽高计算
position:fixed;
top:10%;
left:10%
//3、transform的translate平移是根据自身比例宽高
transfrom:translate(50%,50%)
//4、margin-top和margin-left是根据父元素宽高
//5、border-radius:50%;根据自身
//6、background-size:100% 100% ;根据背景区宽高
实现对角线布局方案
flex实现: 用flex结合align-self和flex结合justify-content
说说CSS的性能优化有哪些方面
- 首屏使用内联样式,解析完html立即渲染,不用再下载css文件
- 异步加载CSS文件,js单线程,css请求下载解析完成前,会阻塞渲染,所以不太重要css样式可以晚加载
- 资源压缩,利用webpack模块化工具压缩css文件
- 减少重排操作
- 小图片转成base64编码
说说回流和重绘
浏览器渲染过程:
- 解析html生成dom树,解析css生成css树
- 把dom树和css树绘制到一起生成渲染树
- layout(回流)根据渲染树确认dom的几何信息
- painting(重绘)根据渲染树和几何信息,得到绝对像素
- display:将像素发给GPU,显示到屏幕上
先回流,再重绘。所以回流一定是伴随着重绘的
如何减少回流:
- 需要实现动画或者dom修改的,尽量用position的fixed和absolute定位,使其脱离文档流
- 修改样式避免for循环一点点改,可以先把数据修改完一次性修改
- 避免使用table布局,table中每个元素大小和内容变化,都会导致整个table重新计算
- 使用css3硬件加速,transform、opacity、filters不会导致回流重绘
怎么实现页面的多主题色
- 主题色少:如果主题色比较少的话,可以定义好主题色变量,在后续的主题色通过变量引用,需要切换主题色时通过js切换主题色就能实现
- 主题色多:如果是主题色比较多,有ui给的色彩方案的话,这个时候可以维护主题色css文件,当切换主题色的时候,通过js切换引用文件实现
解释html5中canvas和html的区别
canvas:
- 通过js动态渲染像素点,拿不到dom,都是画布。操作单个对象需要重绘整个画布
svg:
- 基于xml的矢量图形技术,每个图像都有dom节点,性能比canvas差,特别是图形复杂时。
vue系列
说说对vue的理解
- vue是一套渐进式框架,可以安装需求逐步引入
- 拥有丰富的生态,官方的router、vuex、vite,还有element-ui、ant design
- 拥有响应式系统,vue2通过object.definpropety,vue3通过proxy实现的响应式做到视图和数据同步极大提高开发效率
- 可以根据业务模块,封装模块和代码,组件化模块,提高复用性
- 通过diff算法最小化dom操作,通过虚拟dom操作,一次渲染真实dom,减少性能开销
首屏加载速度慢怎么办?
原因:网络延迟、打包文件体积大、加载脚本时渲染内容阻塞
解决办法:
1、减小入口文件体积:路由懒加载按模块切分路由
2、静态资源本地缓存:采用http缓存、合理使用localstorage
3、ui框架按需引入
4、第三方js文件优先加载
5、使用服务端渲染
说说vue的diff算法
是什么:一种通过同层树节点进行比较的高效算法
比较方式:同层比较、两边向中间收拢、有相同节点就移动,没有见创建新节点
axios主要封装哪些方面?
1、设置请求头及请求头前缀,不同环境匹配不同路径
2、请求方法get、post二次封装,更方便使用
3、请求拦截器
4、响应拦截器:根据不同的状态码做不同操作,500服务器异常、403无权限、401token过期
什么是虚拟dom?如何实现一个虚拟dom?
是什么:虚拟dom就是对真实dom的拷贝,vue可以通过虚拟dom的完成对节点的操作,再通过diff算法计算出最小单元的修改,再去操作真实dom,以此减少dom操作,提高性能
如何实现:
vue2和vue3实现响应式的不同
- vue2中的响应式是通过对象的definePropety方法实现的,vue会循环递归对象里所有的属性,给属性增加get、set方法。当组件的watcher发现数据更新了,会通过set触发视图的更新。当面对一些数据量大的计算时,是非常消耗性能的,而且对新增加的属性会丢失响应式,需要$set重写,或者用数组的方法添加
- vue3中是通过es6的proxy代理实现的响应式,也是给代理对象增加get、set方法,但是proxy只有在需要的时候才追踪依赖。proxy可以拦截对象的读写删的操作,数据不会丢失响应
说说v-if和v-for的优先级
- vue2中v-for的优先级更高,和v-if同时使用会导致每个循环都判断,浪费性能
- vue3中v-if的优先级更高
说说v-once和v-memo
- v-once:vue的内置指令,只渲染一次。重新渲染时,组件元素会被作为静态节点直接复用
- v-memo:vue3.2新增的内置指令,接受传值,只有在传值变化时,重新渲染组件
keep-alive用过吗
keep-alive主要是用来保留组件实例的,在一些需要切换tab页场景的时候会用到。使用keep-alive就能保存组件实例的数据,等下次进来的时候还可以看到。如果需要更新数据的话,有一个actived钩子可以使用,去刷新数据。可以利用includes去动态配置哪些组件要保留哪些不保留,更加灵活使用
vue的指令
- v-html
- v-bind
vue3问题
Vue 3 和Vue 2 有什么区别?
1、实现响应式方法不同,用proxy替代了definePropety
2、使用组合式api,可以把业务数据放在一起,方便维护
3、体积更小,性能更好
4、新增了组件特性:teleport传送门,组件dom送到body上;suspense异步组件
vue3相对于vue2的性能提升
1、运行体积更小,运行更快(移除vue.prototype全局污染;tree-shaking更彻底,移除不使用api)
2、vue3对虚拟dom计算有更高效的diff算法,减少同层dom比较次数
3、用proxy代替definePropety实现响应式,更高效也降低内存占用
ref 和 reactive 有什么区别?
ref:创建基础数据类型的响应式对象,reactive创建对象响应式数据
vue3的生命周期有哪些?
创建:setup
挂载:onBeforeMount、onMounted
更新:onBeforeUpdate、onUpdated
卸载:onBeforeUnmount、onUnmounted
vue3实现组件通信的方法有哪些?
defineProps/defineEmits:父子组件通信
provide和inject:跨层级组件通信,多层级派发
event bus:使用mitt或自定义事件中心
vuex/pinia:状态管理库
watch和watchEffect有什么区别?
1、watch需要显示的监听数据源,而watchEffect不需要,它会自动追踪使用到的响应式数据
2、watchEffect会在初始化时立即执行一次,watch不会立即执行,需要配合immediate才行
路由导航的几种方法?replace和push区别?
vue3中用useRouter钩子函数、vue2中用this.$router跳转、还有router-link跳转
replace不会留下记录,不能够返回上一页;push会留下,也能回退上一页
介绍一下路由守卫,前置守卫后置守卫入参都有哪些?应用场景?
全局守卫:前置守卫beforeEach、解析守卫beforeResolve、后置守卫afterEach
路由独享守卫:独享守卫beforeEnter
组件内守卫:前beforeRouterEnter、更新beforeRouterUpdate、后beforeRouterLeave
应用场景:用户认证和授权、动态路由管理、页面离开确认、数据加载优化
自定义directive指令中常用的钩子有哪些?
bind:第一次绑定到元素时调用
inserted:元素插入父节点时调用
unbind:解绑时调用
在vue中怎么实现响应式,依赖收集?
vue2问题
1、说说slot插槽,使用场景有哪些?
插槽就是在template中先占坑,需要什么内容从父组件填
插槽分为:默认、具名插槽、作用域插槽
使用场景:封装table通用组件、弹框显示内容
说说computed和watch的区别
相同点:coumputed和watch都能实现对数据的监听
区别:computed具备缓存性,只有监听的数据发生变化才会重新计算,且不支持异步操作;watch每次取数据都会重新计算一次,且支持异步操作
移动端
说说怎么解决低端机型的卡顿优化
- 使用css3硬件加速
- 数据分页请求回来渲染
- 做防抖节流的控制
- 避免css选择器的深层嵌套
- 关键路径资源预加载
- 减少dom操作,少不了的批量处理
小程序的生命周期都有哪些?
应用级:全局只触发一次
- onlaunch:初始化加载应用
- onshow:后台进前台
- onhide:前台进后台
- onError:捕获全局异常
- onPageNotFound:找不到页面时配置404页面
页面级:
- onload:初始化第一次加载页面
- onshow:
- onhide
- onready:渲染完成,可以拿到dom做操作
- onUnload:卸载页面
- onReachBottom:滚动触底
- onPullDownRefresh:下拉刷新
生命钩子函数的顺序
onLaunch-->onShow-->onLoad-->onShow-->onReady
onLoad和onshow的区别?
onload:只在页面加载时渲染一次 onshow:每次从后台进入都会渲染一次
为什么不能直接修改data里的数据?
小程序里视图层和逻辑层是分开的,直接修改data里的数据视图不会更新,只有通过setData修改才能同步更新视图层
setData的性能优化有哪些?
- 合并更新,避免频繁的调用
- 局部更新,避免全量更新
- 避免复杂计算逻辑,只负责单一功能
如何避免setData传参过大?
微信小程序对单次传参数据限制在1m,可以通过分批次更新数据和if代替hidden控制组件的显示隐藏,减少渲染负担
wx:if和hidden一起使用会怎么样
以wx:if为准
小程序路由跳转实现
navigateTo redirectTo navigateBack:返回上一页 relaunch:关闭所有页面,打开某个
wx:navigateTo和wx:redirectTo的区别
navigateTo:会保留页面栈,支持跳转 redirectTo:不保留页面栈,跳转,不支持返回
如何实现自定义导航栏
- 在页面配置navigationStyle:custom
- 在页面中通过监听onBackPress事件
如何实现下拉刷新上拉加载?
下拉刷新:在页面配置中开启enablepulldownRefresh:true,在页面监听onPullDownRefresh事件 上拉加载:onReachBottm
常用的wx原生api有哪些?
- wx:showToast:显示提示框
- wx:showModal:显示对话框
- wx:uploadFile:上传文件
- wx:setStorageSync、getStorageSync:同步读写本地缓存
- wx:setStorage、getStorage:异步读写本地缓存
- wx:getLocation:获取当前地址
- wx:chooseImage:选图片
setStorageSync和setStorage有什么区别
相同点:都能够储存数据
不同点:
- setStorageSync:会阻塞主线程,适用于数据量少且关键数据的存储,如果存储大数据且频繁调用,会导致渲染延迟卡死
- setStorage:不会阻塞主线程,适用于高频大数据量存储
自定义组件间如何通信?
- 父子组件通信:properties和triggerEvent
- 兄弟通信:全局事件总线eventbus、全局状态管理
- 跨页面通信:navigateTo传参或者本地存储setStorageSync