service worker/indexdb
- service worker 判断断网问题(workbox\workbox-webpack-plugin)
- indexdb,取上下20条数据,避免内存溢出(localforage)
- 如何定位内存问题(heapdump)
大文件切片上传
- blob.slice
- axios
2.1、前端并发控制 p-limit/pmaps、sindrrsohus) (https://github.com/sindresorhus/p-limit)
2.2 高并发的控制:(rate limit) (redis) - graphql
3.1 可请求所需的数据
3.2 post 和缓存 - jwt https://jwt.io/
jwt的好处? 不需要做数据存储,
分为三部分 header payload signature
闭包
手写 深度拷贝
1 | function deepCopy(obj){ |
华雄面试
什么是盒子模型
- 盒子模型有ie模型和 标准模型
- CSS盒模型本质上是一个盒子,封装周围的HTML元素,它包括:边距,边框,填充,和实际内容
- ie盒模型算上border、padding及自身(不算margin),标准的只算上自身窗体的大小 css设置方法如下
1
2
3
4/* 标准模型 */
box-sizing:content-box;
/*IE模型*/
box-sizing:border-box;
什么是静态式布局?什么是流式布局?什么是自适应式布局?什么是响应式布局?什么是弹性布局?
静态式布局(Static Layout)
布局特点:不管浏览器尺寸具体是多少,网页布局始终按照最初写代码时的布局来显示。常规的pc的网站都是静态(定宽度)布局的,也就是设置了min-width,这样的话,如果小于这个宽度就会出现滚动条,如果大于这个宽度则内容居中外加背景,这种设计常见与pc端。
优点:这种布局方式对设计师和CSS编写者来说都是最简单的,亦没有兼容性问题。
缺点:显而易见,即不能根据用户的屏幕尺寸做出不同的表现。
当前,大部分门户网站、大部分企业的PC宣传站点都采用了这种布局方式。固定像素尺寸的网页是匹配固定像素尺寸显示器的最简单办法。但这种方法不是一种完全兼容未来网页的制作方法,我们需要一些适应未知设备的方法。
流式布局(Liquid Layout)
流式布局(Liquid)的特点(也叫”Fluid”) 是页面元素的宽度按照屏幕分辨率进行适配调整,但整体布局不变。代表作栅栏系统(网格系统)。
网页中主要的划分区域的尺寸使用百分数(搭配min-、max-属性使用),例如,设置网页主体的宽度为80%,min-width为960px。图片也作类似处理(width:100%, max-width一般设定为图片本身的尺寸,防止被拉伸而失真)。
1、布局特点:屏幕分辨率变化时,页面里元素的大小会变化而但布局不变。【这就导致如果屏幕太大或者太小都会导致元素无法正常显示】
2、设计方法:使用%百分比定义宽度,高度大都是用px来固定住,可以根据可视区域 (viewport) 和父元素的实时尺寸进行调整,尽可能的适应各种分辨率。往往配合 max-width/min-width 等属性控制尺寸流动范围以免过大或者过小影响阅读
这种布局方式在Web前端开发的早期历史上,用来应对不同尺寸的PC屏幕(那时屏幕尺寸的差异不会太大),在当今的移动端开发也是常用布局方式,但缺点明显:主要的问题是如果屏幕尺度跨度太大,那么在相对其原始设计而言过小或过大的屏幕上不能正常显示。因为宽度使用%百分比定义,但是高度和文字大小等大都是用px来固定,所以在大屏幕的手机下显示效果会变成有些页面元素宽度被拉的很长,但是高度、文字大小还是和原来一样(即,这些东西无法变得“流式”),显示非常不协调。
自适应布局(Adaptive Layout)
自适应布局的特点是分别为不同的屏幕分辨率定义布局,即创建多个静态布局,每个静态布局对应一个屏幕分辨率范围。改变屏幕分辨率可以切换不同的静态局部(页面元素位置发生改变),但在每个静态布局中,页面元素不随窗口大小的调整发生变化。可以把自适应布局看作是静态布局的一个系列。
1、布局特点:屏幕分辨率变化时,页面里面元素的位置会变化而大小不会变化。
2、设计方法:使用 @media 媒体查询给不同尺寸和介质的设备切换不同的样式。在优秀的响应范围设计下可以给适配范围内的设备最好的体验,在同一个设备下实际还是固定的布局。
响应式布局(Responsive Layout)
即:创建多个流体式布局,分别对应一个屏幕分辨率范围。可以把响应式布局看作是流式布局和自适应布局设计理念的融合。
响应式几乎已经成为优秀页面布局的标准。
1、布局特点:每个屏幕分辨率下面会有一个布局样式,即元素位置和大小都会变。
2、设计方法:媒体查询+流式布局。通常使用 @media 媒体查询 和网格系统 (Grid System) 配合相对布局单位进行布局,实际上就是综合响应式、流动等上述技术通过 CSS 给单一网页不同设备返回不同样式的技术统称。
优点:适应pc和移动端,如果足够耐心,效果完美
缺点:(1)媒体查询是有限的,也就是可以枚举出来的,只能适应主流的宽高。(2)要匹配足够多的屏幕大小,工作量不小,设计也需要多个版本。
总结:
响应式与自适应的原理是相似的,都是检测设备,根据不同的设备采用不同的css,而且css都是采用的百分比的,而不是固定的宽度,不同点是响应式的模板在不同的设备上看上去是不一样的,会随着设备的改变而改变展示样式,而自适应不会,所有的设备看起来都是一套的模板,不过是长度或者图片变小了,不会根据设备采用不同的展示样式,流式就是采用了一些设置,当宽度大于多少时怎么展示,小于多少时怎么展示,而且展示的方式向水流一样,一部分一部分的加载,静态的就是采用固定宽度的了。
流式布局是用于解决类似的设备不同分辨率之间的兼容(一般分辨率差异较少);响应式是用于解决不用设备之间不用分辨率之间的兼容问题(一般是指PC,平板,手机等设备之间较大的分辨率差异)。
弹性布局(rem/em布局)
rem,em区别:rem,em都是顺应不同网页字体大小展现而产生的。其中,em是相对其父元素,在实际应用中相对而言会带来很多不便;而rem是始终相对于html大小,即页面根元素。
因为有些浏览器默认的不是16px,或者用户修改了浏览器默认的字体大小(因浏览器分辨率大小,视力,习惯等因素)。如果我们将其设置为10px,一定会影响在这些浏览器上的效果,所以最好用绝大多数用户默认的16作为基数 * 62.5% 得到我们需要的10px。
1 | html {font-size: 62.5%;/*10 ÷ 16 × 100% = 62.5%*/} |
实际项目设置成 font-size: 62.5%可能会出现问题,因为chrome不支持小于12px的字体,计算小于12px的时候,会默认取12px去计算,导致chrome的em/rem计算不准确。
针对这个现象,可以尝试设置html字体为100px,body 修正为16px,这样 0.1rem 就是 10px,而body的字体仍然是默认大小,不影响未设置大小的元素的默认字体的大小。
5、用em/rem定义尺寸的另一个好处是更能适应缩进/以字体单位padding或margin/浏览器设置字体尺寸等情况(因为em/rem相对于字体大小,会同步改变)。例如:p{ text-indent: 2em; }
css性能优化
1.内联首屏关键CSS(Critical CSS)
性能优化中有一个重要的指标——首次有效绘制(First Meaningful Paint,简称FMP)即指页面的首要内容(primary content)出现在屏幕上的时间。这一指标影响用户看到页面前所需等待的时间,而内联首屏关键CSS(即Critical CSS,可以称之为首屏关键CSS)能减少这一时间。
- 优点:内联CSS也可称为行内CSS或者行级CSS,它直接在标签内部引入,显著的优点是十分的便捷、高效;内联CSS能够使浏览器开始页面渲染的时间提前
- 缺点:但是同时也造成了不能够重用样式的缺点,如果代码行数到达一定长度不建议采用。通常内联CSS作为测试使用,可以查找代码中bug。
1
2
3<body>
<div style="width: 65px;height: 20px;border: 1px solid;">测试元素</div>
</body>
2.文件压缩
虽然文件压缩能够降低文件大小。但CSS文件压缩通常只会去除无用的空格,这样就限制了CSS文件的压缩比例。那是否还有其他手段来精简CSS呢?答案显然是肯定的,如果压缩后的文件仍然超出了预期的大小,我们可以试着找到并删除代码中无用的CSS。
3.优化重排与重绘
4.不要使用@import
最后提一下,不要使用@import引入CSS,相信大家也很少使用。
不建议使用@import主要有以下两点原因。
首先,使用@import引入CSS会影响浏览器的并行下载。使用@import引用的CSS文件只有在引用它的那个css文件被下载、解析之后,浏览器才会知道还有另外一个css需要下载,这时才去下载,然后下载后开始解析、构建render tree等一系列操作。这就导致浏览器无法并行下载所需的样式文件。
css动画
1 .css动画启用GPU加速,应用GPU的图形性能对浏览器中的一些图形操作交给GPU完成。canvas2D,布局合成,css3转换,css3d变换,webGL,视频
什么是闭包,闭包的用途
如果一个函数用到了它作用域外面的变量,那么这个变量和这个函数之间的环境就叫闭包。
闭包用途
1.模仿块级作用域
1 | function x(num){ |
3.储存变量
function S1(){
var a = 1
return function(){
return a++
}
}
var d = S1() // 100
undefined
d()
1
d()
2
d()
3
3.封装私有变量
我们可以把函数当作一个范围,函数内部的变量就是私有变量,在外部无法引用,但是我们可以通过闭包的特点来访问私有变量。
1 | var person = function(){ |
闭包的问题
闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
对prototype的理解 以及prototype的好处
使用 prototype的好处是不会额外产生内存,所有实例化后的对象都会从原型上继承这个方法。也就是需要一个子类拥有父类的某些特性(同种特性可以覆盖),又可以添加自己的特性,而不会影响父类时候使用prototype。
Prototype通常用来解决一个问题:对象的创建比较耗费资源。比如,对象创建的时候需要访问数据库、需要读取外部文件
call和prototype克隆的关系。
- B函数如何操作,才可以继承A的属性和方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23function A(name){
this.name = name;
}
A.prototype.sayName = function(){
alert(this.name);
}
function B(){}
function B(name){
A.call(this,name);//此时,B函数只是继承了A函数的属性name,并未继承A的sayName方法。
// 或者
// A.apply(this,name)
}
B.prototype = new A("Jack");
Call只会继承对象本身的属性和方法,而不会继承原型中的方法。prototype本身的作用是克隆,而不是继承
事件冒泡和事件捕获的差别
在JS中,绑定的事件默认的执行时间是在冒泡阶段执行,而非在捕获阶段(重要),这也是为什么当父类和子类都绑定了某个事件,会先调用子类绑定的事件,后调用父类的事件。直接看下面实例
1 |
|
总结
在JS中,绑定的事件默认的执行时间是在冒泡阶段执行,而非在捕获阶段,必须要理解
不过我们可以通过绑定事件时,指定事件执行时间是在冒泡阶段还是捕获阶段。
obj.addEventListener(event,function(){},bool)
bool:false,代表冒泡阶段执行
bool:true,代表捕获阶段执行
JS在默认情况下获取事件后,就开始从根元素开始捕获所有该事件的监听对象,然后在冒泡阶段逐一执行。捕获阶段是在冒泡阶段前面
阻止冒泡
w3c的方法是e.stopPropagation(),IE则是使用e.cancelBubble = true;
阻止默认行为
w3c的方法是e.preventDefault(),IE则是使用e.returnValue = false;
- 遵守事件冒泡规则,从内到外触发;
自己的对生命周期的理解
答:总共分为8个阶段创建前/后,载入前/后,更新前/后,销毁前/后。
创建前/后: 在beforeCreated阶段,vue实例的挂载元素$el和数据对象data都为undefined,还未初始化。在created阶段,vue实例的数据对象data有了,$el还没有。
载入前/后:在beforeMount阶段,vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染。
更新前/后:当data变化时,会触发beforeUpdate和updated方法。
销毁前/后:在执行destroy方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在
computed 和 watch 的 区别
计算属性computed :
- 支持缓存,只有依赖数据发生改变,才会重新进行计算
- 不支持异步,当computed内有异步操作时无效,无法监听数据的变化
- computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data中声明过或者父组件传递的props中的数据通过计算得到的值
- 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用computed
- 如果computed属性属性值是函数,那么默认会走get方法;函数的返回值就是属性的属性值;在computed中的,属性都有一个get和一个set方法,当数据变化时,调用set方法。
侦听属性watch:
- 不支持缓存,数据变,直接会触发相应的操作;
- watch支持异步;
- 监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
- 当一个属性发生变化时,需要执行对应的操作;一对多;
- 监听数据必须是data中声明过或者父组件传递过来的props中的数据,当数据变化时,触发其他操作,函数有两个参数,
immediate:组件加载立即触发回调函数执行,
deep: 深度监听,为了发现对象内部值的变化,复杂类型的数据时使用,例如数组中的对象内容的改变,注意监听数组的变动不需要这么做。注意:deep无法监听到数组的变动和对象的新增,参考vue数组变异,只有以响应式的方式触发才会被监听到。
babel原理
ES6、7代码输入 -> babylon进行解析 -> 得到AST(抽象语法树)-> plugin用babel-traverse对AST树进行遍历转译 ->得到新的AST树->用babel-generator通过AST树生成ES5代码、
Vue 相关面试
为什么data属性是一个函数而不是一个对象?
对象 会造成 两个组件里的对象两者共用了同一个内存地址
根实例对象data可以是对象也可以是函数(根实例是单例),不会产生数据污染情况
组件实例对象data必须为函数,目的是为了防止多个组件实例对象之间共用一个data,产生数据污染。采用函数的形式,initData时会将其作为工厂函数都会返回全新data对象
Vue 在哪个生命周期有虚拟Dom?
在组件第一次执行render 函数后,就会有虚拟Dom
会调用由模板编译而来的 render 方法,最后生成了 虚拟 DOM
beforeMount() 虚拟dom已创建完成,在数据渲染前最后一次更改数据
mounted() 页面、数据渲染完成,真实dom挂载完成
1 | vue._init |
从上面函数可以看出,虚拟Dom 在 beforeMount 后Watcher 里 执行updateComponent 里执行 render 函数生成的
vue的设计模式
vue的设计模式是mvvm
在Model和View之间多了叫做View-Model的一层
1 | <!--view页面--> |
上面作为理想情况下例子,在ViewModel引入之后,视图完全由接口返回数据驱动,由开发者所控制的逻辑非常轻量。不过这里要说明的是,在MVVM模式下,Controller控制逻辑并非就没了,像操作页面DOM响应的逻辑被SDK(如Vue的内部封装实现)统一实现了,像不操作接口返回的数据是因为服务端在数据返回给前端前已经操作好了。
例子里pageViewModel函数的实现是非常关键的一步,如何将数据模型与页面视图绑定起来呢?在目前的前端领域里有三类实现,Angularjs的主动轮询检查新旧值变化更新视图、Vue利用ES5的Object.defineProperty的getter/setter方法绑定、backbone的发布订阅模式,从主动和被动的方式去实现了ViewModel的关系绑定,接下来主要看看Vue中的MVVM的实现。
Vue2.0中的MVVM实现
Vue2.0的MVVM实现中,对View-Model的实现本质利用的ES5的Object.defineProperty方法,当Object.defineProperty方法在给数据Model对象定义属性的时候先挂载一些方法,在这些方法里实现与界面的值绑定响应关系,当应用的属性被读取或者写入的时候便会触发这些方法,从而达到数据模型里的值发生变化时同步响应到页面上。
Vue和React对比
相同点
都有组件化思想
都支持服务器端渲染
都有Virtual DOM(虚拟dom)
数据驱动视图
都有支持native的方案:Vue的weex、React的React native
都有自己的构建工具:Vue的vue-cli、React的Create React App
区别
数据流向的不同。react从诞生开始就推崇单向数据流,而Vue是双向数据流
数据变化的实现原理不同。react使用的是不可变数据,而Vue使用的是可变的数据
组件化通信的不同。react中我们通过使用回调函数来进行通信的,而Vue中子组件向父组件传递消息有两种方式:事件和回调函数
diff算法不同。
react主要使用diff队列保存需要更新哪些DOM,得到patch树,再统一操作批量更新DOM。
Vue 使用双向指针,边对比,边更新DOM
React
采用了MVC模式
将模型和视图之间实现代码分离,并且减少了代码的重复性
React中的性能优化
定义ShouldComponent()中的条件判断避免重复渲染,因为这个函数本身默认返回true。
定义非数组下标的key值,因数组下标容易导致key值重复,且会在数组变化时更改。key值将在新旧dom中节点比较时运用。
能使用时,尽量使用无状态组件。因为无状态组件不需要声明类,不需要显示声明this关键字。
当你在浏览器地址栏输入一个URL后回车,将会发生的事情?
基本流程:
①查询ip地址
②建立tcp连接,接入服务器
③浏览器发起http请求
④服务器后台操作并做出http响应
⑤网页的解析与渲染
查询ip地址
①浏览器解析出url中的域名。
②查询浏览器的DNS缓存。
③浏览器中没有DNS缓存,则查找本地客户端hosts文件有无对应的ip地址。
④hosts中无,则查找本地DNS服务器(运营商提供的DNS服务器)有无对应的DNS缓存。
⑤若本地DNS没有DNS缓存,则向根服务器查询,进行递归查找。
⑥递归查找从顶级域名开始(如.com),一步步缩小范围,最终客户端取得ip地址。
tcp连接与http连接
①http协议建立在tcp协议之上,http请求前,需先进行tcp连接,形成客户端到服务器的稳定的通道。俗称TCP的三次握手。
②tcp连接完成后,http请求开始,请求有多种方式,常见的有get,post等。
③http请求包含请求头,也可能包含请求体两部分,请求头中包含我们希望对请求文件的操作的信息,请求体中包含传递给后台的参数。
④服务器收到http请求后,后台开始工作,如负载平衡,跨域等,这里就是后端的工作了。
⑤文件处理完毕,生成响应数据包,响应也包含两部分,响应头和相应体,响应体就是我们所请求的文件。
⑥经过网络传输,文件被下载到本地客户端,客户端开始加载。
html渲染
①客户端浏览器加载了html文件后,由上到下解析html为DOM树(DOM Tree)。
②遇到css文件,css中的url发起http请求。
③这是第二次http请求,由于http1.1协议增加了Connection: keep-alive声明,故tcp连接不会关闭,可以复用。
④http连接是无状态连接,客户端与服务器端需要重新发起请求–响应。
在请求css的过程中,解析器继续解析html,然后到了script标签。
⑤由于script可能会改变DOM结构,故解析器停止生成DOM树,解析器被js阻塞,等待js文件发起http请求,然后加载。这是第三次http请求。js执行完成后解析器继续解析。
⑥由于css文件可能会影响js文件的执行结果,因此需等css文件加载完成后再执行。
⑦浏览器收到css文件后,开始解析css文件为CSSOM树(CSS Rule Tree)。
⑧CSSOM树生成后,DOM Tree与CSS Rule Tree结合生成渲染树(Render Tree)。
⑨Render Tree会被css文件阻塞,渲染树生成后,先布局,绘制渲染树中节点的属性(位置,宽度,大小等),然后渲染,页面就会呈现信息。
⑩继续边解析边渲染,遇到了另一个js文件,js文件执行后改变了DOM树,渲染树从被改变的dom开始再次渲染。
⑪继续向下渲染,碰到一个img标签,浏览器发起http请求,不会等待img加载完成,继续向下渲染,之后再重新渲染此部分。
⑫DOM树遇到html结束标签,停止解析,进而渲染结束。
URL到界面显示发生了什么
DNS解析
先本地缓存找,在一层层找
将常见的地址解析成唯一对应的ip地址基本顺序为:本地域名服务器->根域名服务器->com顶级域名服务器依次类推下去,找到后记录并缓存下来如www.google.com为. -> .com -> google.com. -> www.google.com.
TCP连接
三次握手,只要没收到确认消息就要重新发
主机向服务器发送一个建立连接的请求(您好,我想认识您);
服务器接到请求后发送同意连接的信号(好的,很高兴认识您);
主机接到同意连接的信号后,再次向服务器发送了确认信号(我也很高兴认识您),自此,主机与服务器两者建立了连接。
发送HTTP请求
浏览器会分析这个url,并设置好请求报文发出。请求报文中包括请求行、请求头、空行、请求主体。
https默认请求端口 443
, http默认 80
。
常见的http请求如下
闭包中变量访问问题(笔试题)
1 | var name="world"; |
解释:
只要在预解析时发现了闭包中有name的存在,就不会去外部找name,所以第三行的name是undefined。而如果将下边的所有内容包括var name注释掉,则此处的name就只能去闭包外边找,找到了全局变量name,值为world。
在预解析时,发现了在闭包这个位置处(代码第5行)用var定义了一个局部变量name,但预解析时并没有赋值
而对闭包中name值的判断在它var定义之前,所以是undefined,所以if条件成立,执行if的内容
let
有自己的作用域,所以name
不会变量提升,所以会去最外层找,所以name!==''undefined
为什么要使用typescript?
1.我们在项目重构的时候发现,幸亏用了typescript,哈哈,举个小栗子,比如一个字段的类型,我们在项目初期把它定义成了一个number类型,然后突然后端同学告诉我们不行,全部都要换成string,但是不可能我们要用黑科技String()吧,作为一名严谨的程序员,我需要改动他的类型,这时候typescript强大的强类型在编译器检验的时候,便报了一堆错,而我们此时只要根据报错改掉相应的参数即可。这是一点好处。
2.项目来了个新人,我不用不停的跟他讲解这个方法是用来干嘛的,这个参数是什么意思,还能使用这个对象的哪些属性哪些方法。因为我们在项目初期就已经定义好了interface 或者 说是 class 。每个实体的作用一目了然。我们在编辑器coding的时候,提示也会主动跟出来。这也是一点好处。
3.如果看过antd源码的同学肯定知道,antd也使用了typescript,并不是说大牛们用了,我们也要跟风一起用。我想说的是如果你也想做一个类似antd的自己的sdk,那么使用typescript对你来说真的很合适,因为你可以定义一套规范的接口。自己看着爽,使用者用着也很爽。
综上所述,我为什么要使用typescript呢?1.模块管理更佳 2.类型检查更严格3.它使我的开发严谨而自由。
什么是BFC(Block formatting context)?
- BFC(Block formatting context)直译为”块级格式化上下文”。它是一个独立的渲染区域,只有Block-level box参与, 它规定了内部的Block-level Box如何布局,并且与这个区域外部毫不相干。
BFC的布局规则
内部的Box会在垂直方向,一个接一个地放置。
Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠。
每个盒子(块盒与行盒)的margin box的左边,与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。
BFC的区域不会与float box重叠。
BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
如何创建BFC
1、float的值不是none。
2、position的值不是static或者relative。
3、display的值是inline-block、table-cell、flex、table-caption或者inline-flex
4、overflow的值不是visible
- BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
- 因为BFC内部的元素和外部的元素绝对不会互相影响,因此, 当BFC外部存在浮动时,它不应该影响BFC内部Box的布局,BFC会通过变窄,而不与浮动有重叠。同样的,当BFC内部有浮动时,为了不影响外部元素的布局,BFC计算高度时会包括浮动的高度。避免margin重叠也是这样的一个道理。
## 函数防抖和节流的概念和真实项目中的应用场景
函数防抖(debounce):
在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时;典型的案例就是输入搜索:输入结束后n秒才进行搜索请求,n秒内又输入的内容,就重新计时。
立即执行版本
1 | function debounce (callback, wait) { |
举个例子–页面滚动
基于上述场景,首先提出第一种思路:在第一次触发事件时,不立即执行函数,而是给出一个期限值比如200ms,然后:
• 如果在200ms内没有再次触发滚动事件,那么就执行函数
• 如果在200ms内再次触发滚动事件,那么当前的计时取消,重新开始计时
效果:如果短时间内大量触发同一事件,只会执行一次函数。
实现:既然前面都提到了计时,那实现的关键就在于setTimeOut这个函数,由于还需要一个变量来保存计时,考虑维护全局纯净,可以借助闭包来实现:
延时版
1 | /* |
- 对于短时间内连续触发的事件(上面的滚动事件),防抖的含义就是让某个时间期限(如上面的1000毫秒)内,事件处理函数只执行一次。
实际的应用
用户在输入搜索的时候不是每次输入都执行keyup的事件 而是用户输入1s后再执行请求的事件
1s之内用户再次输入的时候会从新计算时间
函数节流(throttle):
规定在一个单位时间内,只能触发一次函数,如果这个单位时间内触发多次函数,只有一次生效; 典型的案例就是鼠标不断点击触发,规定在n秒内多次点击只有一次生效。
立即执行版本
1 | function throttle (callback, wait) { |
延时版
1 |
|
实际的应用
滚动加载更多 在监听滚动条的位置的时候不是用户每次滚动都要去监听滚动条的位置的 而是例如2秒内不管你滚动多少次 我就监听一次滚动条的位置
其他应用场景举例
讲完了这两个技巧,下面介绍一下平时开发中常遇到的场景:
- 搜索框input事件,例如要支持输入实时搜索可以使用节流方案(间隔一段时间就必须查询相关内容),或者实现输入间隔大于某个值(如500ms),就当做用户输入完成,然后开始搜索,具体使用哪种方案要看业务需求。
- 页面resize事件,常见于需要做页面适配的时候。需要根据最终呈现的页面情况进行dom渲染(这种情形一般是使用防抖,因为只需要判断最后一次的变化情况)
vue 相关面试题
vue-lazyloader 怎么实现的(高频)
1、vue-lazyload是通过指令的方式实现的,定义的指令是v-lazy指令
2、指令被bind时会创建一个listener,并将其添加到listener queue里面, 并且搜索target dom节点,为其注册dom事件(如scroll事件)
3、上面的dom事件回调中,会遍历 listener queue里的listener,判断此listener绑定的dom是否处于页面中perload的位置,如果处于则加载异步加载当前图片的资源
4、同时listener会在当前图片加载的过程的loading,loaded,error三种状态触发当前dom渲染的函数,分别渲染三种状态下dom的内容
vue 的响应式原理 (高频)
Vue2 的响应式原理是核心是通过 ES5 的保护对象的 Object.defindeProperty 中的访问器属性中的 get 和 set 方法,data 中声明的属性都被添加了访问器属性,当读取 data 中的数据时自动调用 get 方法,当修改 data 中的数据时,自动调用 set 方法,检测到数据的变化,会通知观察者 Wacher,观察者 Wacher自动触发重新render 当前组件(子组件不会重新渲染),生成新的虚拟 DOM 树
Vue3 Porxy
能说下 vue-router 中常用的 hash 和 history 路由模式实现原理吗?
(1)hash 模式的实现原理
早期的前端路由的实现就是基于 location.hash 来实现的。其实现原理很简单,location.hash 的值就是 URL 中 # 后面的内容。比如下面这个网站,它的 location.hash 的值为 ‘#search’:
https://www.word.com#search
hash 路由模式的实现主要是基于下面几个特性:
URL 中 hash 值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash 部分不会被发送;
hash 值的改变,都会在浏览器的访问历史中增加一个记录。因此我们能通过浏览器的回退、前进按钮控制hash 的切换;
可以通过 a 标签,并设置 href 属性,当用户点击这个标签后,URL 的 hash 值会发生改变;或者使用 JavaScript 来对 loaction.hash 进行赋值,改变 URL 的 hash 值;
我们可以使用 hashchange 事件来监听 hash 值的变化,从而对页面进行跳转(渲染)。
Vue\React 都是根据这个 hashchange 来做的
讲 tcp/ip 网络层、三次握手,为什么不能两次握手
采用三次握手可以减少服务端的资源浪费
如果使用两次握手,服务端回应后不确认客户端的状态,连接建立成功。服务端会长时间等待客户端发送数据,连接长期保持,会造成资源浪费。当多个客户端产生这种情况,服务器就会等待多个客户端的响应,连接数量过多,之后的客户端请求,服务器无法响应。造成服务器处于瘫痪状态。 只有使用三次握手,当服务端收到确认报文后,保证当前时刻,客户端可以发送数据时,才能建立有意义的连接
JavaScript 相关
说说new操作符具体干了什么?
可以自己在控制台打印下,new 出来的 构造函数跟原函数有什么差别
创建一个新的对象obj
将对象与构建函数通过原型链连接起来
将构建函数中的this绑定到新建的对象obj上
根据构建函数返回类型作判断,如果是原始值则被忽略,如果是返回对象,需要正常处理
举个例子:
1 | function Person(name, age){ |
实现new
1 | function mynew(Func, ...args) { |
为什么 typeof null 是 object?
typeof null 输出为 ‘object’ 其实是一个底层的错误,但直到现阶段都无法被修复。
原因是,在 JavaScript 初始版本中,值以 32位 存储。前 3位 表示数据类型的标记,其余位则是值。
实现组合继承
1 | function Super(){ |
数组去重
CDN的作用和原理
为什么有CDN
当下的互联网应用都包含大量的静态内容,但静态内容以及一些准动态内容又是最耗费带宽的,特别是针对全国甚至全世界的大型网站,如果这些请求都指向主站的服务器的话,不仅是主站服务器受不了,单端口500M左右的带宽也扛不住,所以大多数网站都需要CDN服务。
根本上的原因是,访问速度对互联网应用的用户体验、口碑、甚至说直接的营收都有巨大的影响,任何的企业都渴望自己站点有更快的访问速度。
- 为了加速网站的访问。
- 为了节约成本
CDN原理
DNS 服务器根据用户 IP 地址,将域名解析成相应节点的缓存服务器IP地址,实现用户就近访问。使用 CDN 服务的网站,只需将其域名解析权交给 CDN 的全局负载均衡(GSLB)设备,将需要分发的内容注入 CDN,就可以实现内容加速了。
适用场景
static 内容加速,静态内容加速,如:html image js css 等
视频直播加速
视频直播加速,流媒体切片、转码、码流转换等等。
webpack 系列
Web性能优化
- 使用 source-map 特性查看运行时的源代码
- 使用 WebpackDevServer 实现浏览器自动刷新
- 缩小文件范围,优化构建速度
- CSS 支持:支持 less、sass,并支持自动补齐浏览器前缀 postcss
- 优化文件监听范围
- DllPlugin 、HardSourceWebpackPlugin 优化构建性能
- 【重】使用 happypack 并发执行打包任务
为了优化输出质量,webpack 给我们提供了下面的方案:
抽离、压缩CSS
压缩 HTML
【重】Tree Shaking
代码分割 Code Splitting
通过 report
查看 文件打包后的大小
如何提高webpack的构建速度?
随着我们的项目涉及到页面越来越多,功能和业务代码也会随着越多,相应的 webpack 的构建时间也会越来越久
构建时间与我们日常开发效率密切相关,当我们本地开发启动 devServer 或者 build 的时候,如果时间过长,会大大降低我们的工作效率
所以,优化webpack 构建速度是十分重要的环节
如何优化
- 优化 loader 配置
- 合理使用 resolve.extensions
- 优化 resolve.modules
- 优化 resolve.alias
- 使用 DLLPlugin 插件
- 使用 cache-loader
- terser 启动多线程
- 合理使用 sourceMap
- 打包又耗时且页面加载也较慢得很。可以通过cdn直接引入,方便且速度快。
打包生成 sourceMap 的时候,如果信息越详细,打包速度就会越慢。
可以看到,优化webpack构建的方式有很多,主要可以从优化搜索时间、缩小文件搜索范围、减少不必要的编译等方面入手
优化 loader
如采用 ES6 的项目为例,在配置 babel-loader时,可以这样:
1 | module.exports = { |
webpack 如何提高打包速度?
Dllplugin
利用 ‘cache-loader’ 提高打包速度
html-webpack-externals-plugin
分析打包速度
React、Vue、SolidJS、Svelte
SolidJS借鉴了很多Svelte的预编译优化和轻量运行时,已经不需要虚拟DOM,依赖收集借鉴了Vue,jsx 借鉴了 React
Svelte的预编译优化和轻量运行时
React 把每一个组件当成了一个状态机,组件内部通过state来维护组件状态的变化,当组件的状态发生变化时
请问 solidjs、React 要怎么调用子组件的方法,
vue 可以通过 expose 导出使用,那solidjs呢
React
1 | import React , { Component } from "react" |
SEO 优化
TDD 驱动测试开发(Test-Driven Development,TDD)
什么是前端工程化流程,模块化开发,组件化开发
- 前端工程化是一个高层次的思想,而模块化和组件化是为工程化思想下相对较具体的开发方式,因此可以简单的认为模块化和组件化是工程化-的表现形式。
- 模块化和组件化一个最直接的好处就是复用,除了复用之外还有就是分治,我们能够在不影响其他代码的情况下,按需修改某一独立的模块或是组件,因此很多地方我们即使没有复用需要,也可以根据分治需求进行模块化或组件化开发。
什么是前端工程
将前端项目当成一项系统工程进行分析、组织和构建从而达到项目结构清晰、分工明确、团队配合默契、开发效率提高的目的。
模块化开发的好处
避免变量污染,命名冲突
提高代码复用率
提高维护性
依赖关系的管理
在模块化编程中,开发者将程序分解成离散功能块(discrete chunks of functionality),并称之为模块。 每个模块具有比完整程序更小的接触面,使得校验、调试、测试轻而易举。 精心编写的模块提供了可靠的抽象和封装界限,使得应用程序中每个模块都具有条理清楚的设计和明确的目的。
模块应该是职责单一、相互独立、低耦合的、高度内聚且可替换的离散功能块。
什么是组件化
降低整个系统的耦合度,在保持接口不变的情况下,我们可以替换不同的组件快速完成需求,例如输入框,可以替换为日历、时间、范围等组件作具体的实现
调试方便,由于整个系统是通过组件组合起来的,在出现问题的时候,可以用排除法直接移除组件,或者根据报错的组件快速定位问题,之所以能够快速定位,是因为每个组件之间低耦合,职责单一,所以逻辑会比分析整个系统要简单
提高可维护性,由于每个组件的职责单一,并且组件在系统中是被复用的,所以对代码进行优化可获得系统的整体升级
浏览器事件机制
1 | Promise.resolve().then(function () { |
因为第一 promise 由于第一个promise的.then()的返回依然是promise,所以第二个.then()会放到microtask队列继续执行,输出 ‘promise2’;
为什么会需要event-loop?
因为 JavaScript 是单线程的。单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。为了协调事件(event),用户交互(user interaction),脚本(script),渲染(rendering),网络(networking)等,用户代理(user agent)必须使用事件循环(event loops)。
WebSocket 握手的原理是什么?
其实很简单,因为只有一次握手:
服务端验证客户端的握手包符合规范之后,也会发送一个握手包给客户端
自我介绍
你好,我叫陈新鑫,拥有5年多前端经验,呆过两家公司,我在上家公司主要负责 TV 相关多业务,带着3个人的小组,平常我主要负责压测相关
的题目,在这家公司拿过优秀员工奖。然后我自己平常比较喜欢关注前端生态的内容,比较喜欢尝试新东西。会自己写blog,有时间也会混迹在 github 看看代码,提提issue。
TDD pnpm
你有什么想问我的
这个问题确实不怎么好答,相信很多人都被这个问题困扰过
回答没什么想问的呢,可能会给面试官一个你并不想进公司的感觉
瞎问呢又怕惹得面试官不高兴了
其实这个问题问得好的话反而是一个能很好了解对方公司的一个渠道。
以下是一些笔者认为不错的提问,能够很好地了解到对方公司的一些东西,包括开发流程、职业晋升、公司发展等等。大家可以选择性地提出 2 - 3 个感兴趣的问题,这样不仅能帮助到自身了解到公司的一些情况,也能给予面试官一个不错的印象,以下问题针对于技术面:
公司常用的技术栈是什么?
你们如何测试代码?
你们如何解决线上故障?
你们如何准备故障恢复?是否有完善的发布机制?
公司是否有技术分享交流活动?有的话,多久一次呢?
一次迭代的流程是怎么样的?从 PRD 评审开始到发布这一整个流程。
公司技术团队的架构和人员组成?
有公司级别的学习资源吗?比如电子书订阅或者在线课程?
你们认为和竞品相比有什么优势?
参考链接
一名【合格】前端工程师的自检清单
web前端面试 - 面试官系列
特殊面试题
问题
- 分析一下目录.editorconfig 是干嘛的 …
- .gitattributes是干嘛的
- 持续集成是如何实现的
- 分析一下单元测试环境是如何搭建的…
- 分析一下 package.json里面的字段都是干嘛的
- 写一个库的 README 需要哪几个部分?
- 构建是如何做的?
- 分析一下 tsconfig 里面的配置项画一下这个库的程序流程图
- 尝试通过单元测试调试库这个库应该如何使用?
- 如何理解 option…
- 如何理解command.
- 如何理解action…
- 如何实现连续调用的api
- Brackets 应该如何使用
- Brackets 是如何实现的
- Negated Options 是如何实现的?
- 分析一下下面这段代码的执行流程、还可以从功能上分解需求点..
- 程序等于数据结构+算法…
- 阅读源码的流程..
- 本文作者: Littleki
- 本文链接: https:/littleki.gitee.io/2020/04/09/面试/面试题/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!