面试题总结

1.说说你对盒子模型的理解


当对一个文档进行布局(layout)的时候,浏览器的渲染引擎会根据标准之一的 CSS 基础框盒模型(CSS basic box model),将所有元素表示为一个个矩形的盒子(box)

一个盒子由四个部分组成:content、padding、border、margin

content,即实际内容,显示文本和图像

boreder,即边框,围绕元素内容的内边距的一条或多条线,由粗细、样式、颜色三部分组成

padding,即内边距,清除内容周围的区域,内边距是透明的,取值不能为负,受盒子的background属性影响

margin,即外边距,在元素外创建额外的空白,空白通常指不能放其他元素的区域

在CSS中,盒子模型可以分成:

W3C 标准盒子模型

IE 怪异盒子模型

默认情况下,盒子模型为W3C 标准盒子模型

标准盒子模型

标准盒子模型,是浏览器默认的盒子模型

也就是,width/height 只是内容高度,不包含 padding 和 border值

IE 怪异盒子模型

也就是,width/height 包含了 padding和 border值

可以通过box-sizing: border-box;来设置为IE怪异盒子模型


2.css选择器有哪些?优先级?

id选择器(#box),选择id为box的元素

类选择器(.one),选择类名为one的所有元素

标签选择器(div),选择标签为div的所有元素

后代选择器(#box div),选择id为box元素内部所有的div元素

子选择器(.one>one_1),选择父元素为.one的所有.one_1的元素

相邻同胞选择器(.one+.two),选择紧接在.one之后的所有.two元素

群组选择器(div,p),选择div、p的所有元素

优先级

!important >内联 > ID选择器 > 类选择器 > 标签选择器

继承属性

在css中,继承是指的是给父元素设置一些属性,后代元素会自动拥有这些属性.

font-size, font-family, color


3.元素水平垂直居中的方法有哪些?如果元素不定宽高呢?

实现元素水平垂直居中的方式:

利用定位+margin:auto (开启定位后,脱离文档流)

利用定位+margin:负值 (例如盒子100*100,定位top,left偏移后,偏右下方向,需要回-50)

利用定位+transform (定位top,left偏移后,偏右下方向,通过transform: translate(-50%,-50%))

table布局

flex布局

grid布局



4.怎么理解回流跟重绘?什么场景下会触发?

回流:布局引擎会根据各种样式计算每个盒子在页面上的大小与位置

重绘:当计算好盒模型的位置、大小及其他属性后,浏览器根据每个盒子特性进行绘制

具体的浏览器解析渲染机制如下所示:

解析HTML,生成DOM树,解析CSS,生成CSSOM树

将DOM树和CSSOM树结合,生成渲染树(Render Tree)

Layout(回流):根据生成的渲染树,进行回流(Layout),得到节点的几何信息(位置,大小)

Painting(重绘):根据渲染树以及回流得到的几何信息,得到节点的绝对像素

Display:将像素发送给GPU,展示在页面上


当我们对 DOM 的修改引发了 DOM几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性,然后再将计算的结果绘制出来

当我们对 DOM的修改导致了样式的变化(color或background-color),却并未影响其几何属性时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式,这里就仅仅触发了重绘

回流这一阶段主要是计算节点的位置和几何信息,那么当页面布局和几何信息发生变化的时候,就需要回流

触发回流一定会触发重绘

除此之外还有一些其他引起重绘行为:

颜色的修改

文本方向的修改

阴影的修改

下面给出避免回流的经验:

如果想设定元素的样式,通过改变元素的 class 类名 (尽可能在 DOM 树的最里层)

避免设置多项内联样式

应用元素的动画,使用 position 属性的 fixed 值或 absolute 值(如前文示例所提)

避免使用 table 布局,table 中每个元素的大小以及内容的改动,都会导致整个 table 的重新计算

对于那些复杂的动画,对其设置 position: fixed/absolute,尽可能地使元素脱离文档流,从而减少对其他元素的影响

使用css3硬件加速,可以让transform、opacity、filters这些动画不会引起回流重绘

避免使用 CSS 的 JavaScript 表达式

5.什么是响应式设计?响应式设计的基本原理是什么?如何做?

响应式网站设计(Responsive Web design)是一种网络页面设计布局,页面的设计与开发应当根据用户行为以及设备环境(系统平台、屏幕尺寸、屏幕定向等)进行相应的响应和调整

响应式网站常见特点:

同时适配PC + 平板 + 手机等

标签导航在接近手持终端设备时改变为经典的抽屉式导航

网站的布局会根据视口来调整模块的大小和位置

实现方式:

媒体查询:

@mediascreen(min-width:375px)and(max-width:600px){

        body{font-size:18px;}

}

百分比:

通过百分比单位 " % " 来实现响应式的效果

比如当浏览器的宽度或者高度发生变化时,通过百分比单位,可以使得浏览器中的组件的宽和高随着浏览器的变化而变化,从而实现响应式的效果

vw/vh:

vw表示相对于视图窗口的宽度,vh表示相对于视图窗口高度。 任意层级元素,在使用vw单位的情况下,1vw都等于视图宽度的百分之一

rem:

在以前也讲到,rem是相对于根元素html的font-size属性,默认情况下浏览器字体大小为16px,此时1rem = 16px

响应式布局优点可以看到:

面对不同分辨率设备灵活性强

能够快捷解决多设备显示适应问题

缺点:

仅适用布局、信息、框架并不复杂的部门类型网站

兼容各种设备工作量大,效率低下

代码累赘,会出现隐藏无用的元素,加载时间加长

其实这是一种折中性质的设计解决方案,多方面因素影响而达不到最佳效果

一定程度上改变了网站原有的布局结构,会出现用户混淆的情况

6.如果要做优化,CSS提高性能的方法有哪些?

内联首屏关键CSS:

在打开一个页面,页面首要内容出现在屏幕的时间影响着用户的体验,而通过内联css关键代码能够使浏览器在下载完html后就能立刻渲染

异步加载CSS

资源压缩

合理使用选择器:

不要嵌套使用过多复杂选择器,最好不要三层以上

使用id选择器就没必要再进行嵌套

通配符和属性选择器效率最低,避免使用

建立公共样式类,把相同样式提取出来作为公共类使用,比如我们常用的清除浮动等

减少使用昂贵的属性:

在页面发生重绘的时候,昂贵属性如box-shadow/border-radius/filter/透明度/:nth-child等,会降低浏览器的渲染性能

不要使用@import

不用css表达式,表达式只是让你的代码显得更加炫酷,但是他对性能的浪费可能是超乎你的想象的


7.对前端工程师这个职位是怎么样理解的?它的前景会怎么样?

我认为前端工程师是最贴近用户的,是以用户需求为中心,通过不同的改进和亲身体验来完成用户的需求,让用户的交互效果更加舒服,

以前前端的可能就写一些页面,很简单的工作,现在可以通过vue,react等框架来让交互效果更加理想,也把后端的一些任务放到前端处理,所以,前端不仅仅要面对用户,还要面对,后端,产品经理,等所以前端并不局限于前端技术还要会一点后端,会一点产品, 每个前端人员往往都会往全栈的方向去发展,当然这也是我的一个目标,这就我对前端开发人员的理解+

8.说说JavaScript中的数据类型?存储上的差别?

js的数据类型分为两类,一个是基本数据类型,一个是引用数据类型

基本数据类型有undefined、null、boolean、number、string、symbol

引用数据类型有 object

在js的执行过程中,主要有三种数据类型内存空间,分别是代码空间,栈空间,堆空间,其中的代码空间主要是存储可执行代码的,原始类型的数据值都是直接保存在栈中的,引用数据类型的值是存放在堆空间中的, 原始数据类型存储的是变量的值,而引用数据类型存储的是其在堆空间中的地址。

9.typeof 与 instanceof 区别

typeof 操作符返回一个字符串,表示未经计算的操作数的类型

typeof 对于原始数据类型来说,除了null都可以正确的显示类型

对于引用数据类型来说,除了function函数,都会显示object

这样看来typeof它并不能正确的显示数据类型

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上,能判断这个对象是否是之前那个构造函数生成的对象

typeof与instanceof都是判断数据类型的方法,区别如下:

typeof会返回一个变量的基本类型,instanceof返回的是一个布尔值

instanceof 可以准确地判断复杂引用数据类型,但是不能正确判断基础数据类型

而typeof 也存在弊端,它虽然可以判断基础数据类型(null 除外),但是引用数据类型中,除了function 类型以外,其他的也无法判断

10.说说你对闭包的理解?闭包使用场景

闭包就是可以访问其他函数内部变量的函数,我们通常用它来定义私有化的变量和方法,创建一个闭包最简单的方法就是在一个函数内创建一个函数,它有三个特性是 函数内可以再嵌套函数,内部函数可以访问外部的方法和变量,方法和变量不会被垃圾回收机制回收

任何闭包的使用场景都离不开这两点:

创建私有变量

延长变量的生命周期

11.bind、call、apply 区别?如何实现一个bind?

从上面可以看到,apply、call、bind三者的区别在于:

三者都可以改变函数的this对象指向

三者第一个参数都是this要指向的对象,如果如果没有这个参数或参数为undefined或null,则默认指向全局window

三者都可以传参,但是apply是数组,而call是参数列表,且apply和call是一次性传入参数,而bind可以分为多次传入

bind是返回绑定this之后的函数,apply、call 则是立即执行

实现bind的步骤,我们可以分解成为三部分:

修改this指向

动态传递参数

兼容new关键字

12.说说你对事件循环的理解

首先,JavaScript是一门单线程的语言,意味着同一时间内只能做一件事,但是这并不意味着单线程就是阻塞,而实现单线程非阻塞的方法就是事件循环

在JavaScript中,所有的任务都可以分为

同步任务:立即执行的任务,同步任务一般会直接进入到主线程中执行

异步任务:异步执行的任务,比如ajax网络请求,setTimeout定时函数等

从上面我们可以看到,同步任务进入主线程,即主执行栈,异步任务进入任务队列,主线程内的任务执行完毕为空,会去任务队列读取对应的任务,推入主线程执行。上述过程的不断重复就事件循环

异步任务还可以细分为微任务与宏任务

微任务

当前(此次事件循环中)宏任务执行完,在下一个宏任务开始之前需要执行的任务,可以理解为回调事件。

常见的微任务有:

Promise.then

MutaionObserver

Object.observe(已废弃;Proxy 对象替代)

process.nextTick(Node.js)

宏任务

当前调用栈中执行的代码成为宏任务。

常见的宏任务有:

script (可以理解为外层同步代码)

setTimeout/setInterval

UI rendering/UI事件

postMessage、MessageChannel

setImmediate、I/O(Node.js)

13.DOM常见的操作有哪些

1.查找节点. 2.创建节点. 3.添加新节点. 4.删除节点. 5.更新节点.

添加节点: parentNode.appendChild(existingChild)

innerHtml

创建节点:document.createElement(“元素名”)

删除节点:parentNode.removeChild(existingChild)删除已有的子节点,返回值为删除节点

更新节点:parentNode.replaceChild(newChild,existingChild)用新节点替换父节点中已有的子节点

innerHtml不但可以修改一个DOM节点的文本内容,还可以直接通过HTML片段修改DOM节点内部的子树

ele.style.styleName = styleValue 修改样式

查找节点:document.getElementByid(“id属性值”) 返回拥有指定id的第一个对象的引用document.getElementsByClassName('class属性值');返回拥有指定class的对象集合document.getElementsByTagName('标签名');返回拥有指定标签名的对象集合document.getElementsByName('name属性值');返回拥有指定名称的对象结合document/element.querySelector('CSS选择器');仅返回第一个匹配的元素document/element.querySelectorAll('CSS选择器');返回所有匹配的元素

14.说说你对BOM的理解,常见的BOM对象你了解哪些?

一、是什么

BOM (Browser Object Model),浏览器对象模型,提供了独立于内容与浏览器窗口进行交互的对象

其作用就是跟浏览器做一些交互效果,比如如何进行页面的后退,前进,刷新,浏览器的窗口发生变化,滚动条的滚动,以及获取客户的一些信息如:浏览器品牌版本,屏幕分辨率

二、window

Bom的核心对象是window,它表示浏览器的一个实例

在浏览器中,window对象有双重角色,即是浏览器窗口的一个接口,又是全局对象

因此所有在全局作用域中声明的变量、函数都会变成window对象的属性和方法

三、location

除了 hash之外,只要修改location的一个属性,就会导致页面重新加载新URL

location.reload(),此方法可以重新刷新当前页面。这个方法会根据最有效的方式刷新页面,如果页面自上一次请求以来没有改变过,页面就会从浏览器缓存中重新加载

四、navigator

navigator 对象主要用来获取浏览器的属性,区分浏览器类型。属性较多,且兼容性比较复杂

五、screen

保存的纯粹是客户端能力信息,也就是浏览器窗口外面的客户端显示器的信息,比如像素宽度和像素高度

六、history

history对象主要用来操作浏览器URL的历史记录,可以通过参数向前,向后,或者向指定URL跳转

history.go() 接收一个整数数字或者字符串参数:向最近的一个记录中包含指定字符串的页面跳转

history.forward():向前跳转一个页面

history.back():向后跳转一个页面

history.length:获取历史记录数

15.Javascript本地存储的方式有哪些?区别及应用场景?

一、方式

javaScript本地缓存的方法我们主要讲述以下四种:

cookie

Cookie,类型为「小型文本文件」,指某些网站为了辨别用户身份而储存在用户本地终端上的数据。是为了解决 HTTP无状态导致的问题

作为一段一般不超过 4KB 的小型文本数据,它由一个名称(Name)、一个值(Value)和其它几个用于控制 cookie有效期、安全性、使用范围的可选属性组成

但是cookie在每次请求中都会被发送,如果不使用 HTTPS并对其加密,其保存的信息很容易被窃取,导致安全风险。

sessionStorage

sessionStorage和 localStorage使用方法基本一致,唯一不同的是生命周期,一旦页面(会话)关闭,sessionStorage 将会删除数据

localStorage

生命周期:持久化的本地存储,除非主动删除数据,否则数据是永远不会过期的

indexedDB

二、区别

关于cookie、sessionStorage、localStorage三者的区别主要如下:

存储大小:cookie数据大小不能超过4k,sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大

有效时间:localStorage存储持久数据,浏览器关闭后数据不丢失除非主动删除数据; sessionStorage数据在当前浏览器窗口关闭后自动删除;cookie设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭

数据与服务器之间的交互方式,cookie的数据会自动的传递到服务器,服务器端也可以写cookie到客户端; sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存

三、应用场景

在了解了上述的前端的缓存方式后,我们可以看看针对不对场景的使用选择:

标记用户与跟踪用户行为的情况,推荐使用cookie

适合长期保存在本地的数据(令牌),推荐使用localStorage

敏感账号一次性登录,推荐使用sessionStorage

存储大量数据的情况、在线文档(富文本编辑器)保存编辑历史的情况,推荐使用indexedDB

16.什么是防抖和节流?有什么区别?如何实现?

节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效

防抖: n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时

本质上是优化高频率执行代码的一种手段,需要对这类事件进行调用次数的限制

二、区别

相同点:

都可以通过使用 setTimeout 实现

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

不同点:

函数防抖,在一段连续操作结束后,处理回调,利用clearTimeout和 setTimeout实现。函数节流,在一段连续操作中,每一段时间只执行一次,频率较高的事件中使用来提高性能

函数防抖关注一定时间连续触发的事件,只在最后执行一次,而函数节流一段时间内只执行一次

17.如何通过JS判断一个数组

1.通过instanceof判断

let a = [];

a instanceofArray;//true

2.通过constructor判断

我们知道,实例的构造函数属性constructor指向构造函数,那么通过constructor属性也可以判断是否为一个数组。

let a = [1,3,4];

a.constructor === Array;//true

3.通过Object.prototype.toString.call()判断

 Object.prototype.toString().call()可以获取到对象的不同类型,例如

let a = [1,2,3]

Object.prototype.toString.call(a) === '[object Array]';//true

4.通过Array.isArray()判断

Array.isArray() 用于确定传递的值是否是一个数组,返回一个布尔值。

let a = [1,2,3]

Array.isArray(a);//true

18.说说你对作用域链的理解

一、作用域

作用域,即变量(变量作用域又称上下文)和函数生效(能被访问)的区域或集合

我们一般将作用域分成:

全局作用域

函数作用域

块级作用域

二、词法作用域

词法作用域,又叫静态作用域,变量被创建时就确定好了,而非执行阶段确定的。也就是说我们写好代码时它的作用域就确定了,JavaScript 遵循的就是词法作用域

三、作用域链

当在Javascript中使用一个变量的时候,首先Javascript引擎会尝试在当前作用域下去寻找该变量,如果没找到,再到它的上层作用域寻找,以此类推直到找到该变量或是已经到了全局作用域

19.JavaScript原型,原型链 ? 有什么特点?

一、原型

JavaScript 常被描述为一种基于原型的语言——每个对象拥有一个原型对象

当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾

准确地说,这些属性和方法定义在Object的构造器函数(constructor functions)之上的prototype属性上,而非实例对象本身

二、原型链

原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链 (prototype chain),它解释了为何一个对象会拥有定义在其他对象中的属性和方法

在对象实例和它的构造器之间建立一个链接(它是__proto__属性,是从构造函数的prototype属性派生的),之后通过上溯原型链,在构造器中找到这些属性和方法

下面作出总结:

一切对象都是继承自Object对象,Object 对象直接继承根源对象null

一切的函数对象(包括 Object 对象),都是继承自 Function 对象

Object 对象直接继承自 Function 对象

Function对象的__proto__会指向自己的原型对象,最终还是继承自Object对象

20. 请解释什么是事件代理

一、是什么

事件代理,俗地来讲,就是把一个元素响应事件(click、keydown......)的函数委托到另一个元素

前面讲到,事件流的都会经过三个阶段: 捕获阶段 -> 目标阶段 -> 冒泡阶段,而事件委托就是在冒泡阶段完成

事件委托,会把一个或者一组元素的事件委托到它的父层或者更外层元素上,真正绑定事件的是外层元素,而不是目标元素

当事件响应到目标元素上时,会通过事件冒泡机制从而触发它的外层元素的绑定事件上,然后在外层元素上去执行函数

从上面应用场景中,我们就可以看到使用事件委托存在两大优点:

减少整个页面所需的内存,提升整体性能

动态绑定,减少重复工作

21. 谈谈This对象的理解

一、定义

在绝大多数情况下,函数的调用方式决定了 this 的值(运行时绑定)

this 关键字是函数运行时自动生成的一个内部对象,只能在函数内部使用,总指向调用它的对象

二、绑定规则

根据不同的使用场合,this有不同的值,主要分为下面几种情况:

默认绑定:

严格模式下,不能将全局对象用于默认绑定,this会绑定到undefined,只有函数运行在非严格模式下,默认绑定才能绑定到全局对象

隐式绑定:

函数还可以作为某个对象的方法调用,这时this就指这个上级对象

new绑定:

通过构建函数new关键字生成一个实例对象,此时this指向这个实例对象

显示绑定:

apply()、call()、bind()是函数的一个方法,作用是改变函数的调用对象。它的第一个参数就表示改变后的调用这个函数的对象。因此,这时this指的就是这第一个参数

三、箭头函数

在 ES6 的语法中还提供了箭头函语法,让我们在代码书写时就能确定 this 的指向(编译时绑定)

箭头函数没有自己的this指向,它的this指向上一级作用域的this

不能改变this指向

四、优先级

综上,new绑定优先级 > 显示绑定优先级 > 隐式绑定优先级 > 默认绑定优先级

22. new操作符具体干了什么

一、是什么

在JavaScript中,new操作符用于创建一个给定构造函数的实例对象

从上面可以看到:

new 通过构造函数 Person 创建出来的实例可以访问到构造函数中的属性

new 通过构造函数 Person 创建出来的实例可以访问到构造函数原型链中的属性(即实例与构造函数通过原型链连接了起来)

二、流程

从上面介绍中,我们可以看到new关键字主要做了以下的工作:

创建一个新的对象obj

将对象与构建函数通过原型链连接起来

将构建函数中的this绑定到新建的对象obj上

根据构建函数返回类型作判断,如果是原始值则被忽略,如果是返回对象,需要正常处理

23.null,undefined 的区别

1 null:表示"没有对象",即该处不应该有值

作为函数的参数,表示该函数的参数不是对象。

作为对象原型链的终点。

2 undefined:表示"缺少值",就是此处应该有一个值,但是还没有定义

变量被声明了,但没有赋值时,就等于undefined。

调用函数时,应该提供的参数没有提供,该参数等于undefined。

对象没有赋值的属性,该属性的值为undefined。

函数没有返回值时,默认返回undefined。

24.javascript 代码中的"use strict";是什么意思

使用 "use strict" 指令

"use strict" 指令在 JavaScript 1.8.5 (ECMAScript5) 中新增。

它不是一条语句,但是是一个字面量表达式,在 JavaScript 旧版本中会被忽略。

"use strict" 的目的是指定代码在严格条件下执行。

严格模式下你不能使用未声明的变量。

25.同步和异步的区别

所谓单线程,就是指在JS引擎中负责解释和执行JavaScript代码的线程只有一个,一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务。如果一个任务耗时过长,那么后面的任务就必须一直等待下去,会拖延整个程序。我们不妨叫它主线程

在JavaScript中,所有的任务都可以分为

同步任务:立即执行的任务,同步任务一般会直接进入到主线程中执行,就是后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的。

异步任务:异步执行的任务,比如ajax网络请求,setTimeout定时函数等,每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。

26.谈一谈箭头函数与普通函数的区别

一.箭头函数都是匿名函数

普通函数可以有匿名函数,也可以有具体名函数,但是箭头函数都是匿名函数。

二.箭头函数不能用于构造函数,不能使用new

普通函数可以用于构造函数,以此创建对象实例。

三.箭头函数中this的指向不同

在普通函数中,this总是指向调用它的对象,如果用作构造函数,this指向创建的对象实例。

1.箭头函数本身不创建this

也可以说箭头函数本身没有this,但是它在声明时可以捕获其所在上下文的this供自己使用。

2.结合call(),apply()方法使用

箭头函数结合call(),apply()方法调用一个函数时,只传入一个参数对this没有影响。

27.JS 数组和对象的遍历方式,以及几种方式的比较

一、遍历对象方法:

1.for...in

遍历输出的是对象自身的属性以及原型链上可枚举的属性

2.Object.keys()

遍历对象返回的是一个包含对象自身可枚举属性的数组(不含Symbol属性).

3.Objcet.getOwnPropertyNames()

输出对象自身的可枚举和不可枚举属性的数组,不输出原型链上的属性

二.遍历数组方法

1.forEach

2.map

可以对遍历的每一项做相应的处理,返回每次函数调用的结果组成的数组

3.for循环遍历

4.for...in

5.for...of(es6)

只遍历出value,不能遍历出下标,可遍历出Symbol数据类型的属性,此方法作为遍历所有数据结构的统一的方法


28.如何解决跨域问题

1.什么是跨域?

跨域:指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。

同源策略:是指协议,域名,端口都要相同,其中有一个不同都会产生跨域;

1.CORS

跨域资源共享(CORS) 是一种机制,它使用额外的HTTP头来告诉浏览器 让运行在一个 origin (domain) 上的 Web 应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求

2.Node 正向代理

代理的思路为,利用服务端请求不会跨域的特性,让接口和当前站点同域。

3.Nginx 反向代理

4.JSONP

JSONP主要就是利用了script标签没有跨域限制的这个特性来完成的。

5.Websocket

WebSocket规范定义了一种 API,可在网络浏览器和服务器之间建立“套接字”连接

6.window.postMessage

window.postMessage()方法可以安全地实现跨源通信。通常,对于两个不同页面的脚本,只有当执行它们的页面位于具有相同的协议(通常为 https),端口号(443 为 https 的默认值),以及主机 (两个页面的模数Document.domain设置为相同的值) 时,这两个脚本才能相互通信。

29.XML和JSON的区别

以下是JSON和XML之间的一些区别:

1、JSON是JavaScript Object Notation;XML是可扩展标记语言。

2、JSON是基于JavaScript语言;XML源自SGML。

3、JSON是一种表示对象的方式;XML是一种标记语言,使用标记结构来表示数据项。

4、JSON不提供对命名空间的任何支持;XML支持名称空间。

5、JSON支持数组;XML不支持数组。

6、XML的文件相对难以阅读和解释;与XML相比,JSON的文件非常易于阅读。

7、JSON不使用结束标记;XML有开始和结束标签。

8、JSON的安全性较低;XML比JSON更安全。

9、JSON不支持注释;XML支持注释。

10、JSON仅支持UTF-8编码;XML支持各种编码。

30.谈谈你对webpack的看法

webpack 是一个用于现代JavaScript应用程序的静态模块打包工具

静态模块

这里的静态模块指的是开发阶段,可以被 webpack 直接引用的资源(可以直接被获取打包进bundle.js的资源)

webpack的能力:

编译代码能力,提高效率,解决浏览器兼容问题 

 模块整合能力,提高性能,可维护性,解决浏览器频繁请求文件的问题 

 万物皆可模块能力,项目维护性增强,支持不同种类的前端模块类型,统一的模块化方案,所有资源文件的加载都可以通过代码控制

31.webpack的打包原理

将根据文件间的依赖关系对其进行静态分析,然后将这些模块按指定规则生成静态资源,当 webpack 处理程序时,会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。

从启动到结束会依次执行以下三大步骤:

初始化流程:从配置文件和 Shell 语句中读取与合并参数,并初始化需要使用的插件和配置插件等执行环境所需要的参数

编译构建流程:从 Entry 发出,针对每个 Module 串行调用对应的 Loader 去翻译文件内容,再找到该 Module 依赖的 Module,递归地进行编译处理

输出流程:对编译后的 Module 组合成 Chunk,把 Chunk 转换成文件,输出到文件系统

32.如何优化webpack打包速度

代码分割

高达 6M 的入口文件显然是非常影响体验的,因此优化的第一步就是从代码分割开始。代码分割通过把项目中的资源模块按照我们设计的规则打包到不同的 bundle 中,从而降低应用的启动成本,提高响应速度。

速度优化

在前面的速度分析中我们已经知道了打包速度主要耗费在 loader 的处理上

很显然在开发过程中进行 webpack 缓存是极其有必要的,我们在处理样式文件和 js 文件的 loader 之前添加 cache-loader 将结果缓存到磁盘中,可以显著提升二次构建速度

33.说说webpack中常见的Loader?解决了什么问题?

loader 用于对模块的"源代码"进行转换,在 import 或"加载"模块时预处理文件

webpack做的事情,仅仅是分析出各种模块的依赖关系,然后形成资源列表,最终打包生成到指定的文件中。

在webpack内部中,任何文件都是模块,不仅仅只是js文件

默认情况下,在遇到import或者require加载模块的时候,webpack只支持对js 和 json 文件打包

像css、sass、png等这些类型的文件的时候,webpack则无能为力,这时候就需要配置对应的loader进行文件内容的解析

关于配置loader的方式有三种:

配置方式(推荐):在 webpack.config.js文件中指定 loader

内联方式:在每个 import 语句中显式指定 loader

CLI 方式:在 shell 命令中指定它们

二、特性

这里继续拿上述代码,来讲讲loader的特性

从上述代码可以看到,在处理css模块的时候,use属性中配置了三个loader分别处理css文件

因为loader支持链式调用,链中的每个loader会处理之前已处理过的资源,最终变为js代码。顺序为相反的顺序执行,即上述执行方式为sass-loader、css-loader、style-loader

可以通过 loader 的预处理函数,为 JavaScript 生态系统提供更多能力。用户现在可以更加灵活地引入细粒度逻辑,例如:压缩、打包、语言翻译和更多其他特性

34.说说webpack中常见的Plugin?解决了什么问题?

Plugin(Plug-in)是一种计算机应用程序,它和主应用程序互相交互,以提供特定的功能

是一种遵循一定规范的应用程序接口编写出来的程序,只能运行在程序规定的系统下,因为其需要调用原纯净系统提供的函数库或者数据

webpack中的plugin也是如此,plugin赋予其各种灵活的功能,例如打包优化、资源管理、环境变量注入等,它们会运行在 webpack 的不同阶段(钩子 / 生命周期),贯穿了webpack整个编译周期

35.说说你对promise的了解

Promise,译为承诺,是异步编程的一种解决方案,比传统的解决方案(回调函数)更加合理和更加强大,解决了回调地狱的问题

瞬间感受到promise解决异步操作的优点:

链式操作减低了编码难度

代码可读性明显增强

状态

promise对象仅有三种状态

pending(进行中)

fulfilled(已成功)

rejected(已失败)

特点

对象的状态不受外界影响,只有异步操作的结果,可以决定当前是哪一种状态

一旦状态改变(从pending变为fulfilled和从pending变为rejected),就不会再变,任何时候都可以得到这个结果

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject

resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”

reject函数的作用是,将Promise对象的状态从“未完成”变为“失败

Promise构建出来的实例存在以下方法:

then()

then是实例状态发生改变时的回调函数,第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数

catch()

catch()方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数

finally()

finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作

36.async函数是什么,有什么作用

async/await是什么?

async/await从字面意思上很好理解,async是异步的意思,await有等待的意思,而两者的用法上也是如此。async用于申明一个function是异步的,而await 用于等待一个异步方法执行完成。

也就是这样一个过程:

async 表示这是一个 async 函数,而 await 只能在这个函数里面使用。

await 表示在这里等待 await 后面的操作执行完毕,再执行下一句代码。

await 后面紧跟着的最好是一个耗时的操作或者是一个异步操作(方法)。

async和await被称作是异步的终极解决方案

将异步操作变成同步操作

await可以阻塞当前线程,将异步操作变成同步,被阻塞的线程并没有被空闲,而是去执行其他的操作

释放异常(抛出异常)

await可以进行求值操作,当await后面跟的是Promise时,如果Promise中的操作是resolve(成功),那么await获取的就是操作成功时的返回值;如果Promise中的操作是reject(失败),await获取的就是操作失败时的返回值,并且如果在await上加了try catch,是可以捕捉到异常的

async的作用是将方法的返回值封装成Promise

37.有使用过vue吗,谈谈对vue的理解?

vue是什么

Vue.js是一个用于创建用户界面的渐进式开源JavaScript框架,也是一个创建单页应用的Web应用框架

Vue核心特性

#数据驱动(MVVM)

MVVM表示的是 Model-View-ViewModel

Model:模型层,负责处理业务逻辑以及和服务器端进行交互

View:视图层:负责将数据模型转化为UI展示出来,可以简单的理解为HTML页面

ViewModel:视图模型层,用来连接Model和View,是Model和View之间的通信桥梁

组件化

1.什么是组件化一句话来说就是把图形、非图形的各种逻辑均抽象为一个统一的概念(组件)来实现开发的模式,在Vue中每一个.vue文件都可以视为一个组件2.组件化的优势

降低整个系统的耦合度,在保持接口不变的情况下,我们可以替换不同的组件快速完成需求,例如输入框,可以替换为日历、时间、范围等组件作具体的实现

调试方便,由于整个系统是通过组件组合起来的,在出现问题的时候,可以用排除法直接移除组件,或者根据报错的组件快速定位问题,之所以能够快速定位,是因为每个组件之间低耦合,职责单一,所以逻辑会比分析整个系统要简单

提高可维护性,由于每个组件的职责单一,并且组件在系统中是被复用的,所以对代码进行优化可获得系统的整体升级

#指令系统

解释:指令 (Directives) 是带有 v- 前缀的特殊属性作用:当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM

常用的指令

条件渲染指令 v-if

列表渲染指令v-for

属性绑定指令v-bind

事件绑定指令v-on

双向数据绑定指令v-model

38.你对SPA单页面的理解,它的优缺点分别是什么?如何实现SPA应用呢

SPA(single-page application),翻译过来就是单页应用SPA是一种网络应用程序或网站的模型,它通过动态重写当前页面来与用户交互,这种方法避免了页面之间切换打断用户体验在单页应用中,所有必要的代码(HTML、JavaScript和CSS)都通过单个页面的加载而检索,或者根据需要(通常是为响应用户操作)动态装载适当的资源并添加到页面,页面在任何时间点都不会重新加载

单页应用优缺点

优点:

具有桌面应用的即时性、网站的可移植性和可访问性

用户体验好、快,内容的改变不需要重新加载整个页面

良好的前后端分离,分工更明确

缺点:

不利于搜索引擎的抓取

首次渲染速度相对较慢

实现一个SPA

#原理

监听地址栏中hash变化驱动界面变化

用pushsate记录浏览器的历史,驱动界面发送变化

hash 模式

核心通过监听url中的hash来进行路由跳转(监听url中#后的路径/hash值,来切换不同的组件,放到router-view上,完成页面的一个局部更新 )

history模式

history 模式核心借用 HTML5 history api,api 提供了丰富的 router 相关属性先了解一个几个相关的api

history.pushState 浏览器历史纪录添加记录

history.replaceState修改浏览器历史纪录中当前纪录

history.popState 当 history 发生变化时触发

39.SPA首屏加载速度慢的怎么解决?

一、什么是首屏加载

首屏时间(First Contentful Paint),指的是浏览器从响应用户输入网址地址,到首屏内容渲染完成的时间,此时整个网页不一定要全部渲染完成,但需要展示当前视窗需要的内容

首屏加载可以说是用户体验中最重要的环节

二 、加载慢的原因

在页面渲染的过程,导致加载速度慢的因素可能如下:

网络延时问题

资源文件体积是否过大

资源是否重复发送请求去加载了

加载脚本的时候,渲染内容堵塞了

三、解决方案

常见的几种SPA首屏优化方式

减小入口文件体积(使用路由懒加载)

静态资源本地缓存(前端利用localstorage)

UI框架按需加载

图片资源的压缩

组件重复打包

开启GZip压缩

使用SSR

40.Vue路由的实现原理

应用最显著特点之一就是采用的前端路由系统,通过改变URL,在不重新请求页面的情况下,更新页面视图。

更新视图但不重新请求页面,是前端路由原理的核心之一,目前在浏览器环境中这一功能的实现主要有2种方式:

利用URL中的hash("#");

利用History interface在HTML5中新增的方法;

41.Vue中组件和插件有什么区别?

一、组件是什么

回顾以前对组件的定义:

组件就是把图形、非图形的各种逻辑均抽象为一个统一的概念(组件)来实现开发的模式,组件是可复用的 Vue 实例, 把一些公共的模块抽取出来,然后写成单独的的工具组件或者页面,在Vue中每一个.vue文件都可以视为一个组件

组件的优势

降低整个系统的耦合度,在保持接口不变的情况下,我们可以替换不同的组件快速完成需求,例如输入框,可以替换为日历、时间、范围等组件作具体的实现

调试方便,由于整个系统是通过组件组合起来的,在出现问题的时候,可以用排除法直接移除组件,或者根据报错的组件快速定位问题,之所以能够快速定位,是因为每个组件之间低耦合,职责单一,所以逻辑会比分析整个系统要简单

提高可维护性,由于每个组件的职责单一,并且组件在系统中是被复用的,所以对代码进行优化可获得系统的整体升级

#二、插件是什么

插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制——一般有下面几种:

添加全局方法或者属性。如: vue-custom-element

添加全局资源:指令/过滤器/过渡等。如 vue-touch

通过全局混入来添加一些组件选项。如vue-router

添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现。

一个库,提供自己的 API,同时提供上面提到的一个或多个功能。如vue-router

三、两者的区别

两者的区别主要表现在以下几个方面:

编写形式

注册形式

使用场景:

组件 (Component) 是用来构成你的 App 的业务模块,它的目标是 App.vue

插件 (Plugin) 是用来增强你的技术栈的功能模块,它的目标是 Vue 本身

简单来说,插件就是指对Vue的功能的增强或补充

42.Vue组件之间的通信方式都有哪些?

组件间通信的分类

组件间通信的分类可以分成以下

父子组件之间的通信

兄弟组件之间的通信

祖孙与后代组件之间的通信

非关系组件间之间的通信

组件间通信的方案

整理vue中8种常规的通信方案

通过 props 传递

通过 $emit 触发自定义事件

使用 ref

EventBus

$parent 或$root

attrs 与 listeners

Provide 与 Inject

Vuex

小结

父子关系的组件数据传递选择 props 与 $emit进行传递,也可选择ref

兄弟关系的组件数据传递可选择$bus,其次可以选择$parent进行传递

祖先与后代组件数据传递可选择attrs与listeners或者 Provide与 Inject

复杂关系的组件数据传递可以通过vuex存放共享的变量

43.你了解vue的diff算法吗?说说看

diff 算法是一种通过同层的树节点进行比较的高效算法

vue2中:

其有两个特点:

比较只会在同层级进行, 不会跨层级比较

在diff比较的过程中,循环从两边向中间比较

diff 算法在很多场景下都有应用,在 vue 中,作用于虚拟 dom 渲染成真实 dom 的新旧 VNode 节点比较

vue3中:

事件缓存:将事件缓存,可以理解为变成静态的了

添加静态标记:Vue2 是全量 Diff,Vue3 是静态标记 + 非全量 Diff

静态提升:创建静态节点时保存,后续直接复用

使用最长递增子序列优化了对比流程:Vue2 里在 updateChildren() 函数里对比变更,在 Vue3 里这一块的逻辑主要在 patchKeyedChildren() 函数里,具体看下面

44.为什么需要Virtual Dom

一、是什么

Real DOM,真实 DOM,意思为文档对象模型,是一个结构化文本的抽象,在页面渲染出的每一个结点都是一个真实 DOM 结构,如下:

Virtual Dom,本质上是以 JavaScript 对象形式存在的对 DOM 的描述

创建虚拟 DOM 目的就是为了更好将虚拟的节点渲染到页面视图中,虚拟 DOM 对象的节点与真实 DOM 的属性一一照应

使用虚拟 DOM 的优势如下:

简单方便:如果使用手动操作真实 DOM 来完成页面,繁琐又容易出错,在大规模应用下维护起来也很困难

性能方面:使用 Virtual DOM,能够有效避免真实 DOM 数频繁更新,减少多次引起重绘与回流,提高性能

跨平台:React 借助虚拟 DOM,带来了跨平台的能力,一套代码多端运行

两者的区别如下:

虚拟 DOM 不会进行排版与重绘操作,而真实 DOM 会频繁重排与重绘

虚拟 DOM 的总损耗是“虚拟 DOM 增删改+真实 DOM 差异增删改+排版与重绘”,真实 DOM 的总损耗是“真实 DOM 完全增删改+排版与重绘”

45.Vue3.0的设计目标是什么?做了哪些优化

更小:

Vue3移除一些不常用的 API

引入tree-shaking,可以将无用模块“剪辑”,仅打包需要的,使打包的整体体积变小了

更快:

主要体现在编译方面:

diff算法优化

静态提升

事件监听缓存

SSR优化

更友好

vue3在兼顾vue2的options API的同时还推出了composition API,大大增加了代码的逻辑组织和代码复用能力

TypeScript支持:

Vue3是基于typeScript编写的,提供了更好的类型检查,能支持复杂的类型推导

API设计一致性:

语法 API

这里当然说的就是composition API,其两大显著的优化:

优化逻辑组织

优化逻辑复用

提高自身可维护性

开放更多底层功能

46.Vue3.0 所采用的 Composition Api 与 Vue2.x 使用的 Options Api 有什么不同?


通常使用Vue2开发的项目,普遍会存在以下问题:

代码的可读性随着组件变大而变差

每一种代码复用的方式,都存在缺点

TypeScript支持有限

然而,当组件变得复杂,导致对应属性的列表也会增长,这可能会导致组件难以阅读和理解

Composition Api

在 Vue3 Composition API 中,组件根据逻辑功能来组织的,一个功能所定义的所有 API 会放在一起(更加的高内聚,低耦合)

即使项目很大,功能很多,我们都能快速的定位到这个功能所用到的所有 API

三、对比

下面对Composition Api与Options Api进行两大方面的比较 逻辑组织 逻辑复用

逻辑组织:

Options API

可以看到,这种碎片化使得理解和维护复杂组件变得困难

选项的分离掩盖了潜在的逻辑问题。此外,在处理单个逻辑关注点时,我们必须不断地“跳转”相关代码的选项块

#Compostion API

而Compositon API正是解决上述问题,将某个逻辑关注点相关的代码全都放在一个函数里,这样当需要修改一个功能时,就不再需要在文件中跳来跳去

下面举个简单例子,将处理count属性相关的代码放在同一个函数了

逻辑复用

使用单个mixin似乎问题不大,但是当我们一个组件混入大量不同的 mixins 的时候

会存在两个非常明显的问题:

命名冲突

数据来源不清晰

小结

在逻辑组织和逻辑复用方面,Composition API是优于Options API

因为Composition API几乎是函数,会有更好的类型推断。

Composition API对 tree-shaking 友好,代码也更容易压缩

Composition API中见不到this的使用,减少了this指向不明的情况

如果是小型组件,可以继续使用Options API,也是十分友好的

47.说一下Vue数据响应式的原理

意思就是在改变数据的时候,视图会跟着更新。这意味着你只需要进行数据的管理,给我们搬砖提供了很大的便利。React也有这种特性,但是React的响应式方式跟VUE完全不同。

“vue的数据响应式就是当数据发生变化,通知改变的代码。数据响应式原理的核心就是采用了数据劫持结合发布者-订阅者模式的方式来实现数据的响应式,通过Object.defineProperty()对数据进行拦截,把这些属性全部转换成getter/setter,get()方法可以读取数据、收集依赖,set()方法可以改写数据,在数据变动时会对数据进行比较,如果数据发生了变化,会发布消息通知订阅者,触发监听回调,更新视图。”

React是通过this.setState去改变数据,然后根据新的数据重新渲染出虚拟DOM,最后通过对比虚拟DOM找到需要更新的节点进行更新。

也就是说React是依靠着虚拟DOM以及DOM的diff算法做到这一点的。而关于React这方面的文章,我已经写了很多了,还不了解的同学可以自行复习一下

而VUE则是利用了Object.defineProperty的方法里面的setter 与getter方法的观察者模式来实现。

所以在学习VUE的响应式原理之前,先学习两个预备知识:

Object.defineProperty 与 观察者模式。

如果你已经掌握了,可以直接跳到第三part。

二、预备知识

2.1 Object.defineProperty

这个方法就是在一个对象上定义一个新的属性,或者改变一个对象现有的属性,并且返回这个对象。里面有两个字段 set,get。顾名思义,set都是取设置属性的值,而get就是获取属性的值。

举个栗子:

varbValue;

varo={};

Object.defineProperty(o,"b",

{get:function(){console.log('监听正在获取b')returnbValue;},

set:function(newValue){console.log('监听正在设置b')bValue=newValue;},

enumerable:true,configurable:true});

o.b=38;console.log(o.b)

最终打印

监听正在设置b监听正在获取b38

从在上述栗子中,可以看到当我们对 o.b 赋值38的时候,就会调用set函数,这时候会通知订阅者触发指令update函数给bValue修改值,之后我们就可以通过o.b来获取这个值,这时候,get函数被调用。

掌握到这一步,我们已经可以实现一个极简的VUE双向绑定了。

48.说说对 React 的理解?有哪些特性?

一、是什么

React,用于构建用户界面的 JavaScript 库,只提供了 UI 层面的解决方案

遵循组件设计模式、声明式编程范式和函数式编程概念,以使前端应用程序更高效

使用虚拟 DOM 来有效地操作 DOM,遵循从高阶组件到低阶组件的单向数据流

帮助我们将界面成了各个独立的小块,每一个块就是组件,这些组件之间可以组合、嵌套,构成整体页面

react 类组件使用一个名为 render() 的方法或者函数组件return,接收输入的数据并返回需要展示的内容

二、特性

React 特性有很多,如:

JSX 语法

单向数据绑定

虚拟 DOM:

当组件(是Virtual DOM上的组件)的状态(state)发生改变时,一个神奇的diff算法会计算出最少的步骤更新到浏览器页面上,diff算法寻找到需要变更的DOM节点,然后把这个修改更新到浏览器的DOM节点上。

声明式编程:

而用 React 实现上述功能则如下:

<Mapzoom={4}center={(lat,lng)}>  <Markerposition={(lat,lng)}title={"Hello Marker"}/></Map>

声明式编程方式使得 React 组件很容易使用,最终的代码简单易于维护

Component

49.说说 Real DOM 和 Virtual DOM 的区别?优缺点?

一、是什么

Real DOM,真实 DOM,意思为文档对象模型,是一个结构化文本的抽象,在页面渲染出的每一个结点都是一个真实 DOM 结构,如下:

Virtual Dom,本质上是以 JavaScript 对象形式存在的对 DOM 的描述

创建虚拟 DOM 目的就是为了更好将虚拟的节点渲染到页面视图中,虚拟 DOM 对象的节点与真实 DOM 的属性一一照应

二、区别

两者的区别如下:

虚拟 DOM 不会进行排版与重绘操作,而真实 DOM 会频繁重排与重绘

虚拟 DOM 的总损耗是“虚拟 DOM 增删改+真实 DOM 差异增删改+排版与重绘”,真实 DOM 的总损耗是“真实 DOM 完全增删改+排版与重绘”

三、优缺点

真实 DOM 的优势:易用

缺点:

效率低,解析速度慢,内存占用量过高

性能差:频繁操作真实 DOM,易于导致重绘与回流

使用虚拟 DOM 的优势如下:

简单方便:如果使用手动操作真实 DOM 来完成页面,繁琐又容易出错,在大规模应用下维护起来也很困难

性能方面:使用 Virtual DOM,能够有效避免真实 DOM 数频繁更新,减少多次引起重绘与回流,提高性能

跨平台:React 借助虚拟 DOM,带来了跨平台的能力,一套代码多端运行

缺点:

在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化

首次渲染大量 DOM 时,由于多了一层虚拟 DOM 的计算,速度比正常稍慢

50.说说React 生命周期有哪些不同阶段?每个阶段对应的方法是?

一、是什么

React整个组件生命周期包括从创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程

二、流程

这里主要讲述react16.4之后的生命周期,可以分成三个阶段:

创建阶段:

创建阶段主要分成了以下几个生命周期方法:

constructor:在 React 组件挂载之前,会调用它的构造函数。

getDerivedStateFromProps:组件创建和更新阶段,不论是props变化还是state变化,也会调用

在每次render方法前调用,第一个参数为即将更新的props,第二个参数为上一个状态的state,可以比较

props 和 state来加一些限制条件,防止无用的state更新,该方法需要返回一个新的对象作为新的state或者返回null

表示state状态不需要更新

render:类组件必须实现的方法,用于渲染DOM结构,可以访问组件state与prop属性

componentDidMount:组件挂载到真实DOM节点后执行,其在render方法之后执行

更新阶段:

该阶段的函数主要为如下方法:

getDerivedStateFromProps:同上

shouldComponentUpdate:用于控制组件重新渲染的生命周期,state发生变化,组件会进入重新渲染的流程,在这里return false可以阻止组件的更新

render:render() 方法是 class 组件中唯一必须实现的方法。

getSnapshotBeforeUpdate:触发时间为update发生的时候,在render之后dom渲染之前返回一个值, 读取到的 DOM 元素状态是可以保证与 componentDidUpdate 中一致的

componentDidUpdate: 每次state改变并重新渲染页面后都会进入这个生命周期卸载或销毁阶段

卸载阶段:

componentWillUnmount:在此处完成组件的卸载和数据的销毁。

51.说说 React中的setState执行机制

一个组件的显示形态可以由数据状态和外部参数所决定,而数据状态就是state, 当需要修改里面的值的状态需要通过调用setState来改变,从而达到更新组件内部数据的作用

setState第一个参数可以是一个对象,或者是一个函数,而第二个参数是一个回调函数,用于可以实时的获取到更新之后的数据

在使用setState更新数据的时候,setState的更新类型分成:同步更新,异步更新

在组件生命周期或React合成事件中,setState是异步

在setTimeout或者原生dom事件中,setState是同步

对同一个值进行多次 setState, setState 的批量更新策略会对其进行覆盖,取最后一次的执行结果

52.说说对React中类组件和函数组件的理解?有什么区别?

语法上:

函数式组件是一个纯函数,它是需要接受props参数并且返回一个React元素就可以了。类组件是需要继承React.Component的,而且class组件需要创建render并且返回React元素,语法上来讲更复杂。

状态管理:

函数式组件没有状态管理,类组件有状态管理。

调用方式:

函数式组件可以直接调用,返回一个新的React元素;类组件在调用时是需要创建一个实例的,然后通过调用实例里的render方法来返回一个React元素。

53.说说对React Hooks的理解?解决了什么问题?

一、是什么

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性

至于为什么引入hook,官方给出的动机是解决长时间使用和维护react过程中常遇到的问题,例如:

难以重用和共享组件中的与状态相关的逻辑

逻辑复杂的组件难以开发与维护,当我们的组件需要处理多个互不相关的 local state 时,每个生命周期函数中可能会包含着各种互不相关的逻辑在里面

类组件中的this增加学习成本,类组件在基于现有工具的优化上存在些许问题

由于业务变动,函数组件不得不改为类组件等等

二、有哪些

上面讲到,Hooks让我们的函数组件拥有了类组件的特性,例如组件内的状态、生命周期

最常见的hooks有如下:

useState

useEffect

useReducer

useCallback

useMemo

useRef

三、解决什么

通过对上面的初步认识,可以看到hooks能够更容易解决状态相关的重用的问题:

每调用useHook一次都会生成一份独立的状态

通过自定义hook能够更好的封装我们的功能

54.说说你对Redux的理解?其工作原理

一、是什么

在react中每个组件的state是由自身进行管理,包括组件定义自身的state、组件之间的通信通过props传递、使用Context实现数据共享等,如果让每个组件都存储自身相关的状态,理论上来讲不会影响应用的运行,但在开发及后期我们将比较难以维护,所以我们可以把数据进行集中式的管理,redux就是一个实现上述集中管理的容器的工具,redux并不是只应用在react中,还与其他界面库一起使用,如Vue

二、工作原理

创建一个store文件夹,新建一个index.js文件

文件中导入redux的createStore方法,用于创建公共数据区域

创建一个reducer纯函数,接受两个参数state,actions分别表示分别表示数据和操作state的方法,返回state数据给组件页面

把reducer作为createStore的参数抛出

在需要使用的页面导入store文件,通过store.getState获取数据,通过store.dispatch触发action修改state数据

store.subscrible 方法监听 store 的改变,避免数据不更新

55.说说React 性能优化的手段有哪些

一、是什么

React凭借virtual DOM和diff算法拥有高效的性能,但是某些情况下,性能明显可以进一步提高

在前面文章中,我们了解到类组件通过调用setState方法, 就会导致render,父组件一旦发生render渲染,子组件一定也会执行render渲染

二、如何做

React中如何避免不必要的render (opens new window)中,我们了解到如何避免不必要的render来应付上面的问题,主要手段是通过shouldComponentUpdate、PureComponent、React.memo,这三种形式这里就不再复述

除此之外, 常见性能优化常见的手段有如下:

避免使用内联函数

使用 React Fragments 避免额外标记

使用 Immutable:

理解Immutable中 (opens new window),我们了解到使用 Immutable可以给 React 应用带来性能的优化,主要体现在减少渲染的次数

在做react性能优化的时候,为了避免重复渲染,我们会在shouldComponentUpdate()中做对比,当返回true执行render方法

Immutable通过is方法则可以完成对比,而无需像一样通过深度比较的方式比较

懒加载组件:

从工程方面考虑,webpack存在代码拆分能力,可以为应用创建多个包,并在运行时动态加载,减少初始包的大小

而在react中使用到了Suspense和 lazy组件实现代码拆分功能

事件绑定方式:

从性能方面考虑,在render方法中使用bind和render方法中使用箭头函数这两种形式在每次组件render的时候都会生成新的方法实例,性能欠缺

而constructor中bind事件与定义阶段使用箭头函数绑定这两种形式只会生成一个方法实例,性能方面会有所改善

服务端渲染:

采用服务端渲染端方式,可以使用户更快的看到渲染完成的页面

服务端渲染,需要起一个node服务,可以使用express、koa等,调用react的renderToString方法,将根组件渲染成字符串,再输出到响应中

56.vue、react、angular区别

1. 基本概念

Angular 是一个应用设计框架与开发平台,用于创建高效、复杂、精致的单页面应用。

React 是一个用于构建用户界面的 JavaScript 库

Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。

2. 三者比较

2.1 相同点

1.    都是基于javascript/typescript的前端开发库,为前端开发提供高效、复用性高的开发方式

2.    都有组件和模板的开发思想

3.    各自的组件都有生命周期,不用的组件可以卸载,不占用资源

4.  都支持指令,如样式、事件等的指令

2.2 不同点

1.  创始和发行不同:

        Angular是由googl提供支持的,初始发行于 2016年9月;React由Facebook维护,初始发行于 2013年3月;Vue是由前google人员创建,初始发行于2014年2月

2.  应用类型不同:

        Angular支持开发native应用程序、SPA单页应用程序、混合应用程序和web应用程序;React支持开发SPA和移动应用程序;Vue支持开发高级SPA,开始支持native应用程序

3.  模型不同

        angular基于MVC(模型-视图-控制器)架构;react和vue是基于Virtual DOM(虚拟的文档对象模型)

4.  数据流流向不同

      Angular使用的是双向数据绑定,React用的是单数据流的,而Vue则支持两者。

5. 对微应用和微服务的支持不同

        Angular使用的是TypeScript,因此它更适合于单页Web应用(single page web application,SPA),而非微服务。相反,React和Vue的灵活性更适合微应用和微服务的开发。

6. 对原生应用的支持不同

        React Native为iOS和Android开发原生应用;Angular的NativeScript已被原生应用所采用,特别是Ionic框架已经被广泛地运用在制作混合应用等方面;Vue的Weex平台正在开发之中,尚无下一步使之成为全面跨平台的计划。

7. 框架和库

        Angular 是一个框架而不是一个库,因为它提供了关于如何构建应用程序的强有力的约束,并且还提供了更多开箱即用的功能。React 和 Vue 是是一种库,可以和各种包搭配。

8. 组件之间传值方式不同

    Angular 中直接的父子组件,父组件可以直接访问子组件的 public 属性和方法,也可以借助于@Input 和 @Output 进行通讯。没有直接关系的,借助于 Service 单例进行通讯;React 组件之间通过通过prop或者state来通信,不同组件之间还有Rex状态管理功能;Vue组件之间通信通过props ,以及Vuex状态管理来传值

57.说说你对 TypeScript 的理解?与 JavaScript 的区别?

一、是什么

TypeScript 是 JavaScript 的类型的超集,支持ES6语法,支持面向对象编程的概念,如类、接口、继承、泛型等

其是一种静态类型检查的语言,提供了类型注解,在代码编译阶段就可以检查出数据类型的错误

同时扩展了JavaScript 的语法,所以任何现有的JavaScript 程序可以不加改变的在 TypeScript 下工作

为了保证兼容性,TypeScript 在编译阶段需要编译器编译成纯 JavaScript 来运行,是为大型应用之开发而设计的语言

二、特性

TypeScript 的特性主要有如下:

类型批注和编译时类型检查 :在编译时批注变量类型

类型推断:ts 中没有批注变量类型会自动推断变量的类型

类型擦除:在编译过程中批注的内容和接口会在运行时利用工具擦除

接口:ts 中用接口来定义对象类型

枚举:用于取值被限定在一定范围内的场景

Mixin:可以接受任意类型的值

泛型编程:写代码时使用一些以后才指定的类型

名字空间:名字只在该区域内有效,其他区域可重复使用该名字而不冲突

元组:元组合并了不同类型的对象,相当于一个可以装不同类型数据的数组

三、区别

TypeScript 是 JavaScript 的超集,扩展了 JavaScript 的语法

TypeScript 可处理已有的 JavaScript 代码,并只对其中的 TypeScript 代码进行编译

TypeScript 文件的后缀名 .ts (.ts,.tsx,.dts),JavaScript 文件是 .js

在编写 TypeScript 的文件的时候就会自动编译成 js 文件

58.说说你对 TypeScript 中泛型的理解?应用场景?

一、是什么

泛型程序设计(generic programming)是程序设计语言的一种风格或范式

泛型允许我们在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型 在typescript中,定义函数,接口或者类的时候,不预先定义好具体的类型,而在使用的时候在指定类型的一种特性

二、应用场景

通过上面初步的了解,后述在编写 typescript 的时候,定义函数,接口或者类的时候,不预先定义好具体的类型,而在使用的时候在指定类型的一种特性的时候,这种情况下就可以使用泛型

灵活的使用泛型定义类型,是掌握typescript 必经之路

59.说说你对微信小程序的理解?优缺点?

一、是什么

小程序是一种不需要下载安装即可使用的应用,它实现了应用“触手可及”的梦想,用户扫一扫或者搜一下即可打开应用

也体现了“用完即走”的理念,用户不用关心是否安装太多应用的问题。应用将无处不在,随时可用,但又无需安装卸载

二、优缺点

优点:

随搜随用,用完即走:使得小程序可以代替许多APP,或是做APP的整体嫁接,或是作为阉割版功能的承载体

流量大,易接受:小程序借助自身平台更加容易引入更多的流量

安全

开发门槛低

降低兼容性限制

缺点:

用户留存:及相关数据显示,小程序的平均次日留存在13%左右,但是双周留存骤降到仅有1%

体积限制:微信小程序只有2M的大小,这样导致无法开发大型一些的小程序

受控微信:比起APP,尤其是安卓版的高自由度,小程序要面对很多来自微信的限制,从功能接口,甚至到类别内容,都要接受微信的管控

60.说说你对发布订阅、观察者模式的理解?区别?

一、观察者模式

观察者模式定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并自动更新

观察者模式属于行为型模式,行为型模式关注的是对象之间的通讯,观察者模式就是观察者和被观察者之间的通讯

二、发布订阅模式

发布-订阅是一种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。而是将发布的消息分为不同的类别,无需了解哪些订阅者(如果有的话)可能存在

同样的,订阅者可以表达对一个或多个类别的兴趣,只接收感兴趣的消息,无需了解哪些发布者存在

三、区别

在观察者模式中,观察者是知道Subject的,Subject一直保持对观察者进行记录。然而,在发布订阅模式中,发布者和订阅者不知道对方的存在。它们只有通过消息代理进行通信。

在发布订阅模式中,组件是松散耦合的,正好和观察者模式相反。

观察者模式大多数时候是同步的,比如当事件触发,Subject就会去调用观察者的方法。而发布-订阅模式大多数时候是异步的(使用消息队列)

61.项目做过哪些性能优化

一、体验优化

从用户角度而言,优化能够让页面加载得更快、对用户的操作响应得更及时,能够给用户提供更为友好的体验。

1,首屏渲染优化,请求少、加载体积小、善用缓存

2,动画优化,避免某些动画造成页面的卡顿

3,优化用户的操作感官,提升视觉反馈,比如 hover 小手,让用户一眼就知道是否可操作

4,长列表复用 dom,优化滚动效果及页面卡顿现象,减少页面一次性渲染的数量

5,骨架屏的使用

6,组件的预加载,懒加载

二、提升页面性能

减少 http 请求 和 冗余数据

组件,路由懒加载

配置 nginx 优化

优化 webpack 打包机制

使用 CDN

预渲染

SSR

图片转 base64

后台分布式部署,负载均衡

62.描述浏览器的渲染过程,DOM树和渲染树的区别

浏览器的渲染过程:

解析 HTML 构建 DOM(DOM 树),并行请求 css/image/js

CSS 文件下载完成,开始构建 CSSOM(CSS 树)

CSSOM 构建结束后,和 DOM 一起生成 Render Tree(渲染树)

布局(Layout):计算出每个节点在屏幕中的位置

显示(Painting):通过显卡把页面画到屏幕上

DOM 树 和 渲染树 的区别:

DOM 树与 HTML 标签一一对应,包括 head 和隐藏元素

渲染树不包括 head 和隐藏元素,大段文本的每一个行都是独立节点,每一个节点都有对应的 css 属性

63.你认为什么样的前端代码是好的

1.如何评价代码质量的高低?

代码质量的评价有很强的主观性,描述代码质量的词汇也有很多,比如可读性、可维护性、灵活、优雅、简洁等,这些词汇是从不同的维度去评价代码质量的。它们之间有互相作用,并不是独立的,比如,代码的可读性好、可扩展性好就意味着代码的可维护性好。代码质量高低是一个综合各种因素得到的结论。我们并不能通过单一的维度去评价一段代码的好坏。

2.最常用的评价标准有哪几个?

最常用到几个评判代码质量的标准是:可维护性、可读性、可扩展性、灵活性、简洁性、可复用性、可测试性。其中,可维护性、可读性、可扩展性又是提到最多的、最重要的三个评价标准。

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

浏览器根据请求的 URL 交给 DNS 域名解析,找到真实 IP,向服务器发起请求;

服务器交给后台处理完成后返回数据,浏览器接收文件(HTML、JS、CSS、图像等);

浏览器对加载到的资源(HTML、JS、CSS 等)进行语法解析,建立相对应的内部数据结构(如 HTML 的 DOM);

载入解析到的资源文件,渲染页面,完成。

65.http请求报文响应报文的格式

HTTP请求报文格式

客户端连上服务器后,向服务器发出获取某个Web资源的消息,称之为客户端向服务器发送了一个HTTP请求。

HTTP请求报文主要由

请求行(说明请求类型,要访问的资源以及使用http版本)

请求头部(用来说明服务器要使用的附加信息)

空行、请求正文组成。

例如,GET /index.html HTTP/1.1

HTTP响应报文格式

所谓响应其实就是服务器对请求处理的结果,或者如果浏览器请求的直接就是一个静态资源的话,响应的就是这个资源本身。HTTP响应报文主要由:

状态行(http协议版本号,状态码,状态信息)

消息头部(用来说明客户端要使用的一些附加信息)

空行、响应正文组成。

66.Token cookie session 区别

1.Session会话:客户端A访问服务器,服务器存储A的数据value,把key返回给客户端A,客户端A下次带着key(session ID)来访问服务器,服务器就能给出客户端A的数据。如果负载均衡,客户端A访问了另一个服务器,那个服务器没有客户端A的数据。

2.Cookie:客户端A访问服务器,服务器返回cookie给客户端A,客户端A存储cookie,下次需要带着cookie访问服务器,服务器返回相应的数据。

3.Token令牌:客户端A访问服务器,服务器给了客户端token,客户端A拿着token访问服务器,服务器验证token,返回数据。

二、下面详细介绍一下session、cookie、token的区别,详细如下:

1.session和cookie区别:

·      数据存放位置不同:Session数据是存在服务器中的,cookie数据存放在浏览器当中。

·      安全程度不同:cookie放在服务器中不是很安全,session放在服务器中,相对安全。

·      性能使用程度不同:session放在服务器上,访问增多会占用服务器的性能;考虑到减轻服务器性能方面,应使用cookie。

·      数据存储大小不同:单个cookie保存的数据不能超过4K,session存储在服务端,根据服务器大小来定。

2.token和session区别:

·      token是开发定义的,session是http协议规定的;

·      token不一定存储,session存在服务器中;

·      token可以跨域,session不可以跨域,它是与域名绑定的。

67.CORS跨域的原理

什么是 CORS?

CORS 全称 Cross-Origin Resource Sharing,即跨域资源共享。

CORS 是一种基于 HTTP Header 的机制,该机制通过允许服务器标示除了它自己以外的其它域。服务器端配合浏览器实现 CORS 机制,可以突破浏览器对跨域资源访问的限制,实现跨域资源请求。

CORS 验证机制

CORS 的验证机制分两种模式:简单请求和预检请求。

简单请求

简单请求模式下浏览器直接发送请求,并在请求头中携带 Origin。 服务器端接到请求后,会根据自己的跨域规则,通过响应头 Access-Control-Allow-Origin 来返回验证结果。

预检请求

需要预检的请求必须首先使用 OPTIONS 方法发起一个预检请求到服务器,服务器基于预检请求的信息来判断是否接受接下来的实际请求。

68.什么是MVVM

1、什么是MVVM

MVVM,是Model-View-ViewModel的简写,是M-V-VM三部分组成。

Model:模型层,负责处理业务逻辑以及和服务器端进行交互

View:视图层:负责将数据模型转化为UI展示出来,可以简单的理解为HTML页面

ViewModel:视图模型层,用来连接Model和View,是Model和View之间的通信桥梁

MVVM采用双向数据绑定,view中数据变化将自动反映到viewmodel上,反之,model中数据变化也将会自动展示在页面上。把Model和View关联起来的就是ViewModel。ViewModel负责把Model的数据同步到View显示出来,还负责把View的修改同步回Model。

MVVM核心思想,是关注model的变化,让MVVM框架利用自己的机制自动更新DOM,也就是所谓的数据-视图分离,数据不会影响视图。

69.说说你对版本管理的理解?常用的版本管理工具有哪些?

一、是什么

版本控制(Version control),是维护工程蓝图的标准作法,能追踪工程蓝图从诞生一直到定案的过程。此外,版本控制也是一种软件工程技巧,借此能在软件开发的过程中,确保由不同人所编辑的同一程序文件都得到同步

透过文档控制,能记录任何工程项目内各个模块的改动历程,并为每次改动编上序号

版本控制系统在当今的软件开发中,被认为是理所当然的配备工具之一,根据类别可以分成:

本地版本控制系统

集中式版本控制系统:

SVN

TortoiseSVN是一款非常易于使用的跨平台的 版本控制/版本控制/源代码控制软件

CVS

CVS是版本控制系统,是源配置管理(SCM)的重要组成部分。使用它,您可以记录源文件和文档的历史记录

老牌的版本控制系统,它是基于客户端/服务器的行为使得其可容纳多用户,构成网络也很方便

这一特性使得CVS成为位于不同地点的人同时处理数据文件(特别是程序的源代码)时的首选

分布式版本控制系统

Git

Git是目前世界上最先进的分布式版本控制系统,旨在快速高效地处理从小型到大型项目的所有事务

特性:易于学习,占用内存小,具有闪电般快速的性能

使用Git和Gitlab搭建版本控制环境是现在互联网公司最流行的版本控制方式

HG

Mercurial是一个免费的分布式源代码管理工具。它可以有效地处理任何规模的项目,并提供简单直观的界面

Mercurial是一种轻量级分布式版本控制系统,采用 Python语言实现,易于学习和使用,扩展性强

三、总结

版本控制系统的优点如下:

记录文件所有历史变化,这是版本控制系统的基本能力

随时恢复到任意时间点,历史记录功能使我们不怕改错代码了

支持多功能并行开发,通常版本控制系统都支持分支,保证了并行开发的可行

多人协作并行开发,对于多人协作项目,支持多人协作开发的版本管理将事半功倍

70.说说你对Git的理解?

一、是什么

git,是一个分布式版本控制软件,最初目的是为更好地管理Linux内核开发而设计分布式版本控制系统的客户端并不只提取最新版本的文件快照,而是把代码仓库完整地镜像下来。这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复(GIT就是一个共享代码库,代码放在网上统一管理,大家谁修改了代码都可以在网上改,这样可以实现多人并发开发一个大项目)

项目开始,只有一个原始版仓库,别的机器可以clone这个原始版本库,那么所有clone的机器,它们的版本库其实都是一样的,并没有主次之分

所以在实现团队协作的时候,只要有一台电脑充当服务器的角色,其他每个人都从这个“服务器”仓库clone一份到自己的电脑上,并且各自把各自的提交推送到服务器仓库里,也从服务器仓库中拉取别人的提交

github实际就可以充当这个服务器角色,其是一个开源协作社区,提供Git仓库托管服务,既可以让别人参与你的开源项目,也可以参与别人的开源项目

二、工作原理

当我们通过git init创建或者git clone一个项目的时候,项目目录会隐藏一个.git子目录,其作用是用来跟踪管理版本库的

Git 中所有数据在存储前都计算校验和,然后以校验和来引用,所以在我们修改或者删除文件的时候,git能够知道

Git用以计算校验和的机制叫做 SHA-1 散列(hash,哈希), 这是一个由 40 个十六进制字符(0-9 和 a-f)组成字符串,基于 Git 中文件的内容或目录结构计算出来,如下:

当我们修改文件的时候,git就会修改文件的状态,可以通过git status进行查询,状态情况如下:

已修改(modified):表示修改了文件,但还没保存到数据库中。

已暂存(staged):表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。

已提交(committed):表示数据已经安全的保存在本地数据库中。

文件状态对应的,不同状态的文件在Git中处于不同的工作区域,主要分成了四部分:

工作区:相当于本地写代码的区域,如 git clone 一个项目到本地,相当于本地克隆了远程仓库项目的一个副本

暂存区:暂存区是一个文件,保存了下次将提交的文件列表信息,一般在 Git 仓库目录中

本地仓库:提交更新,找到暂存区域的文件,将快照永久性存储到 Git 本地仓库

远程仓库:远程的仓库,如 github

71.说说Git常用的命令有哪些

从上图可以看到,git日常简单的使用就只有上图6个命令:

add

commit

push

pull

clone

checkout

72.说说 git 发生冲突的场景?如何解决?

一、是什么

一般情况下,出现冲突的场景有如下:

多个分支代码合并到一个分支时

多个分支向同一个远端分支推送

具体情况就是,多个分支修改了同一个文件(任何地方)或者多个分支修改了同一个文件的名称

如果两个分支中分别修改了不同文件中的部分,是不会产生冲突,直接合并即可

应用在命令中,就是push、pull、stash、rebase等命令下都有可能产生冲突情况,从本质上来讲,都是merge和patch(应用补丁)时产生冲突

二、总结

当Git无法自动合并分支时,就必须首先解决冲突,解决冲突后,再提交,合并完成

解决冲突就是把Git合并失败的文件手动编辑为我们希望的内容,再提交

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269