2020面试题总结

407 阅读34分钟

HTTP的几种请求方法用途

  • 1)GET方法 发送一个请求来取得服务器上的某一资源
  • 2)POST方法 向URL指定的资源提交数据或附加新的数据
  • 3)PUT方法 跟POST方法很像,也是想服务器提交数据。但是,它们之间有不同。PUT指定了资源在服务器上的位置,而POST没有
  • 4)HEAD方法 只请求页面的首部
  • 5)DELETE方法 删除服务器上的某资源
  • 6)OPTIONS方法 它用于获取当前URL所支持的方法。如果请求成功,会有一个Allow的头包含类似“GET,POST”这样的信息
  • 7)TRACE方法 TRACE方法被用于激发一个远程的,应用层的请求消息回路
  • 8)CONNECT方法 把请求连接转换到透明的TCP/IP通道

POST和GET的区别

  • 在浏览器进行回退操作时,get请求是无害的,而post请求则会重新请求一次

  • get请求参数是连接在url后面的,而post请求参数是存放在requestbody内的

  • get请求因为浏览器对url长度有限制(不同浏览器长度限制不一样)对传参数量有限制,而post请求因为参数存放在requestbody内所以参数数量没有限制(事实上get请求也能在requestbody内携带参数,只不过不符合规定,有的浏览器能够获取到数据,而有的不能),大小get不超过2KB,post无限制

  • 因为get请求参数暴露在url上,所以安全方面post比get更加安全

  • get请求浏览器会主动cache,post并不会,除非主动设置

  • get请求参数会保存在浏览器历史记录内,post请求并不会

  • get请求只能进行url编码,而post请求可以支持多种编码方式

  • get请求产生1个tcp数据包,post请求产生2个tcp数据包

  • 浏览器在发送get请求时会将header和data一起发送给服务器,服务器返回200状态码,而在发送post请求时,会先将header发送给服务器,服务器返回100,之后再将data发送给服务器,服务器返回200 OK

从浏览器地址栏输入URL到显示页面的步骤

blog.csdn.net/qq_32657025…

1、域名解析 当我们在浏览器中输入一个URL,例如”www.google.com”时,这个地址并不是谷歌网站真正意义上的地址。互联网上每一台计算机的唯一标识是它的IP地址,因此我们输入的网址首先需要先解析为IP地址,这一过程叫做DNS解析。

DNS解析是一个递归查询的过程。例如,我们需要解析”www.google.com”时,会经历以下步骤:

在本地域名服务器中查询IP地址,未找到域名; 本地域名服务器回向根域名服务器发送请求,未找到域名; 本地域名服务器向.com顶级域名服务器发送请求,未找到域名; 本地域名服务器向.google.com域名服务器发送请求,找到该域名,将对应的IP返回给本地域名服务器。

2、TCP连接

HTTP协议是使用TCP协议作为其传输层协议的,在拿到服务器的IP地址后,浏览器客户端会与服务器建立TCP连接。该过程包括三次握手:

第一次握手:建立连接时,客户端向服务端发送请求报文 第二次握手:服务器收到请求报文后,如同意连接,则向客户端发送确认报文 第三次握手,客户端收到服务器的确认后,再次向服务器给出确认报文,完成连接。 三次握手主要是为了防止已经失效的请求报文字段发送给服务器,浪费资源。

3、浏览器发送HTTP请求

浏览器构建http请求报文,并通过TCP协议传送到服务器的指定端口。http请求报文一共包括三个部分:

请求行:指定http请求的方法、url、http协议版本等 请求头:描述浏览器的相关信息,语言、编码等。如下

请求正文:当发送POST, PUT等请求时,通常需要向服务器传递数据。这些数据就储存在请求正文中。

4、服务器处理HTTP请求

服务器处理http请求,并返回响应报文。响应报文包括三个部分:

状态码:http服务常用的状态码及其含义如下 状态码 含义 常见示例

  • 1** 服务器已经接受到请求,客户端可继续发送请求

  • 2** 请求成功 200:请求已成功,请求所希望的响应头或数据体将随此响应返回。

  • 3** 重定向 303:对应当前请求的响应可以在另一个 URI 上被找到,而且客户端应当采用 GET 的方式访问那个资源。304 (未修改) 自从上次请求后,请求的网页未修改过。 服务器返回此响应时,不会返回网页内容。

  • 4** 客户端错误 400(错误请求) 服务器不理解请求的语法。403(禁止) 服务器拒绝请求。 404:请求的网页不存在 405(方法禁用) 禁用请求中指定的方法。

  • 5** 服务器错误 503: 服务器超时

  • 响应头:包含了响应的相关信息,如日期等

  • 响应正文:服务器返回给浏览器的文本信息,通常的html、js、css、图片等就包含在这一部分里面。

5、浏览器页面渲染

浏览器接受到http服务器发送过来的响应报文,并开始解析html文档,渲染页面。具体的渲染过程包括:构建DOM树、构建渲染树、定位页面元素、绘制页面元素等。具体可参看:浏览器时如何渲染页面的

6、断开TCP连接

客户端与服务器四次挥手,断开tcp连接。

第一次挥手:客户端想分手,发送消息给服务器 第二次挥手:服务器通知客户端已经接受到分手请求,但还没做好分手准备 第三次回收:服务器已经做好分手准备,通知客户端 第四次挥手:客户端发送消息给服务器,确定分手,服务器关闭连接

HTML5有哪些新特性、移除了哪些元素?

blog.csdn.net/lxcao/artic…

  • 绘画 canvas;

  • 用于媒介回放的 video和 audio元素;

  • 语意化更好的内容元素,比如article、footer、header、nav、section;

  • 本地离线存储 localStorage

localStorage.getItem()

localStorage.setItem()

localStorage.removeItem()

  • sessionStorage的数据在浏览器关闭后自动删除;操作参考localStorage
  • 事件监听
if(window.addEventListener){
 window.addEventListener("storage",handle_storage,false);//
}else if(window.attachEvent){ //兼容IE
 window.attachEvent("onstorage",handle_storage);
}
 function handle_storage(e){}

移除:

  <basefont> 默认字体,不设置字体,以此渲染
  <font> 字体标签
  <center> 水平居中
  <u> 下划线
  <big> 大字体
  <strike> 中横线
  <tt> 文本等宽
  <frameset>
  <noframes>
  <frame>

请描述一下cookies,sessionStorage和localStorage的区别?

cookie 是网站为了标识用户信息而存储在本地(client side)上的数据,一般会加密

cookie数据始终在同源请求中传递,即在浏览器和服务器之间来回传递

seesionstorage 和 localstorage 不会把数据发到服务器,仅在本地保存

存储大小,cookie的大小不能超过4k sessionstorage 和 localstorage 的大小一般在5M以上,比cookie大的多

有效时间不同

localstorage 存储持久数据,浏览器关闭后数据不丢失除非主动删除数据

sessionstorage 数据在当前浏览器窗口关闭或者会话结束后自动删除

cookie 设置的cookie过期之前一直有效,即使窗口或者浏览器关闭

存储位置 cookie未设置expires时候,存在内存中,设置时间后存在硬盘中;sessionstorage存放在内存里;localstorage存放在硬盘。

节流函数和防抖函数

函数防抖(debounce)

函数防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。简单的说,当一个动作连续触发,则只执行最后一次。

函数防抖的应用场景

 连续的事件,只需触发一次回调的场景有:

  • 搜索框搜索输入。只需用户最后一次输入完,再发送请求

  • 手机号、邮箱验证输入检测

  • 窗口大小Resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。

let debounce =  function(fn, delay) {
    let _timer = null;//利用闭包,一直存在
    return function () {
        let content = this;
        let args = [].slice.call(arguments);//把event对象传过去
        if(_timer){clearTimeout(_timer);}
        _timer = setTimeout(function () {
            fn.apply(content,args);
        }, delay);

    };
};
函数防抖在执行目标方法时,会等待一段时间。当又执行相同方法时,若前一个定时任务未执行完,则 clear 掉定时任务,重新定时。

 函数节流(throttle)

限制一个函数在一定时间内只能执行一次。

函数节流应用场景

间隔一段时间执行一次回调的场景有:

  • 滚动加载,加载更多或滚到底部监听

  • 谷歌搜索框,搜索联想功能

  • 高频点击提交,表单重复提交

// 定时器方案
function throttle(fn,wait){
    var timer = null;
    return function(){
        var context = this;
        var args = arguments;
        if(!timer){
            timer = setTimeout(function(){
                fn.apply(context,args);
                timer = null;
            },wait)
        }
    }
}
    
function handle(){
    console.log(Math.random());
}
    
window.addEventListener("mousemove",throttle(handle,1000));

// 时间戳方案
function throttle(fn,wait){
    var pre = Date.now();
    return function(){
        var context = this;
        var args = arguments;
        var now = Date.now();
        if( now - pre >= wait){
            fn.apply(context,args);
            pre = Date.now();
        }
    }
}

function handle(){
    console.log(Math.random());
}
    
window.addEventListener("mousemove",throttle(handle,1000));

相同点:

  • 都可以通过使用 setTimeout 实现。

  • 目的都是,降低回调执行频率。节省计算资源。

不同点:

  • 函数防抖,在一段连续操作结束后,处理回调,利用 clearTimeout 和 setTimeout 实现。

  • 函数节流,在一段连续操作中,每一段时间只执行一次,频率较高的事件中使用来提高性能。

  • 函数防抖关注一定时间连续触发,只在最后执行一次,而函数节流侧重于一段时间内只执行一次。

简述vue和react的区别

React 和Vue是现在主流的两个框架(相对来说angular用的已经少了)

两者的区别体现在以下方面

相同点:

1、react和vue都支持服务端渲染

2、都有虚拟DOM,组件化开发,通过props传参进行父子组件数据的传递

3、都是数据驱动视图

4、都有支持native的方案(react的react native,vue的weex)

5、都有状态管理(react有redux,vue有vuex)

不同点:

1、react严格上只能算是MVC的view层,vue则是MVVM模式

2、虚拟DOM不一样,vue会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树

而对于react而言,每当应用的状态被改变时,全部组件都会重新渲染,所以react中会需要shouldComponentUpdate这个生命周期函数方法来进行控制

3、组件写法不一样,react推荐的做法是JSX+inline style,也就是把HTML和CSS全都写进javaScript了

4、数据绑定:vue实现了数据的双向绑定,react数据流动是单向的

5、state对象在react应用中是不可变的,需要使用setState方法更新状态

在vue中,state对象不是必须的,数据有data属性在vue对象中管理

谈谈正则

1、元字符

2、重复限定符

node.js的特点以及优缺点

特点

Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台。

Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好

优点

动态语言:开发效率非常高,并有能力构建复杂系统,如ql.io。

性能和I/O负载:Nodejs非常好的解决了IO密集的问题,通过异步IO来实现。libuv是一个多平台支持库,主要关注异步I/O。

连接的内存开销:每个Node.js进程可以支持超过12万活跃的连接,每个连接消耗大约2K的内存。

操作性:实现了Nodejs对于内存堆栈的监控系统。

缺点

nodejs更新很快,可能会出现版本兼容

nodejs还不算成熟,还没有大制作

nodejs不像其他的服务器,对于不同的链接,不支持进程和线程操作

node如何实现前后端分离以及好处

www.cnblogs.com/cloudml/p/4…

试想一下,如果前端掌握了Controller,我们可以做url design,我们可以根据场景决定在服务端同步渲染,还是根据view层数据输出json数据,我们还可以根据表现层需求很容易的做bigpipe,comet,socket等等,完全是需求决定使用方式。

现阶段我们主要以后端MVC的模式进行开发,这种模式严重阻碍了前端开发效率,也让后端不能专注于业务开发。 解决方案是让前端能控制Controller层,但是如果在现有技术体系下很难做到,因为不可能让所有前端都学java,安装后端的开发环境,写VM。 NodeJS就能很好的解决这个问题,我们无需学习一门新的语言,就能做到以前开发帮我们做的事情,一切都显得那么自然。

分层就涉及每层之间的通讯,肯定会有一定的性能损耗。但是合理的分层能让职责清晰、也方便协作,会大大提高开发效率。分层带来的损失,一定能在其他方面的收益弥补回来。 另外,一旦决定分层,我们可以通过优化通讯方式、通讯协议,尽可能把损耗降到最低。

PC端和手机端的区别

blog.csdn.net/fairyier/ar…

第一: PC考虑的是浏览器的兼容性,而移动端开发考虑的更多的是手机兼容性,因为目前不管是android手机还是ios手机,一般浏览器使用的都是webkit内核,所以说做移动端开发,更多考虑的应该是手机分辨率的适配,和不同操作系统的略微差异化。

第二: 在部分事件的处理上,移动端多出来的事件是触屏事件,而缺少的是hover事件。 另外包括移动端弹出的手机键盘的处理,这样的问题在PC端都是遇不到的。

第三: 在布局上,移动端开发一般是要做到布局自适应的,我使用的一直是rem布局,感觉很好。

@function px2rem($px, $base: 75) {
    @return ($px / $base) * 1rem;
}
/*
稿子上量得某按钮宽60px,高20px
*/
.btn{
    width:px2rem(60);
    height:px2rem(20);
}

第四: 在动画处理上,PC端由于要考虑IE的兼容性,所以通常使用JS做动画的通用性会更好一些,但是CSS3做了很大的牺牲, 而在手机端,如果要做一些动画、特效等,第一选择肯定是CSS3, 既简单、效率又高。

CSS有哪些属性是可以继承的?

   一、无继承性的属性

1、display:规定元素应该生成的框的类型

2、文本属性:

vertical-align:垂直文本对齐

text-decoration:规定添加到文本的装饰

text-shadow:文本阴影效果

white-space:空白符的处理

unicode-bidi:设置文本的方向

3、盒子模型的属性:width、height、margin 、margin-top、margin-right、margin-bottom、margin-left、border、border-style、border-top-style、border-right-style、border-bottom-style、border-left-style、border-width、border-top-width、border-right-right、border-bottom-width、border-left-width、border-color、border-top-color、border-right-color、border-bottom-color、border-left-color、border-top、border-right、border-bottom、border-left、padding、padding-top、padding-right、padding-bottom、padding-left

4、背景属性:background、background-color、background-image、background-repeat、background-position、background-attachment

5、定位属性:float、clear、position、top、right、bottom、left、min-width、min-height、max-width、max-height、overflow、clip、z-index

6、生成内容属性:content、counter-reset、counter-increment

7、轮廓样式属性:outline-style、outline-width、outline-color、outline

8、页面样式属性:size、page-break-before、page-break-after

9、声音样式属性:pause-before、pause-after、pause、cue-before、cue-after、cue、play-during 二、有继承性的属性

1、字体系列属性

font:组合字体

font-family:规定元素的字体系列

font-weight:设置字体的粗细

font-size:设置字体的尺寸

font-style:定义字体的风格

font-variant:设置小型大写字母的字体显示文本,这意味着所有的小写字母均会被转换为大写,但是所有使用小型大写字体的字母与其余文本相比,其字体尺寸更小。

font-stretch:对当前的 font-family 进行伸缩变形。所有主流浏览器都不支持。

font-size-adjust:为某个元素规定一个 aspect 值,这样就可以保持首选字体的 x-height。

2、文本系列属性

text-indent:文本缩进

text-align:文本水平对齐

line-height:行高

word-spacing:增加或减少单词间的空白(即字间隔)

letter-spacing:增加或减少字符间的空白(字符间距)

text-transform:控制文本大小写

direction:规定文本的书写方向

color:文本颜色

3、元素可见性:visibility

4、表格布局属性:caption-side、border-collapse、border-spacing、empty-cells、table-layout

5、列表布局属性:list-style-type、list-style-image、list-style-position、list-style

6、生成内容属性:quotes

7、光标属性:cursor

8、页面样式属性:page、page-break-inside、windows、orphans

9、声音样式属性:speak、speak-punctuation、speak-numeral、speak-header、speech-rate、volume、voice-family、pitch、pitch-range、stress、richness、、azimuth、elevation

三、所有元素可以继承的属性

1、元素可见性:visibility

2、光标属性:cursor

四、内联元素可以继承的属性

1、字体系列属性

2、除text-indent、text-align之外的文本系列属性

五、块级元素可以继承的属性

1、text-indent、text-align

css4新增特性

1.否定伪类:not

否定伪类选择器其实在CSS3选择器中就出现了,只不过CSS4选择器对否定伪类选择器升级了。在CSS3中,你可以通过否定伪类选择器不去选中你不需要用到的CSS样式的元素。比如说,你想除了.intro的段落之外文本都不加粗,你就可以这样使用伪类选择器。

p:not(.intro) { font-weight: normal; }

在CSS4选择器中,可以传入一个逗号(,)分隔的列表选择器:

p:not(.intro, blockquote) { font-weight: normal; }

2.匹配任何伪类:matches

这个伪类意味着,可以将规则应用到一个组合选择器中,如:

p:matches(.alert, .error, .warn) { color: red; }

带有只要元素带

带有.alert、.error和.warn中任何一个类名,文本颜色都将会是red。

你可以在http://css4-selectors.com网站上测试你的浏览器是否支持这些CSS4选择器。这也是一个资源网站,你可以在这个站上找到将要添加的选择器。

3.关系伪类:has

这个选择器通过一个参数从一个列表选择器中匹配到相对应的元素。有一个最易说明的示例,在这个例子中任何一个包含的链接都会加上一个黑色的边框:

a:has( > img ) { border: 1px solid #000; }

在下面这个示例中,使用:has和:not结合在一起,选择不包含段落

的元素:

li:not(:has(p)){ padding-bottom: 1em; }

4.表单限制伪类 :required:optional

匹配表单项中必选项与可选项。

input:required { color:#f30; }

5.可读可写伪类 :read-write、:read-only

大部分元素都是可读不可写的,所以都是 :read-only;像 text input 这些值可以改变的,和 HTML5 中设置了 contenteditable 的元素其本身是可改变的,这些被认为是具有写属性的,所以是 :read-write

:read-write { font-family: Georgia; }

  1. :any-link

目前,我们可以使用:link和:visited表明链接的访问状态。这个更进一步,它会检查href的是否在用户的浏览历史中存在,以确定是否一个给定的链接是否已被访问。

:link, :visited {

color: blue;

}

css4 ,可以直接这样应用,上述代码等价于:

:any-link {

color: blue;

}

7.:scoped

css4选择器,CSS被赋予一个全局范围。换句话说,如果你添加下面的CSS:

div {

color: #444;

}

所有的div将应用的 color:#444 的样式规则(这里假设没有被复写的情况下)。css4选择器 允许将样式和风格限定于一个元素标签内。

在这个例子中,我们已将应用范围到限定在aside元素中。在这种标记风格将只适用于父元素下的子元素。

8.css网格布局

网格布局为我们创建了类似于表格布局的结构,然而,我们可以使用CSS而非HTML中所描述的选择器来创建布局,通过媒体查询来重定义布局以适应不同的上下文内容。这样我们就可以正确区分视觉和源码的元素顺序。作为一个设计师,你可以自由地改变页面元素的位置来适应不同断点下的布局,而不必为你响应式设计去调整结构。与HTML中基于表格的布局不同,网格布局允许你堆叠元素。因此,在需要的情况下,一个元素可以重叠另一元素。

原型链 以及属性

简单的回顾一下构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么假如我们让原型对象等于另一个类型的实例,结果会怎样?显然,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立。如此层层递进,就构成了实例与原型的链条。这就是所谓的原型链的基本概念。——摘自《javascript高级程序设计》

图中由相互关联的原型组成的链状结构就是原型链,也就是蓝色的这条线。

vue首屏性能优化,解决页面加载时间过长(白屏)方法

优化策略:

1、路由懒加载

  • vue异步组件
  • es提案的import()
  • webpack的require,ensure()

vue异步组件技术 ==== 异步加载

vue-router配置路由 , 使用vue的异步组件技术 , 可以实现按需加载 . 但是,这种情况下一个组件生成一个js文件

/* vue异步组件技术 */
{
  path: '/home',
  name: 'home',
  component: resolve => require(['@/components/home'],resolve)
},{
  path: '/index',
  name: 'Index',
  component: resolve => require(['@/components/index'],resolve)
},{
  path: '/about',
  name: 'about',
  component: resolve => require(['@/components/about'],resolve)
} 

/* 注意resolve和require两边的空格 */

组件懒加载方案二 路由懒加载(使用import)

// 下面3行代码,没有指定webpackChunkName,每个组件打包成一个js文件。
/* const Home = () => import('@/components/home')
const Index = () => import('@/components/index')
const About = () => import('@/components/about') */
// 下面3行代码,指定了相同的webpackChunkName,会合并打包成一个js文件。 把组件按组分块
const Home =  () => import(/* webpackChunkName: 'ImportFuncDemo' */ '@/components/home')
const Index = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '@/components/index')
const About = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '@/components/about')
{
  path: '/about',
  component: About
}, {
  path: '/index',
  component: Index
}, {
  path: '/home',
  component: Home
}

webpack提供的require.ensure()

vue-router配置路由,使用webpack的require.ensure技术,也可以实现按需加载。 这种情况下,多个路由指定相同的chunkName,会合并打包成一个js文件。

/* 组件懒加载方案三: webpack提供的require.ensure() */
{
  path: '/home',
  name: 'home',
  component: r => require.ensure([], () => r(require('@/components/home')), 'demo')
}, {
  path: '/index',
  name: 'Index',
  component: r => require.ensure([], () => r(require('@/components/index')), 'demo')
}, {
  path: '/about',
  name: 'about',
  component: r => require.ensure([], () => r(require('@/components/about')), 'demo-01')
}

如果是在 vuecli 3中,我们还需要多做一步工作 因为 vuecli 3默认开启 prefetch(预先加载模块),提前获取用户未来可能会访问的内容 在首屏会把这十几个路由文件,都一口气下载了 所以我们要关闭这个功能,在 vue.config.js中设置:

2、ui框架按需加载 这里以饿了么ui为例: 原本的引进方式引进了整个包:

import ElementUI from 'element-ui'
Vue.use(ElementUI)

但实际上我用到的组件只有按钮,分页,表格,输入与警告 所以我们要按需引用:

import { Button, Input, Pagination, Table, TableColumn, MessageBox } from 'element-ui';
Vue.use(Button)
Vue.use(Input)
Vue.use(Pagination)
Vue.prototype.$alert = MessageBox.alert

3、gzip压缩

安装 compression-webpack-plugin

cnpm i compression-webpack-plugin -D

const CompressionPlugin = require('compression-webpack-plugin')

在 vue.congig.js中引入并修改 webpack配置:

configureWebpack: (config) => {
if (process.env.NODE_ENV === 'production') {
    // 为生产环境修改配置...
    config.mode = 'production'
    return {
        plugins: [new CompressionPlugin({
            test: /\.js$|\.html$|\.css/, //匹配文件名
            threshold: 10240, //对超过10k的数据进行压缩
            deleteOriginalAssets: false //是否删除原文件
        })]
    }
}

在服务器我们也要做相应的配置 如果发送请求的浏览器支持 gzip,就发送给它 gzip格式的文件 我的服务器是用 express框架搭建的 只要安装一下 compression就能使用

// 要放在所有其他中间件注册之前
const compression = require('compression')
app.use(compression())

闭包

闭包是指有权访问另一个函数作用域中的变量的函数;

闭包的特性

看一下这一段的封装,jQuery也采用了类似的封装方式,外部的方法和变量就不会污染闭包内部的东西,同时,闭包内的变量也会有效的保存下来

function person(name) {
    // 变量作用域为函数内部,外部无法访问,防止了变量名冲突和污染
    var name = '小明';
    this.sayName= function() {
        alert(name)
    }
    this.changeName= function(newName) {
        name = newName
    }
}
// 外部无法访问内部变量
let a = new person()
console.log(a.name) // undefiend
a.changeName('小白')
// 这里修改的name会保存下来
a.sayName() // 小白

由于闭包会常驻内存,使用不当会导致内存溢出。解决方法:手动解除引用,name = null。

关于垃圾回收机制可以看下这两篇博客 blog.csdn.net/zxd10001/ar…

www.cnblogs.com/leftJS/p/11…

Web前端性能优化——如何有效提升静态文件的加载速度

一、如何优化

用户在访问网页时, 最直观的感受就是页面内容出来的速度,我们要做的优化工作, 也主要是为了这个目标。那么为了提高页面加载(或者渲染)速度呢?一般来说有三个方面:

1、代码逻辑:优秀的代码逻辑结构可以有效减少渲染页面使用的内存和速度(比如虚拟DOM),此方面不在本文讨论范围内。

2、SSR服务器渲染,也就是所谓的“直出”。将首屏所有内容在服务器端渲染成html静态代码后,直接输出给浏览器,可以有效加快用户访问站点时首屏的加载时间。不过此方面也不在本文讨论范围内。

3、提升静态文件的加载速度,这是本文会讨论的点,而这方面大致又可分为下面几点:

— 加快静态文件下载速度

— 减少静态文件的文件大小

— 减少静态文件请求数量,从而减少发起请求的次数(对于移动端页面来说,请求的开销比网速的开销要大)

(一)代码压缩

最常规的优化手段之一。

我们在平时开发的时候,JS脚本文件和CSS样式文件中的代码,都会依据一定的代码规范(比如javascript-standard-style)来提高项目的可维护性,以及团队之间合作的效率。

但是在项目发布现网后, 这些代码是给客户端(浏览器)识别的,此时代码的命名规范、空格缩进都已没有必要,我们可以使用工具将这些代码进行混淆和压缩,减少静态文件的大小

这里我们选择使用Webpack,具体会在后面介绍。

(二)文件合并

1、合并js脚本文件

2、合并css样式文件

3、合并css引用的图片,使用sprite雪碧图。

这里我们继续选择使用Webpack,具体会在后面介绍。

(三)gzip

我们的文件在压缩合并之后,文件大小和文件数量都有了客观的减少。但是一旦站点业务逻辑多了,或者引入的第三方库多了之后,对于移动端来说,文件大小还是不太乐观。

这个时候就是gzip压缩登场的时候啦~我们在webpack的配置中增加gzip压缩配置:

上面代码会对文件大小大于10240,并且压缩率好于0.8的js、css文件进行gzip压缩,执行打包代码后生成结果文件如下:

我们可以看到除了原有的js和css文件外,我们还得到了压缩后的gz文件。

把所有这些文件一起部署到服务器上。(当然也可以直接nginx或其他web server配置gzip压缩)

(四)CDN和缓存

为什么使用CDN?

CDN 是一个全球(或者只有国内,具体看供应商)分布式网络,它把网站内容更快地传递给服务范围内的一个具体位置,而往往这个具体的位置离实际的内容服务器距离很远。举个极端点的例子,你的网站主机在爱尔兰(海南),而你的用户则在澳大利亚(漠河)访问。这时当你的用户访问你的网站的时候,延迟会很大,把你的(静态)数据用 CDN 放到澳大利亚(漠河)则会很大程度上提高用户访问网站的体验。

如果没有CDN服务,我们可以添加Expires头,减少DNS查找(DNS一般指域名系统(服务)协议。域名系统(服务)协议(DNS)是一种分布式网络目录服务,主要用于域名与 IP 地址的相互转换,以及控制因特网的电子邮件的发送。),配置ETag,使AjaX可缓存。

还可参考blog.csdn.net/weixin_3085…

(五)安全方面: CSP

web前端对于xss安全漏洞一定不陌生。我们知道Javascript语句甚至是css表达式都可能导致xss攻击,现在很多前端会使用CSP策略来进行脚本源的限制防御。

而我们由于使用的cdn域名和业务域名不一样:

cdn域名:cdn.xxx.qq.com 业务域名:xxx.qq.com

我们可以:

  1. 在index.html静态入口文件的meta http-equiv头中做配置;

服务器端直接返回相应的HTTP response header头信息;

例如:

这里除了指定了cdn的域名源,告诉浏览器从这个域名加载的js文件都是可信的。同时因为我们使用的webpack打包压缩代码后的一些特性,我们还需要加上'unsafe-inline'标识。

使用CSP策略我们可以指定浏览器安全解析script、css、fonts、media等资源的源与方式。

二、webpack2.0

使用webpack2最重要的地方就是使用它tree-shaking的特性。这个特性对于ES6的module管理有着非常优美的优化,大概能减少30%左右的包体积。

ES module和CommonJS的require模块管理不同,前者是基于静态的,而后者是动态的。
ES module:
只允许静态同步 import
在模块执行之前,导入和导出已经关联
导入和导出是不可变的

CJS:
允许动态同步 require()
导出仅在模块执行后才知道
导出可以在模块初始化后添加,替换和删除

现在我们来看一下如何使用webpack:

代码压缩

我们自己写的代码因为在开发时需要遵循一定的代码规范,所以会有很多多余的换行和空格字符,甚至是便于阅读的长变量名,这些其实对于机器(浏览器)来说,都不是必要的。所以我们可以把这些都干掉。比如我们写的代码可能是这样的:

接着我们就使用Webpack来进行压缩。首先,需要在工程根目录的package.json(相信使用过npm包管理的前端同学一定不陌生)文件中添加webpack的依赖配置:

各个工程应该按需引入需要的loader和webpack-plugin库。有一点需要注意的是:webpack本身是没有对各个类型的文件进行分析处理的能力的,这个时候我们需要使用各种第三方库的loader,比如css-loader等(当然我们也可以自己编写loader)。同时webpack也有强大的第三方Plugin插件供我们对文件进行进一步处理。

接下来我们就可以在scripts中指向的脚本文件里编写webpack对应的构建代码了。

例如在webpackConfig配置中的plugins属性数组中,我们可以添加以下配置:

而最终生成的文件结构如下:

我们可以看到所有样式代码被压缩后抽离到了一个app.[hash].css文件中,所有js逻辑代码按照业务逻辑和第三方库被抽离到了app.[hash].js和vendor.[hash].js文件中。

被打包文件的内容也已经被webpack压缩混淆,减少了加载文件的Content Size。

关于其他的webpack用法配置,可以查询官方文档和中文文档,这里就不一一详细说明了

目前webpack3 和webpack4使用了新的方式打包代码,可以进一步提升js在浏览器中的执行效率。

三、题外话

跨域方面: CORS

我们知道由于现代浏览器安全策略的不断完善,对跨域请求的限制也是各种各样。

当我们保存在静态资源文件中的script对其他域名发起请求时就会遇到跨域问题,如果没有做任何措施,请求会被浏览器拦截。

当前主流的跨域解决方案主要是JSONP和CORS

由表可见,随着前端不断发展,CORS跨域是大趋势。 CORS需要被请求端根据请求者的host,与白名单比对后返回正确的HTTP response header头信息 。

关于CORS: www.cnblogs.com/ay742936292…

作者原链接https://www.cnblogs.com/wetest/p/7738862.html

ES6中相关(async和await的区别以及它们返回的是什么东西)

www.cnblogs.com/DFX339/p/10…

vue中的登录拦截

www.jb51.net/article/157…

兼容IE9 www.jb51.net/article/138…

vue父子组件的生命周期

1. 加载渲染过程

同步引入时生命周期顺序为: 父组件的beforeCreate、created、beforeMount --> 所有子组件的beforeCreate、created、beforeMount --> 所有子组件的mounted --> 父组件的mounted 总结:父组件先创建,然后子组件创建;子组件先挂载,然后父组件挂载。

若有孙组件呢? 父组件先beforeCreate => created => beforeMount , 然后子组件开始beforeCreate => created => beforeMount ,然后孙组件beforeCreate => created => beforeMount => mounted,孙组件挂载完成了,子组件mounted,父组件再mounted

异步引入时生命周期顺序为: 父组件的beforeCreate、created、beforeMount、mounted --> 子组件的beforeCreate、created、beforeMount、mounted

总结:父组件创建,父组件挂载;子组件创建,子组件挂载。

  1. 子组件更新过程 父beforeUpdate->子beforeUpdate->子updated->父updated

3.父组件更新过程 父beforeUpdate->父updated

4.销毁过程 父beforeDestroy->子beforeDestroy->子destroyed->父destroyed

Vue核心思想:数据驱动、组件化

一,数据驱动

传统的前端数据交互是用Ajax从服务端获取数据,然后操作DOM来改变视图;或者前端交互要改变数据时,又要再来一次上述步骤,而手动操作DOM是一个繁琐的过程且易出错。 Vue.js 是一个提供了 MVVM 风格的双向数据绑定的 Javascript 库,专注于View 层。它让开发者省去了操作DOM的过程,只需要改变数据。Vue会通过Dircetives指令,对DOM做一层封装,当数据发生改变会通知指令去修改对应的DOM,数据驱动DOM变化,DOM是数据的一种自然映射。 Vue还会对操作进行监听,当视图发生改变时,vue监听到这些变化,从而改变数据,这样就形成了数据的双向绑定。 Vue是一种MVVM框架。而DOM是数据的一个种自然映射。传统的模式是通过Ajax请求从model请求数据,然后手动的触发DOM传入数据修改页面。Vue中,Directives对view进行了封装,当model里的数据发生变化是,Vue就会通过Directives指令去修改DOM。同时也通过DOM Listener实现对视图view的监听,当DOM改变时,就会被监听到,实现model的改变,实现数据的双向绑定。

二,组件响应原理

数据(model)改变驱动视图(view)自动更新

当你把一个普通的 JavaScript 对象传给 Vue 实例的 data选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器的原因。 用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。这里需要注意的问题是浏览器控制台在打印数据对象时 getter/setter 的格式化并不同,所以你可能需要安装 vue-devtools 来获取更加友好的检查接口。 每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知watcher重新计算,从而致使它关联的组件得以更新。

三,组件化

扩展HTML元素,封装可重用的代码。每一个组件都对应一个ViewModel。页面上每个独立的可视/可交互区域都可以视为一个组件。每个组件对应一个工程目录,组件所需要的各种资源在这个目录下就进维护。页面是组件的容器,组件可以嵌套自由组合形成完整的页面。

组件化实现了扩展HTML元素,封装可用的代码。页面上每个独立的可视/可交互区域视为一个组件;每个组件对应一个工程目录,组件所需要的各种资源在这个目录下就近维护;页面不过是组件的容器,组件可以嵌套自由组合形成完整的页面。

原文链接:blog.csdn.net/weixin_4512…

将数组扁平化并去除其中重复的数据,最终得到了一个升序且不重复的数据

var arr=[[1,2,3],[3,4,5,6],[6,7,8,9,[11,12,13,[14]]],10]
利用ES6语法
Array.fromy(从一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。)、new Set()数组去重、arr.flat() 数组扁平化   sort()排序
const newArr = Array.from(new Set(arr.flat(Infinity))).sort((a,b)=>{return a-b})

数组去重的方法

segmentfault.com/a/119000001…

[...new Set(arr)] ES6语法

call、apply、bind区别

bind语法:

func.bind(thisArg[, arg1[, arg2[, ...]]])
thisArg 当绑定函数被调用时,该参数会作为原函数运行时的this指向。当使用new 操作符调用绑定函数时,该参数无效。
arg1, arg2, ... 当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。
call语法:

fun.call(thisArg, arg1, arg2, ...)
thisArg::在fun函数运行时指定的this值。需要注意的是,指定的this值并不一定是该函数执行时真正的this值,如果这个函数处于非严格模式下,则指定为null和undefined的this值会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。
arg1, arg2, ... 指定的参数列表。
apply语法:

fun.apply(thisArg, [argsArray])
thisArg: 在 fun 函数运行时指定的 this 值。需要注意的是,指定的 this 值并不一定是该函数执行时真正的 this 值,如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的自动包装对象。
argsArray: 一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 fun 函数。如果该参数的值为null 或 undefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。