一切又从头
自我介绍,部门介绍
编码方式
不按套路出牌,只知道:
- UTF-8:Unicode字符的实现方式之一,它使用1-4个字符表示一个符号,根据不同的符号而变化字节长度
- ASCII
承认非科班不知道,没学过
其实有几种我知道的,补充:
- Unicode:包含世界上所有的字符,是一个字符集
- GBK/GB2312/GB18030:GBK/GB2312表示简体中文,GB18030表示繁体中文。
计算机网络
TCP在哪一层
TCP 3次握手 4次挥手
HTTP 1.0/1.1/2.0区别
HTTP 1.1
- HTTP1.1默认开启长连接,在一个TCP连接上可以传送多个HTTP请求和响应。使用 TCP 长连接的方式改善了 HTTP/1.0 短连接造成的性能开销。
- HTTP1.1支持管道(pipeline)网络传输,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。
HTTP 2.0
- 二进制分帧:HTTP 2 采用二进制格式传输数据,而非HTTP 1.x 的文本格式,二进制协议解析起来更高效。
- 多路复用:HTTP 2 同域名下所有通信都在单个连接上完成。
- 头部压缩:只发送头部数据的差异数据,而不是全部发送,减少头部的信息量
- 服务端推送:服务端可以在发送页面HTML时主动推送其它资源,而不用等到浏览器解析到相应位置,发起请求再响应。例如服务端可以主动把JS和CSS文件推送给客户端,而不需要客户端解析HTML时再发送这些请求。服务端可以主动推送,客户端也有权利选择是否接收。如果服务端推送的资源已经被浏览器缓存过,浏览器可以通过发送RST_STREAM帧来拒收。主动推送也遵守同源策略,服务器不会随便推送第三方资源给客户端。
HTTP的keep-alive是干什么的?
HTTP1.0中,默认短连接,在HTTP请求头中加入Connection: keep-alive来告诉对方这个请求响应完成后不要关闭
keep-alive的优点:
- 较少的CPU和内存的使用(由于同时打开的连接的减少了)
- 允许请求和应答的HTTP管线化
- 降低拥塞控制(TCP连接减少了)
- 减少了后续请求的延迟(无需再次握手)
- 报告错误无需关闭TCP连接
HTTP缓存
强缓存:HTTP 1.0 Expires 绝对时间,HTTP 1.1 Cache-Control 相对时间
Cache-Control字段:
- public可以被所有⽤户缓存,包括终端和CDN等中间代理服务器
- private只能被终端浏览器缓存,不允许中继缓存服务器进行缓存
- no-cache,先缓存本地,但是在命中缓存之后必须与服务器验证缓存的新鲜度才能使用
- no-store,不会产生任何缓存
协商缓存:
当第⼀次请求时服务器返回的响应头中没有Cache-Control和Expires或者Cache-Control和Expires过期抑或它的属性设置为no-cache时,那么浏览器第二次请求时就会与服务器进行协商。
如果缓存和服务端资源的最新版本是⼀致的,那么就无需再次下载该资源,服务端直接返回304 Not Modified 状态码,如果服务器发现浏览器中的缓存已经是旧版本了,那么服务器就会把最新资源的完整内容返回给浏览器,状态码就是200 OK。
协商缓存也分为2种:
Last-Modified/If-Modified-Since:
服务器的资源的最新修改时间,速度较快
ETag/If-None-Match
ETag是根据资源内容进行hash,生成一个信息摘要,只要资源内容有变化,即可确定客户端的缓存资源是否为最新,精度较⾼。
Event Loop与宏任务微任务:
- 首先执行同步代码,这属于宏任务
- 当执行完所有同步代码后,执行栈为空,查询是否有异步代码需要执行
- 执行所有微任务
- 当执行完所有微任务后
- 然后开始下一轮 Event Loop,执行宏任务中的异步代码,也就是
setTimeout
中的回调函数
前端安全
XSS和CSRF八股文
HTML与CSS
盒式模型
defer和async是什么,有什么区别
- defer:异步加载脚本,script被异步加载后并不会立刻执行,而是等待文档被解析完毕后。
- async:同样是异步加载脚本,区别是脚本加载完毕后立刻执行,这导致async属性下的脚本是乱序的,对于script有先后依赖关系的情况,并不适用。
Vue和React
Vue和React的区别
自作多情加了小程序的区别
Vue v-model
支持双向绑定,相比于 React 来说使用更加方便,改变数据方式不同,Vue 修改状态相比来说要简单许多,React 需要使用 setState
来改变状态,并且使用这个 API 也有一些坑点。并且 Vue 的底层使用了依赖追踪,页面更新渲染已经是最优的了,但是 React 还是需要用户手动去优化这方面的问题。
React 需要使用 JSX,有一定的上手成本,并且需要一整套的工具链支持,但是完全可以通过 JS 来控制页面,更加的灵活。Vue 使用了模板语法,相比于 JSX 来说没有那么灵活,但是完全可以脱离工具链,通过直接编写 render
函数就能在浏览器中运行。
(redux的作者Dan Abramov,同时他也是React的核心成员之一)
Vue中的key的作用和diff算法
- 同级比较,再比较子节点
- 先判断一方有子节点一方没有子节点的情况(如果新的
children
没有子节点,将旧的子节点移除),再比较都有子节点的情况(核心diff
) - 递归比较子节点
- 正常
Diff
两个树的时间复杂度是O(n^3)
,但实际情况下我们很少会进行跨层级的移动DOM
,所以Vue
将Diff
进行了优化,从O(n^3) -> O(n)
,只有当新旧children
都为多个子节点时才需要用核心的Diff
算法进行同层级比较。 - 新旧
children
中的节点只有顺序是不同的时候,最佳的操作应该是通过移动元素的位置来达到更新的目的,key
是children
中节点的唯一标识,以便能够在旧children
的节点中找到可复用的节点。
O(n^3)怎么来的?
不是很清楚。。。
看大佬的说明
关于O(n^3)怎么计算出来的
问题描述
原问题标题“React 和 Vue 的 diff 时间复杂度从 O(n^3) 优化到 O(n) ,那么 O(n^3) 和 O(n) 是如何计算出来的? ”
- 这里的n指的是页面的VDOM节点数,这个不太严谨。如果更严谨一点,我们应该应该假设
变化之前的节点数为m,变化之后的节点数为n。- React 和 Vue 做优化的前提是“放弃了最优解“,本质上是一种权衡,有利有弊。
倘若这个算法用到别的行业,比如医药行业,肯定是不行的,为什么?
React 和 Vue 做的假设是:
- 检测VDOM的变化只发生在同一层
- 检测VDOM的变化依赖于用户指定的key
如果变化发生在不同层或者同样的元素用户指定了不同的key或者不同元素用户指定同样的key,
React 和 Vue都不会检测到,就会发生莫名其妙的问题。但是React 认为, 前端碰到上面的第一种情况概率很小,第二种情况又可以通过提示用户,让用户去解决,因此
这个取舍是值得的。 没有牺牲空间复杂度,却换来了在大多数情况下时间上的巨大提升。
明智的选择!基本概念
首先大家要有个基本概念。
其实这是一个典型的最小编辑距离的问题,相关算法有很多,比如Git中
,提交之前会进行一次对象的diff操作,就是用的这个最小距离编辑算法。leetcode 有原题目,
如果想明白这个O(n^3), 可以先看下这个。对于树,我们也是一样的,我们定义三种操作,用来将一棵树转化为另外一棵树:
- 删除:删除一个节点,将它的children交给它的父节点
- 插入:在children中 插入一个节点
- 修改:修改节点的值
事实上,从一棵树转化为另外一棵树,我们有很多方式,我们要找到最少的。
直观的方式是用动态规划,通过这种记忆化搜索减少时间复杂度。
算法
由于树是一种递归的数据结构,因此最简单的树的比较算法是递归处理。
详细描述这个算法可以写一篇很长的论文,这里不赘述。
大家想看代码的,这里有一份
我希望没有吓到你。确切地说,树的最小距离编辑算法的时间复杂度是
O(n^2m(1+logmn))
,
我们假设m 与 n 同阶
, 就会变成O(n^3)
。
看图吧:
如上所示, 左侧树a节点依次进行如下对比:
a->e、a->d、a->b、a->c、a->a
之后左侧树其它节点b、c、d、e亦是与右侧树每个节点对比, 算法复杂度能达到O(n^2)
查找完差异后还需计算最小转换方式,这其中的原理我没仔细去看,最终达到的算法复杂度是O(n^3)
将两颗树中所有的节点一一对比需要O(n²)的复杂度,在对比过程中发现旧节点在新的树中未找到,那么就需要把旧节点删除,删除一棵树的一个节点(找到一个合适的节点放到被删除的位置)的时间复杂度为O(n),同理添加新节点的复杂度也是O(n),合起来diff两个树的复杂度就是O(n³)
Vue和React的diff算法有什么区别:
不知道了。
Vue 2.X进行diff时,调用patch打补丁函数,一边比较一边给真实的DOM打补丁
Vue2.X对比节点,当节点元素类型相同,但是className不同时,认为是不同类型的元素,删除重新创建,而react则认为是同类型节点,进行修改操作
① Vue 2.X的列表比对,采用从两端到中间的方式,旧集合和新集合两端各存在两个指针,两两进行比较,如果匹配上了就按照新集合去调整旧集合,每次对比结束后,指针向队列中间移动;
② 而react则是从左往右依次对比,利用元素的index和标识lastIndex进行比较,如果满足index < lastIndex就移动元素,删除和添加则各自按照规则调整;
③ 当一个集合把最后一个节点移动到最前面,react会把前面的节点依次向后移动,而Vue只会把最后一个节点放在最前面,这样的操作来看,Vue的diff性能是高于react的Vue3.x借鉴了 ivi算法和 inferno算法。在创建VNode时就确定其类型,以及在mount/patch的过程中采用位运算来判断一个VNode的类型,在这个基础之上再配合核心的Diff算法,使得性能上较Vue2.x有了提升。(实际的实现可以结合Vue3.x源码看)
该算法中还运用了动态规划的思想求解最长递归子序列
更多推荐阅读:聊一聊Diff算法(React、Vue2.x、Vue3.x)
vue 项目如何性能优化
这一问我没明白什么意思,没答上来,找了一下其实可以说的很多的
代码层面:
- 合理使用
v-if
和v-show
- 区分
computed
和watch
的使用 v-for
遍历为item
添加key
v-for
遍历避免同时使用v-if
- 通过
addEventListener
添加的事件在组件销毁时要用removeEventListener
手动移除这些事件的监听 - 图片懒加载
- 路由懒加载
- CDN加载依赖
- 第三方插件按需引入
SSR
服务端渲染,首屏加载速度快,SEO
效果好
Webpack 层面优化:
- 对图片进行压缩
- 使用
CommonsChunkPlugin
插件提取公共代码 - 提取组件的 CSS
- 优化
SourceMap
- 构建结果输出分析,利用
webpack-bundle-analyzer
可视化分析工具
手撕代码
比较两个版本号 version1 和 version2。
如果 version1 > version2 返回 1,如果 version1 < version2 返回 -1, 除此之外返回 0。
你可以假设版本字符串非空,并且只包含数字和 . 字符。
. 字符不代表小数点,而是用于分隔数字序列。
例如,2.5 不是“两个半”,也不是“差一半到三”,而是第二版中的第五个小版本。
你可以假设版本号的每一级的默认修订版号为 0。例如,版本号 3.4 的第一级(大版本)和第二级(小版本)修订号分别为 3 和 4。其第三级和第四级修订号均为 0。
示例 1:
输入: version1 = "0.1", version2 = "1.1"
输出: -1
示例 2:
输入: version1 = "1.0.1", version2 = "1"
输出: 1
示例 3:
输入: version1 = "7.5.2.4", version2 = "7.5.3"
输出: -1
示例 4:
输入:version1 = "1.01", version2 = "1.001"
输出:0
解释:忽略前导零,“01” 和 “001” 表示相同的数字 “1”。
示例 5:
输入:version1 = "1.0", version2 = "1.0.0"
输出:0
解释:version1 没有第三级修订号,这意味着它的第三级修订号默认为 “0”。
我写的完整版代码:
function compare(version1, version2) { let arr1 = version1.split('.'), arr2 = version2.split('.') const len1 = arr1.length, len2 = arr2.length; const len = Math.max(len1, len2); while(arr1.length < len) { arr1.push(0); } while(arr2.length < len) { arr2.push(0); } for (let i = 0; i < len; i++) { let num1 = parseInt(arr1[i]), num2 = parseInt(arr2[i]); if (num1 < num2) { return -1; } else if (num1 > num2) { return 1; } } return 0; } console.log(compare("0.1", "1.1")); console.log(compare("1.0.1", "1")); console.log(compare("7.5.2.4", "7.5.3")); console.log(compare("1.01", "1.001")); console.log(compare("1.0", "1.0.0"));
全部评论
(13) 回帖