说一说cookie sessionStorage localStorage 区别?
得分点 : 数据存储位置
、生命周期
、存储大小
、写入方式、数据共享、发送请求时是否携带、应用场景
标准回答 : Cookie、SessionStorage、 LocalStorage都是浏览器的本地存储。 它们的共同点:都是存储在浏览器本地的 它们的区别:cookie是由服务器端写入的,而SessionStorage、 LocalStorage都是由前端写入的,cookie的生命周期是由服务器端在写入的时候就设置好的,LocalStorage是写入就一直存在,除非手动清除,SessionStorage是页面关闭的时候就会自动清除。cookie的存储空间比较小大概4KB,SessionStorage、 LocalStorage存储空间比较大,大概5M。Cookie、SessionStorage、 LocalStorage数据共享都遵循同源原则,SessionStorage还限制必须是同一个页面。在前端给后端发送请求的时候会自动携带Cookie中的数据,但是SessionStorage、 LocalStorage不会 加分回答 由于它们的以上区别,所以它们的应用场景也不同,Cookie一般用于存储登录验证信息SessionID或者token,LocalStorage常用于存储不易变动的数据,减轻服务器的压力,SessionStorage可以用来检测用户是否是刷新进入页面,如音乐播放器恢复播放进度条的功能。
说一说JS数据类型有哪些,区别是什么?
解题思路
得分点
Number
、String
、Boolean
、BigInt
、Symbol
、Null
、Undefined
、Object
、8种
标准回答 : JS数据类型分为两类:一类是基本数据类型,也叫简单数据类型,包含7种类型,分别是Number 、String、Boolean、BigInt、Symbol、Null、Undefined。另一类是引用数据类型也叫复杂数据类型,通常用Object代表,普通对象,数组,正则,日期,Math数学函数都属于Object。
数据分成两大类的本质区别:基本数据类型和引用数据类型它们在内存中的存储方式不同。
基本数据类型是直接存储在栈中的简单数据段,占据空间小,属于被频繁使用的数据。
引用数据类型是存储在堆内存中,占据空间大。引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址,当解释器寻找引用值时,会检索其在栈中的地址,取得地址后从堆中获得实体。
说一说你对闭包的理解?
得分点 : 变量背包、作用域链、局部变量不销毁、函数体外访问函数的内部变量、内存泄漏、内存溢出、形成块级作用域、柯里化、构造函数中定义特权方法、Vue中数据响应式Observer
标准回答: 闭包 一个函数和词法环境的引用捆绑在一起,这样的组合就是闭包(closure)。一般就是一个函数A,return其内部的函数B,被return出去的B函数能够在外部访问A函数内部的变量,这时候就形成了一个B函数的变量背包,A函数执行结束后这个变量背包也不会被销毁,并且这个变量背包在A函数外部只能通过B函数访问。 闭包形成的原理:作用域链,当前作用域可以访问上级作用域中的变量 闭包解决的问题:能够让函数作用域中的变量在函数执行结束之后不被销毁,同时也能在函数外部可以访问函数内部的局部变量。 闭包带来的问题:由于垃圾回收器不会将闭包中变量销毁,于是就造成了内存泄露,内存泄露积累多了就容易导致内存溢出。 加分回答 闭包的应用,能够模仿块级作用域,能够实现柯里化,在构造函数中定义特权方法、Vue中数据响应式Observer中使用闭包等。
说一说promise是什么与使用方法?
得分点: pendding、rejected、resolved、微任务、then、catch、Promise.resolve()、Promise.reject()、Promise.all() Promise.any()、Promise.race()
标准回答 : Promise的作用:Promise是异步微任务,解决了异步多层嵌套回调的问题,让代码的可读性更高,更容易维护 Promise使用:Promise是ES6提供的一个构造函数,可以使用Promise构造函数new一个实例,Promise构造函数接收一个函数作为参数,这个函数有两个参数,分别是两个函数 resolve
和reject
,resolve
将Promise的状态由等待变为成功,将异步操作的结果作为参数传递过去;reject
则将状态由等待转变为失败,在异步操作失败时调用,将异步操作报出的错误作为参数传递过去。实例创建完成后,可以使用then
方法分别指定成功或失败的回调函数,也可以使用catch捕获失败,then和catch最终返回的也是一个Promise,所以可以链式调用。 Promise的特点: 1. 对象的状态不受外界影响(Promise对象代表一个异步操作,有三种状态)。 - pending(执行中) - Resolved(成功,又称Fulfilled) - rejected(拒绝) 其中pending为初始状态,fulfilled和rejected为结束状态(结束状态表示promise的生命周期已结束)。 2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。 Promise对象的状态改变,只有两种可能(状态凝固了,就不会再变了,会一直保持这个结果): - 从Pending变为Resolved - 从Pending变为Rejected 3. resolve 方法的参数是then中回调函数的参数,reject 方法中的参数是catch中的参数 4. then 方法和 catch方法 只要不报错,返回的都是一个fullfilled状态的promise 加分回答 Promise的其他方法: Promise.resolve() :返回的Promise对象状态为fulfilled,并且将该value传递给对应的then方法。 Promise.reject():返回一个状态为失败的Promise对象,并将给定的失败信息传递给对应的处理方法。 Promise.all():返回一个新的promise对象,该promise对象在参数对象里所有的promise对象都成功的时候才会触发成功,一旦有任何一个iterable里面的promise对象失败则立即触发该promise对象的失败。 Promise.any():接收一个Promise对象的集合,当其中的一个 promise 成功,就返回那个成功的promise的值。 Promise.race():当参数里的任意一个子promise被成功或失败后,父promise马上也会用子promise的成功返回值或失败详情作为参数调用父promise绑定的相应句柄,并返回该promise对象。
简化来说:
概念:异步编程的一种解决方案,解决了地狱回调的问题 2. 使用方法:new Promise((resolve,reject) => { resolve(); reject(); })里面有多个resovle或者reject只执行第一个。如果第一个是resolve的话后面可以接.then查看成功消息。如果第一个是reject的话,.catch查看错误消息。
说一说跨域是什么?如何解决跨域问题?
得分点
同源限制、协议、域名、端口、CORS、node中间件、JSONP、postmessage
跨域:当前页面中的某个接口请求的地址和当前页面的地址如果协议、域名、端口其中有一项不同,就说该接口跨域了。
跨域限制的原因:浏览器为了保证网页的安全,出的同源协议策略。
跨域报错信息:
跨域解决方案
cors:目前最常用的一种解决办法,通过设置后端允许跨域实现。
res.setHeader(‘Access-Control-Allow-Origin’, ‘*’);
res.setHeader(“Access-Control-Allow-Methods”, “GET, PUT, OPTIONS, POST”);
node中间件、nginx反向代理:跨域限制的时候浏览器不能跨域访问服务器,node中间件和nginx反向代理,都是让请求发给代理服务器,静态页面面和代理服务器是同源的,然后代理服务器再向后端服务器发请求,服务器和服务器之间不存在同源限制。
JSONP:利用的原理是script标签可以跨域请求资源,将回调函数作为参数拼接在url中。后端收到请求,调用该回调函数,并将数据作为参数返回去,注意设置响应头返回文档类型,应该设置成javascript。
postmessage:H5新增API,通过发送和接收API实现跨域通信。
简单来说:
跨域场景:前后端分离式开发、调用第三方接口
说一说BFC
BFC被称为块级格式上下文,是一个独立渲染区域,不会影响到外面的元素。BFC的创建条件有:float不为空、position为absolute或fixed、display为inline-block等、overflow:hidden。布局规则:box在垂直方向上,且距离由margin决定,计算BFC高度时要考虑浮动元素。BFC使用场景:清除浮动,去除边距重叠
【主要用途】
1、清除内部浮动,父元素设置为BFC可以清除子元素的浮动(最常用overflow:hidden,IE6需加上*zoom:1):计算BFC高度时会检测浮动子盒子高度
2、解决外边距合并问题
3、右侧盒子自适应:BFC区域不会与浮动盒子产生交集,而是紧贴浮动边缘
说一说Vuex是什么,每个属性是干嘛的,如何使用 ?
得分点 state、mutations、getters、actions、module、store.commit、store.dispatch 标准回答 Vuex是集中管理项目公共数据的。Vuex 有state、mutations 、getters、actions、module属性。 state 属性用来存储公共管理的数据。 mutations 属性定义改变state中数据的方法, 注意:不要在mutation中的方法中写异步方法ajax,那样数据就不可跟踪了 。 getters 属性可以认为是定义 store 的计算属性。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。 action属性类似于 mutation,不同在于:Action 提交的是 mutation,而不是直接变更状态。Action 可以包含任意异步操作。 moudle属性是将store分割成模块。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块,从上至下进行同样方式的分割 使用方法: state :直接以对象方式添加属性 mutations :通过store.commit
调用 action:通过 store.dispatch
方法触发 getters:直接通过store.getters.调用 加分回答 可以使用mapState、mapMutations、mapAction、mapGetters一次性获取每个属性下对应的多个方法。 VueX在大型项目中比较常用,非关系组件传递数据比较方便。
说一说JavaScript有几种方法判断变量的类型?
1、typeof 用来判断基本数据类型,对于引用数据类型function 返回function其他 都返回Object. 2、instanceof 主要用于区分引用数据类型,检测方法是检测的类型再当前实例的原型链上,用其检测出来的结果都是true,不太适合检测简单数据类型的检测,检测过程繁琐且对简单数据类型中的undefined null symbol检测不出来。 3、constructor:用于检测引用数据类型,检测方法是获取实例的构造函数判断和某个类是否相同,如哦相同就说明该数据是符合那个数据类型的,这种方法不会把原型链上的其他类也加入进来,避免了原型链的干扰。 4、Object.prototype.toString.call():适用于所有类型的检测,检测方法是Object.prototype.toString.call(数据)返回的是该数据类型的字符串。最精准的就是这个方法
说一说样式优先级的规则是什么?
!important > 内联样式(style) > ID选择器(id) > 类选择器(class) > 标签选择器
说一说Vue2.0 双向绑定的原理与缺陷?
得分点 : Object.defineProperty、getter、setter
标准回答 : Vue响应式指的是:组件的data发生变化,立刻触发试图的更新 原理: Vue 采用数据劫持结合发布者-订阅者模式的方式来实现数据的响应式,通过Object.defineProperty来劫持数据的setter,getter,在数据变动时发布消息给订阅者,订阅者收到消息后进行相应的处理。 通过原生js提供的监听数据的API,当数据发生变化的时候,在回调函数中修改dom 核心API:Object.defineProperty Object.defineProperty API的使用 作用: 用来定义对象属性 特点: 默认情况下定义的数据的属性不能修改 描述属性和存取属性不能同时使用,使用会报错 响应式原理: 获取属性值会触发getter方法 设置属性值会触发setter方法 在setter方法中调用修改dom的方法 加分回答 Object.defineProperty的缺点 1. 一次性递归到底开销很大,如果数据很大,大量的递归导致调用栈溢出 2. 不能监听对象的新增属性和删除属性 3. 无法正确的监听数组的方法,当监听的下标对应的数据发生改变时
说一说数组去重都有哪些方法?
得分点 : 对象属性、new Set() 、indexOf、hasOwnProperty、reduce+includes、filter
标准回答 : 第一种方法:利用对象属性key排除重复项:遍历数组,每次判断对象中是否存在该属性,不存在就存储在新数组中,并且把数组元素作为key,设置一个值,存储在对象中,最后返回新数组。这个方法的优点是效率较高,缺点是占用了较多空间,使用的额外空间有一个查询对象和一个新的数组 第二种方法:利用Set类型数据无重复项:new 一个 Set,参数为需要去重的数组,Set 会自动删除重复的元素,再将 Set 转为数组返回。这个方法的优点是效率更高,代码简单,思路清晰,缺点是可能会有兼容性问题 第三种方法:filter+indexof 去重:这个方法和第一种方法类似,利用 Array 自带的 filter 方法,返回 arr.indexOf(num) 等于 index 的num。原理就是 indexOf 会返回最先找到的数字的索引,假设数组是 [1, 1],在对第二个1使用 indexOf 方法时,返回的是第一个1的索引0。这个方法的优点是可以在去重的时候插入对元素的操作,可拓展性强。 第四种方法:这个方法比较巧妙,从头遍历数组,如果元素在前面出现过,则将当前元素挪到最后面,继续遍历,直到遍历完所有元素,之后将那些被挪到后面的元素抛弃。这个方法因为是直接操作数组,占用内存较少。 第五种方法:reduce +includes去重:这个方法就是利用reduce遍历和传入一个空数组作为去重后的新数组,然后内部判断新数组中是否存在当前遍历的元素,不存在就插入到新数组中。这种方法时间消耗多,内存空间也有额外占用。 方法还有很多,常用的、了解的这些就可以 加分回答 以上五个方法中,在数据低于10000条的时候没有明显的差别,高于10000条,第一种和第二种的时间消耗最少,后面三种时间消耗依次增加,由于第一种内存空间消耗比较多,且现在很多项目不再考虑低版本浏览器的兼容性问题,所以建议使用第二种去重方法,简洁方便。
说一说null 和 undefined 的区别,如何让一个属性变为null
得分点 : 操作的变量没有被赋值、全局对象的一个属性、函数没有return返回值、值 null
特指对象的值未设置 undefined == null、undefined !== null
标准回答: undefind 是全局对象的一个属性,当一个变量没有被赋值或者一个函数没有返回值或者某个对象不存在某个属性却去访问或者函数定义了形参但没有传递实参,这时候都是undefined。undefined通过typeof判断类型是’undefined’。undefined == undefined undefined === undefined 。 null代表对象的值未设置,相当于一个对象没有设置指针地址就是null。null通过typeof判断类型是’object’。null === null null == null null == undefined null !== undefined undefined 表示一个变量初始状态值,而 null 则表示一个变量被人为的设置为空对象,而不是原始状态。在实际使用过程中,不需要对一个变量显式的赋值 undefined,当需要释放一个对象时,直接赋值为 null 即可。 让一个变量为null,直接给该变量赋值为null即可。 加分回答 null 其实属于自己的类型 Null,而不属于Object类型,typeof 之所以会判定为 Object 类型,是因为JavaScript 数据类型在底层都是以二进制的形式表示的,二进制的前三位为 0 会被 typeof 判断为对象类型,而 null 的二进制位恰好都是 0 ,因此,null 被误判断为 Object 类型。 对象被赋值了null 以后,对象对应的堆内存中的值就是游离状态了,GC 会择机回收该值并释放内存。因此,需要释放某个对象,就将变量设置为 null,即表示该对象已经被清空,目前无效状态。
说一下浮动 和 清除浮动
包括左浮动,右浮动 设置浮动会脱离文档流,当父元素不设高度需要子元素来撑开,而子元素设置浮动就会导致父元素的高度塌陷 解决塌陷: 1.给父元素设置高度 2.给父元素设置overflow:hidden 3.在下方创建一个空白div,添加样式clear:both 4.父级添加after伪类 father::after{ content:‘’; display:table; clear:both }
说一说es6中箭头函数?
得分点: 没有this、this是从外部获取、不能使用new、没有arguments、没有原型和super
标准回答: 箭头函数相当于匿名函数,简化了函数定义。箭头函数有两种写法,当函数体是单条语句的时候可以省略{}和return。另一种是包含多条语句,不可以省略{}和return。 箭头函数最大的特点就是没有this,所以this是从外部获取,就是继承外部的执行上下文中的this,由于没有this关键字所以箭头函数也不能作为构造函数, 同时通过 call()
或 apply()
方法调用一个函数时,只能传递参数(不能绑定this),第一个参数会被忽略。箭头函数也没有原型和super。不能使用yield关键字,因此箭头函数不能用作 Generator 函数。不能返回直接对象字面量。 加分回答 箭头函数的不适用场景: -定义对象上的方法 当调用 dog.jumps
时,lives
并没有递减。因为 this
没有绑定值,而继承父级作用域。 var dog = { lives: 20, jumps: () => { this.lives–; } } -不适合做事件处理程序 此时触发点击事件,this不是button,无法进行class切换 var button = document.querySelector(‘button’); button.addEventListener(‘click’, () => { this.classList.toggle(‘on’); }); 箭头函数函数适用场景: -简单的函数表达式,内部没有this引用,没有递归、事件绑定、解绑定,适用于map、filter等方法中,写法简洁 var arr = [1,2,3]; var newArr = arr.map((num)=>num*num) -内层函数表达式,需要调用this,且this应与外层函数一致时 let group = { title: “Our Group”, students: [“John”, “Pete”, “Alice”], showList() { this.students.forEach( student => alert(this.title + ': ’ + student) ); } }; group.showList();
简单来说: 没有自己的this,即不能作为构造函数,里面的this是执行上下文的this 2. 不能被new 3. 没有arguments对象 4. 不能作为generator函数,不能用yied命令 5. 没有prototype属性
说一说call apply bind的作用和区别?
首先,call apply bind三个方法都可以用来改变函数的this指向,具体区别如下:
1、fn.call (newThis,params) call函数的第一个参数是this的新指向,后面依次传入函数fn要用到的参数。会立即执行fn函数
。
2、fn.apply (newThis,paramsArr) apply函数的第一个参数是this的新指向,第二个参数是fn要用到的参数数组,会立即执行fn函数
。
3、fn.bind (newThis,params) bind函数的第一个参数是this的新指向,后面的参数可以直接传递,也可以按数组的形式传入。 不会立即执行fn函数
,且只能改变一次fn函数的指向
,后续再用bind更改无效。返回的是已经更改this指向的新fn
说一说HTML语义化?
- 对于开发者而言:语义化标签有着更好的页面结构,利于个人的代码编写。
- 对于用户而言,当网络卡顿时有良好的页面结构,有利于增加用户的体验。
- 对于爬虫来说,有利于搜索引擎的SEO优化,利于网站有更靠前的排名。
- 对于团队来讲,有利于代码的开发和后期的维护。
说一说this指向(普通函数、箭头函数)?
- 普通函数this指向window
- 构造函数的this指向该构造函数
- 箭头函数this指向它上一级作用域中的this
说一说CSS尺寸设置的单位
- px:绝对像素
- rem:相对于根元素像素
- em:相对于浏览器设定的元素像素
- vw:视口宽度
- vh:视口高度
说几个未知宽高元素水平垂直居中方法
未知宽高元素水平垂直都居中的实现方法:
- 设置元素相对父级定位
position:absolute;left:50%;right:50%
,让自身平移自身高度50%transform: translate(-50%,-50%);
,这种方式兼容性好,被广泛使用的一种方式 - 设置元素的父级为弹性盒子
display:flex
,设置父级和盒子内部子元素水平垂直都居中justify-content:center; align-items:center
,这种方式代码简洁,但是兼容性ie 11以上支持,由于目前ie版本都已经很高,很多网站现在也使用这种方式实现水平垂直居中 - 设置元素的父级为网格元素
display: grid
,设置父级和盒子内部子元素水平垂直都居中justify-content:center; align-items:center
,这种方式代码简介,但是兼容性ie 10以上支持 - 设置元素的父级为表格元素
display: table-cell
,其内部元素水平垂直都居中text-align: center;vertical-align: middle;
,设置子元素为行内块display: inline-block;
,这种方式兼容性较好
说一说JS变量提升?
得分点 : Var声明的变量声明提升、函数声明提升、let和const变量不提升
标准回答 : 变量提升是指JS的变量和函数声明会在代码编译期,提升到代码的最前面。 变量提升成立的前提是使用Var关键字进行声明的变量,并且变量提升的时候只有声明被提升,赋值并不会被提升,同时函数的声明提升会比变量的提升优先。 变量提升的结果,可以在变量初始化之前访问该变量,返回的是undefined。在函数声明前可以调用该函数。 加分回答 使用let和const声明的变量是创建提升,形成暂时性死区,在初始化之前访问let和const创建的变量会报错。
说一说 HashRouter 和 HistoryRouter的区别和原理?
1.history和hash都是利用浏览器的2种特性实现前端路由,history是利用浏览历史记录栈的API实现,hash是监听location hash值变化事件来实现。 2.history的url没有#号,hash有#号。 3.相同的url,history会触发添加到浏览器历史记录栈中,hash不会触发,history需要后端配合,如果后端不配合刷新页面会出现404,hash不需要。 hashRouter原理:通过window.onhashchange获取url中hash值。 historyRouter原理:通过history.pushState,使用它做页面跳转不会触发页面刷新,使用window.onpopstate监听浏览器的前进和后退。
说一说事件循环Event loop,宏任务与微任务?
js是单线程的,主线程在执行时会不断循环往复的从同步队列中读取任务,执行任务,当同步队列执行完毕后再从异步队列中依次执行。宏任务与微任务都属于异步任务,再执行上微任务的优先级高于宏任务,因此每一次都会先执行完微任务在执行宏任务。宏任务有定时器,Dom事件,ajax事件,微任务有:promise的回调、MutationObserver 的回调 ,process.nextTick
说一下Diff算法?
得分点 patch、patchVnode、updateChildren、vue优化时间复杂度为O(n) 标准回答 Diff算法比较过程 第一步:patch函数中对新老节点进行比较 如果新节点不存在就销毁老节点 如果老节点不存在,直接创建新的节点 当两个节点是相同节点的时候,进入 patctVnode 的过程,比较两个节点的内部 第二步:patchVnode函数比较两个虚拟节点内部 如果两个虚拟节点完全相同,返回 当前vnode 的children 不是textNode,再分成三种情况 - 有新children,没有旧children,创建新的 - 没有新children,有旧children,删除旧的 - 新children、旧children都有,执行updateChildren
比较children的差异,这里就是diff算法的核心 当前vnode 的children 是textNode,直接更新text 第三步:updateChildren函数子节点进行比较 - 第一步 头头比较。若相似,旧头新头指针后移(即 oldStartIdx++
&& newStartIdx++
),真实dom不变,进入下一次循环;不相似,进入第二步。 - 第二步 尾尾比较。若相似,旧尾新尾指针前移(即 oldEndIdx--
&& newEndIdx--
),真实dom不变,进入下一次循环;不相似,进入第三步。 - 第三步 头尾比较。若相似,旧头指针后移,新尾指针前移(即 oldStartIdx++
&& newEndIdx--
),未确认dom序列中的头移到尾,进入下一次循环;不相似,进入第四步。 - 第四步 尾头比较。若相似,旧尾指针前移,新头指针后移(即 oldEndIdx--
&& newStartIdx++
),未确认dom序列中的尾移到头,进入下一次循环;不相似,进入第五步。 - 第五步 若节点有key且在旧子节点数组中找到sameVnode(tag和key都一致),则将其dom移动到当前真实dom序列的头部,新头指针后移(即 newStartIdx++
);否则,vnode对应的dom(vnode[newStartIdx].elm
)插入当前真实dom序列的头部,新头指针后移(即 newStartIdx++
)。 - 但结束循环后,有两种情况需要考虑: - 新的字节点数组(newCh)被遍历完(newStartIdx > newEndIdx
)。那就需要把多余的旧dom(oldStartIdx -> oldEndIdx
)都删除,上述例子中就是c,d
; - 新的字节点数组(oldCh)被遍历完(oldStartIdx > oldEndIdx
)。那就需要把多余的新dom(newStartIdx -> newEndIdx
)都添加。
说一说三栏布局的实现方案
三栏布局:
左右两栏宽度固定,中间自适应布局。中间都是需要margin的 方法一:利用绝对定位right{position:absolution,width:200px,height:200px,top:0,right:0},left{position:absolution,width:200px,height:200px}center{ margin-left: 100px; margin-right: 100px;} 方法二:利用flex布局,将center设为flex:1,两边固定大小就好。 方法三:利用float。.left { float: left; width: 100px; height: 100px; background: tomato; } .right { float: right; width: 200px; height: 100px; background: gold; } .center { height: 100px; margin-left: 100px; margin-right: 200px; background: lightgreen; } 方法四:圣杯布局 将中间左右设置左浮动,中间宽度100%,左magin-left:-100%,右magin-left:200px(右盒子宽度为200px),发现左右有挡住中间内容,则设置整个大盒子的padding:0 200px,给左右两个盒子加一个定位,加了定位之后左右两个盒子就可以设置left和right值。左:position:relative,left:-200px,右:position:relative,right:-200px。圣杯布局
的实现方案:三个元素放在同一个父级元素中,代表中间盒子的元素放在最前面,父级盒子设置左右padding
,三个盒子全部浮动,设置中间盒子宽度100%,左右盒子设置固定宽度,设置左边盒子左边距-100%同时相对自身定位,右边平移自身宽度,右边盒子设置右边距-自身宽度,最后设置父级盒子清除浮动,否则父级盒子的高度无法被撑开 解释:magin-left:-100%这个百分比是以父元素内容长度的百分比,圣杯布局的左的父元素contain,contain里包含者着中左右。双飞翼布局:contain只包含center 方法五:双飞翼布局 双飞翼布局: - 优点:不会像圣杯布局那样变形,CSS样式代码更简洁 - 缺点:多加了一层dom节点 双飞翼布局的实现方案:三个盒子对应三个元素,其中中间盒子套了两层,中间盒子内部盒子设置margin
,三个盒子全部浮动,设置中间盒子宽度100%,左右盒子设置固定宽度,设置左边盒子左边距-100%,右边盒子设置右边距-自身宽度,最后设置父级盒子清除浮动,否则父级盒子的高度无法被撑开
说一下浏览器垃圾回收机制?
有两种机制:1、标记清除:对所有活动对象进行标记,清除阶段会将没有标记的对象清除;标记整理算法:标记结束后,算法将活动对象压入内存一端,则需要清理的对象在边界,直接被清理掉就行。(效率低) 2、引用计数:将对象是否不再需要简化定义为有没有其他对象引用它,如果没有引用指向这个对象,则会被垃圾回收机制回收。(内存空间不连续)
说一说 vue 的 keep-alive ?
1、keep-alive是vue的内置组件,能在组件切换过程中将状态保留在内存中,相当于缓存,防止DOM的重复渲染;2、keep-alive有三个属性:include(只有名字匹配的才会被缓存)、exclude(任何名字匹配的都不会被缓存)、max(最多可以缓存多少个组件)。3、在路由router的中:相应组件下规定meta属性,定义keep-alive:true;4、可以结合Vue组件实例加载顺序讲解,VNode->实例化->_updata->真实Node,在实例化的时候会判断该组件是否被keep-alive保存过,是的话则直接拿其中的DOM进行渲染。
XSS攻击是什么?
得分点 XSS是跨站脚本攻击、向目标网站插入恶意代码、大量用户访问网站时运行恶意脚本获取信息 标准回答 XSS是跨站脚本攻击(Cross Site Scripting),不写为CSS是为了避免和层叠样式表(Cascading Style Sheets)的缩写混淆,所以将跨站脚本攻击写为XSS。攻击者可以通过向Web页面里面插入script代码,当用户浏览这个页面时,就会运行被插入的script代码,达到攻击者的目的。XSS的危害一般是泄露用户的登录信息cookie,攻击者可以通过cookie绕过登录步骤直接进入站点。XSS的分类分为反射型和存储型。反射型就是临时通过url访问网站,网站服务端将恶意代码从url中取出,拼接在HTML中返回给浏览器,用户就会执行恶意代码。存储型就是将恶意代码以留言的形式保存在服务器数据库,任何访问网站的人都会受到攻击。预防XSS攻击的方案基本是对数据进行严格的输出编码,比如HTML元素的编码,JavaScript编码,css编码,url编码等等。 加分回答 XSS的危害: - 获取cookie:网站中的登录一般都是用cookie作为某个用户的身份证明,这是服务器端返回的一串字符。如果cookie被攻击者拿到,那么就可以绕过密码登录。当空间、论坛如果可以被插入script代码,那么进入空间或者论坛的人的账号就可以轻易被攻击者获取。 - 恶意跳转:直接在页面中插入window.location.href进行跳转。 XSS的分类: - 反射型XSS(非持久型XSS):通过URL参数直接注入 - 存储型XSS(持久型XSS):存储到数据库后读取时注入 XSS的预防: - 浏览器的防御和“X-XSS-Protection”有关,默认值为1,即默认打开XSS防御,可以防御反射型的XSS,不过作用有限,只能防御注入到HTML的节点内容或属性的XSS,例如URL参数中包含script标签。不建议只依赖此防御手段。 - 防御HTML节点内容,通过转义<为<以及>为>来实现防御HTML节点内容。 - 预防HTML属性,通过转义"->&quto来实现防御,一般不转义空格,但是这要求属性必须带引号。 - 预防JavaScript代码,通过将数据进行JSON序列化。 - 防御富文本是比较复杂的工程,因为富文本可以包含HTML和script,这些难以预测与防御,建议是通过白名单的方式来过滤允许的HTML标签和标签的属性来进行防御,大概的实现方式是: - 将HTML代码段转成树级结构的数据 - 遍历树的每一个节点,过滤节点的类型和属性,或进行特殊处理 - 处理完成后,将树级结构转化成HTML代码 - 开启浏览器XSS防御:Http Only cookie,禁止 JavaScript 读取某些敏感 Cookie,攻击者完成XSS注入后也无法窃取此 Cookie。
说一说js继承的方法和优缺点?
- 一、原型链继承 - 缺点: - 1.引用类型的属性被所有实例共享 - 2.在创建 Child 的实例时,不能向 Parent 传参 - 二、借用构造函数(经典继承) - 优点: - 1.避免了引用类型的属性被所有实例共享 - 2.可以在 Child 中向 Parent 传参 - 缺点: - 1.方法都在构造函数中定义,每次创建实例都会创建一遍方法。 - 三、组合继承 - 优点: - 1.融合原型链继承和构造函数的优点,是 JavaScript 中最常用的继承模式。 - 四、原型式继承 - 缺点: - 1.包含引用类型的属性值始终都会共享相应的值,这点跟原型链继承一样。 - 五、寄生式继承 - 缺点: - 1.跟借用构造函数模式一样,每次创建对象都会创建一遍方法。 - 六、寄生组合式继承 - 优点: - 1.这种方式的高效率体现它只调用了一次 Parent 构造函数,并且因此避免了在 Parent.prototype 上面创建不必要的、多余的属性。 - 2.与此同时,原型链还能保持不变; - 3.因此,还能够正常使用 instanceof 和 isPrototypeOf。
说一说defer和async区别?
得分点 加载JS文档和渲染文档可以同时进行、JS代码立即执行、JS代码不立即执行、渲染引擎和JS引擎互斥 标准回答 浏览器会立即加载JS文件并执行指定的脚本,“立即”指的是在渲染该 script 标签之下的文档元素之前,也就是说不等待后续载入的文档元素,读到就加载并执行
加上async属性,加载JS文档和渲染文档可以同时进行(异步),当JS加载完成,JS代码立即执行,会阻塞HTML渲染。 `` 加上defer,加载后续文档元素的过程将和 script.js 的加载并行进行(异步),当HTML渲染完成,才会执行JS代码。 加分回答 渲染阻塞的原因: 由于 JavaScript 是可操纵 DOM 的,如果在修改这些元素属性同时渲染界面(即 JavaScript 线程和 UI 线程同时运行),那么渲染线程前后获得的元素数据就可能不一致了。因此为了防止渲染出现不可预期的结果,浏览器设置 GUI 渲染线程与 JavaScript 引擎为互斥的关系。当浏览器在执行 JavaScript 程序的时候,GUI 渲染线程会被保存在一个队列中,直到 JS 程序执行完成,才会接着执行。如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉
说一下浏览器如何渲染页面的?
浏览器拿到HTML,先将HTML转换成dom树,再将CSS样式转换成stylesheet,根据dom树和stylesheet创建布局树,对布局树进行分层,为每个图层生成绘制列表,再将图层分成图块,紧接着光栅化将图块转换成位图,最后合成绘制生成页面。 加分回答 分层的目的:避免整个页面渲染,把页面分成多个图层,尤其是动画的时候,把动画独立出一个图层,渲染时只渲染该图层就ok,transform,z-index等,浏览器会自动优化生成图层 光栅化目的:页面如果很长但是可视区很小,避免渲染非可视区的样式造成资源浪费,所以将每个图层又划分成多个小个子,当前只渲染可视区附近区域
说一说computed和watch的区别?
得分点 : computed值有缓存、触发条件是依赖值发生更改、 watch无缓存支持异步、监听数据变化
标准回答: computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值; watch: 更多的是观察的作用,支持异步,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作; 加分回答 computed应用场景:需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算; watch应用场景:需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
说一说 Vue 中 $nextTick 作用与原理?
得分点: 异步渲染、获取DOM、Promise
标准回答 : Vue 在更新 DOM 时是异步执行的,在修改数据后,视图不会立刻更新,而是等同一事件循环中的所有数据变化完成之后,再统一进行视图更新。所以修改完数据,立即在方法中获取DOM,获取的仍然是未修改的DOM。 $nextTick的作用是:该方法中的代码会在当前渲染完成后执行,就解决了异步渲染获取不到更新后DOM的问题了。
n
e
x
t
T
i
c
k
的
原
理
:
nextTick的原理:
nextTick的原理:nextTick本质是返回一个Promise 加分回答 应用场景:在钩子函数created()里面想要获取操作Dom,把操作DOM的方法放在$nextTick中
说一说new会发生什么?
看下new的实现代码就知道有以下四步:1. 创建一个新对象 2. 将新对象的__proto__(原型)指向构造函数的prototype(原型对象)3. 构造函数绑定新对象的this并执行返回结果 4. 判断返回结果是否为null,如果为null,返回新对象,否则直接返回执行结果。
说一下token 能放在cookie中吗?
可以, 1. 客户端使用用户名跟密码请求登录 2. 服务端收到请求,去验证用户名与密码 3. 验证成功后,服务端签发一个 token ,并把它发送给客户端 4. 客户端接收 token 以后会把它存储起来,比如放在 cookie 里或者 localStorage 里 5. 客户端每次发送请求时都需要带着服务端签发的 token(把 token 放到 HTTP 的 Header 里) 6. 服务端收到请求后,需要验证请求里带有的 token ,如验证成功则返回对应的数据
说一下浏览器输入URL发生了什么?
得分点 : DNS解析、TCP握手、HTTP缓存、重定向、服务器状态码、渲染引擎和JS引擎互斥、渲染过程、浏览器进程、网络进程、渲染进程
标准回答 : 输入地址,浏览器查找域名的 IP 地址。 浏览器向 该 IP 地址的web 服务器发送一个 HTTP 请求,在发送请求之前浏览器和服务器建立TCP的三次握手,判断是否是HTTP缓存,如果是强制缓存且在有效期内,不再向服务器发请求,如果是HTTP协商缓存向后端发送请求且和后端服务器对比,在有效期内,服务器返回304,直接从浏览器获取数据,如果不在有效期内服务器返回200,返回新数据。 请求发送出去服务器返回重定向,浏览器再按照重定向的地址重新发送请求。 如果请求的参数有问题,服务器端返回404,如果服务器端挂了返回500。 如果有数据一切正常,当浏览器拿到服务器的数据之后,开始渲染页面同时获取HTML页面中图片、音频、视频、CSS、JS,在这期间获取到JS文件之后,会直接执行JS代码,阻塞浏览器渲染,因为渲染引擎和JS引擎互斥,不能同时工作,所以通常把Script标签放在body标签的底部。 渲染过程就是先将HTML转换成dom树,再将CSS样式转换成stylesheet,根据dom树和stylesheet创建布局树,对布局树进行分层,为每个图层生成绘制列表,再将图层分成图块,紧接着光栅化将图块转换成位图,最后合成绘制生成页面。
说一说组件通信的方式?
- 父子组件通信常用props和 e m i t 还 有 emit还有 emit还有refs;
- 兄弟组件通信常用定义的公共事件bus的 o n 、 on、 on、emit;
- 祖先和子孙组件通信常用 a t t r s 和 attrs和 attrs和listener、provide和inject;
- 复杂通信常用vuex
说一说 v-if 和 v-show区别?
相同点
:都是控制元素隐藏和显示的指令.
不同点
: v-show控制的无论是true还是false都会在DOM树中显示,相当于通过display:none控制元素隐藏, v-if控制的元素无论是true还是false都不会在dom树显示,v-show适合在切换频繁显示/隐藏的元素上,v-if不适合使用在切换频繁的元素上也不适合在元素内容很多上
说一说盒模型?
得分点 : 标准盒模型、怪异盒模型、box-sizing:border-box
、盒模型大小
标准回答 : CSS盒模型定义了盒的每个部分包含 margin, border, padding, content 。根据盒子大小的计算方式不同盒模型分成了两种,标准盒模型和怪异盒模型。 标准模型,给盒设置 width
和 height
,实际设置的是 content box。padding
和 border
再加上设置的宽高一起决定整个盒子的大小。 怪异盒模型,给盒设置 width
和 height
,包含了padding
和border
,设置的 width
和 height
就是盒子实际的大小 默认情况下,盒模型都是标准盒模型 设置标准盒模型:box-sizing:content-box
设置怪异盒模型:box-sizing:border-box
说一说伪数组和数组的区别?
得分点 : 类型是object、不能使用数组方法、可以获取长度、可以使用for in遍历
标准回答: 伪数组它的类型不是Array,而是Object,而数组类型是Array。可以使用的length属性查看长度,也可以使用[index]获取某个元素,但是不能使用数组的其他方法,也不能改变长度,遍历使用for in方法。 伪数组的常见场景: -函数的参数arguments -原生js获取DOM:document.querySelector(‘div’) 等 -jquery获取DOM:$(“div”)等 加分回答 伪数组转换成真数组方法 -Array.prototype.slice.call(伪数组) -[].slice.call(伪数组) -Array.from(伪数组) 转换后的数组长度由 length
属性决定。索引不连续时转换结果是连续的,会自动补位。
说一说如何实现可过期的localstorage数据?
- 一种是惰性删除:惰性删除是指获取数据的时候,拿到存储的时间和当前时间做对比,如果超过过期时间就清除Cookie。
- 另一种是定时删除:每隔一段时间执行一次删除操作,并通过限制删除操作执行的次数和频率,来减少删除操作对CPU的长期占用。 LocalStorage清空应用场景:token存储在LocalStorage中,要清空
说一说创建ajax过程?
得分点 : new XMLHttpRequest()、设置请求参数open()、发送请求request.send()、响应request.onreadystatechange
标准回答 : 创建ajax过程: 1. 创建XHR对象:new XMLHttpRequest() 2. 设置请求参数:request.open(Method, 服务器接口地址); 3. 发送请求: request.send(),如果是get请求不需要参数,post请求需要参数request.send(data) 4. 监听请求成功后的状态变化:根据状态码进行相应的处理。 XHR.onreadystatechange = function () { if (XHR.readyState == 4 && XHR.status == 200) { console.log(XHR.responseText); // 主动释放,JS本身也会回收的 XHR = null; } }; 加分回答 POST请求需要设置请求头 readyState值说明 0:初始化,XHR对象已经创建,还未执行open 1:载入,已经调用open方法,但是还没发送请求 2:载入完成,请求已经发送完成 3:交互,可以接收到部分数据 4:数据全部返回 status值说明 200:成功 404:没有发现文件、查询或URl 500:服务器产生内部错误
说一下有什么方法可以保持前后端实时通信?
得分点 轮询、长轮询、 iframe流、WebSocket、SSE 标准回答 保持前后端实时通信的方法有以下几种: 轮询是客户端和服务器之间会一直进行连接,每隔一段时间就询问一次。其缺点也很明显:连接数会很多,一个接受,一个发送。而且每次发送请求都会有Http的Header,会很耗流量,也会消耗CPU的利用率。优点就是实现简单,无需做过多的更改。缺点是轮询的间隔过长,会导致用户不能及时接收到更新的数据;轮询的间隔过短,会导致查询请求过多,增加服务器端的负担 长轮询是对轮询的改进版,客户端发送HTTP给服务器之后,如果没有新消息,就一直等待。有新消息,才会返回给客户端。在某种程度上减小了网络带宽和CPU利用率等问题。由于http数据包的头部数据量往往很大(通常有400多个字节),但是真正被服务器需要的数据却很少(有时只有10个字节左右),这样的数据包在网络上周期性的传输,难免对网络带宽是一种浪费。优点是做了优化,有较好的时效性。缺点是保持连接会消耗资源; 服务器没有返回有效数据,程序超时。 iframe流方式是在页面中插入一个隐藏的iframe,利用其src属性在服务器和客户端之间创建一条长连接,服务器向iframe传输数据(通常是HTML,内有负责插入信息的javascript),来实时更新页面。优点是消息能够实时到达;浏览器兼容好。缺点是服务器维护一个长连接会增加开销;IE、chrome、Firefox会显示加载没有完成,图标会不停旋转。 WebSocket是类似Socket的TCP长连接的通讯模式,一旦WebSocket连接建立后,后续数据都以帧序列的形式传输。在客户端断开WebSocket连接或Server端断掉连接前,不需要客户端和服务端重新发起连接请求。在海量并发和客户端与服务器交互负载流量大的情况下,极大的节省了网络带宽资源的消耗,有明显的性能优势,且客户端发送和接受消息是在同一个持久连接上发起,实时性优势明显。缺点是浏览器支持程度不一致,不支持断开重连。 SSE(Server-Sent Event)是建立在浏览器与服务器之间的通信渠道,然后服务器向浏览器推送信息。SSE 是单向通道,只能服务器向浏览器发送,因为 streaming 本质上就是下载。 优点是SSE 使用 HTTP 协议,现有的服务器软件都支持。SSE 属于轻量级,使用简单;SSE 默认支持断线重连; 加分回答 轮询适用于:小型应用,实时性不高 长轮询适用于:一些早期的对及时性有一些要求的应用:web IM 聊天 iframe适用于:客服通信等 WebSocket适用于:微信、网络互动游戏等 SSE适用于:金融股票数据、看板等
说一下重绘、重排区别如何避免?
得分点 改变元素尺寸、重新计算布局树、重排必定重绘、重绘避开了重建布局树和分层、GPU加速、脱离文档流、样式集中改变 标准回答 重排 :当DOM的变化影响了元素的几何信息(元素的的位置和尺寸大小),浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫做重排。 重绘:当一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来的过程,所以重绘跳过了创建布局树和分层的阶段。 重排需要重新计算布局树,重绘不需要,重排必定发生重绘,但是涉及到重绘不一定要重排。涉及到重排对性能的消耗更多一些。 触发重排的方法: -页面初始渲染,这是开销最大的一次重排 -添加/删除可见的DOM元素 -改变元素位置 -改变元素尺寸,比如边距、填充、边框、宽度和高度等 -改变元素内容,比如文字数量,图片大小等 -改变元素字体大小 -改变浏览器窗口尺寸,比如resize事件发生时 -激活CSS伪类(例如::hover
) -设置 style 属性的值,因为通过设置style属性改变结点样式的话,每一次设置都会触发一次reflow -查询某些属性或调用某些计算方法:offsetWidth、offsetHeight等 避免重排的方式 -样式集中改变 -使用 absolute 或 fixed 脱离文档流 -使用GPU加速:transform 加分回答 GPU的过程是以下这几步 : 1. 获取DOM并将其分割成多个层(renderLayer) 2. 将每个层栅格化,并独立的绘制进位图中 3. 将这些位图作为纹理上传至GPU 4. 复合多个层来生成最终的屏幕图像(最后的layer) 开启了GPU加速的元素被独立出来,不会再影响其他dom的布局,因为它改变之后,只是相当于被贴上了页面。
说一说 Vue 列表为什么加 key?
得分点 性能优化、diff算法节点比对、key不能是index 标准回答 为了性能优化 因为vue是虚拟DOM,更新DOM时用diff算法对节点进行一一比对,比如有很多li元素,要在某个位置插入一个li元素,但没有给li上加key,那么在进行运算的时候,就会将所有li元素重新渲染一遍,但是如果有key,那么它就会按照key一一比对li元素,只需要创建新的li元素,插入即可,不需要对其他元素进行修改和重新渲染。 加分回答 key也不能是li元素的index,因为假设我们给数组前插入一个新元素,它的下标是0,那么和原来的第一个元素重复了,整个数组的key都发生了改变,这样就跟没有key的情况一样了
说一说vue-router 实现懒加载的方法?
目前有三种方法:
1、利用Vue的异步组件和Webpack: const Home = require =>{ require([‘…/components/home.vue’],()=>{ resolve(require(‘…/components/home.vue’)); }) };
2、AMD在1的基础上 const Home = require =>{ require([‘…/components/home.vue’],resolved); };
3、ES6利用Webpack的chunk【推荐使用】 const Home = () =>import(‘…/components/home.vue’)
说一说前端性能优化手段?
得分点 图片压缩和文件压缩、雪碧图/精灵图、节流防抖、HTTP缓存、本地缓存、Vue的keep-alive缓存、ssr服务器端渲染、懒加载、对dom查询进行缓存、将dom操作合并 标准回答 前端性能优化分为两类,一类是文件加载更快,另一类是文件渲染更快。 加载更快的方法: 让传输的数据包更小(压缩文件/图片):图片压缩和文件压缩 减少网络请求的次数:雪碧图/精灵图、节流防抖 减少渲染的次数:缓存(HTTP缓存、本地缓存、Vue的keep-alive缓存等) 渲染更快的方法: 提前渲染:ssr服务器端渲染 避免渲染阻塞:CSS放在HTML的head中 JS放在HTML的body底部 避免无用渲染:懒加载 减少渲染次数:对dom查询进行缓存、将dom操作合并、使用减少重排的标签 加分回答 雪碧图的应用场景一般是项目中不常更换的一些固定图标组合在一起,比如logo、搜索图标、切换图标等。 电商项目中最常用到的懒加载,一般在查看商品展示的时候通常下拉加载更多,因为商品数据太多,一次性请求过来数据太大且渲染的时间太长。
说一说性能优化有哪些性能指标,如何量化?
得分点 加载速度、第一个请求响应时间、页面加载时间、交互动作的反馈时间、帧率FPS、异步请求完成时间 Lighthouse、Throttling 、Performance、Network、WebPageTest 标准回答 常用的性能优化指标 - Speed Index(lighthouse,速度指数) - TTFB(Network,第一个请求响应时间) - 页面加载时间 - 首次渲染 - 交互动作的反馈时间 - 帧率FPS(动画 ctrl+shift+p) - 异步请求完成时间 使用性能测量工具进行量化 - Chrome DevTools - 开发调试、性能评测 - Audit(Lighthouse) - Throttling 调整网络吞吐 - Performance 性能分析 - Network 网络加载分析 - Lighthouse - 网站整体质量评估 - 还可以提出优化建议 - WebPageTest - 测试多地点(球各地的用户访问你的网站的性能情况) - 全面性能报告(first view,repeat view,waterfall chart 等等) - WebPageTest 还可以进行本地安装,让你的应用在还没上线的时候就可以测试。 加分回答 常用的性能测量API DNS 解析耗时: domnLookupEnd - domnLookupStart TCP 连接耗时: connectEnd - connectStart SSL 安全连接耗时: connectEnd - secureConnectionStart 网络请求耗时 (TTFB): responseStart - requestStart 数据传输耗时: responseEnd - responseStart DOM 解析耗时: domInteractive - responseEnd 资源加载耗时: loadEventStart - domContentLoadedEventEnd First Byte时间: responseStart - domnLookupStart 白屏时间: responseEnd - fetchStart 首次可交互时间: domInteractive - fetchStart DOM Ready 时间: domContentLoadEventEnd - fetchStart 页面完全加载时间: loadEventStart - fetchStart http 头部大小: transferSize - encodedBodySize 重定向次数:performance.navigation.redirectCount 重定向耗时: redirectEnd - redirectStart
说一说服务端渲染?
-
- 传统服务端渲染 : JSP/PHP等等(前后端不分离的旧时代,模板引擎😅,后端拿到前端页面替换数据,前后端耦合) - 现代服务端渲染 : (Vue->Nuxt.js,React->Next.js,自己做SSR , 前后端分离的同时实现SSR) 2. 服务端渲染(SSR)即是在服务器端渲染完整的HTML后返回给客户端(通常是浏览器)。 3. 客户端渲染(CSR),也就是常见的单页面应用(SPA),由服务器端返回的初始 HTML 页面,由 JS 去异步加载数据,完成页面的渲染。由于ajax请求是异步的,百度Google等搜索引擎抓取的页面是几乎空白的,可以右键查看网页源代码看是否完整的HTML结构判断是SSR还是CSR。 4. 同构渲染,就是将SSR和CSR结合在一起,服务端渲染首屏后,激活应用,按照SPA方式运行,结合CSR和SSR各自优点。 5. 从上可知,服务端渲染的优点是: - 搜索引擎爬虫能爬取完整HTML,有利于做搜索引擎优化(SEO),提高网站流量和曝光率 - 首屏渲染在服务端就完成,所以有更快的首屏加载速度 - 只是首屏快,其他页面不像SPA一样是无感刷新,切换页面通过超链接进行页面跳转,体验稍差(传统PHP/JSP) 6.服务端渲染的缺点: - 由于是在服务端进行渲染,需要占用更多服务器端资源(更多的CPU和内存)。 - 由于在服务端渲染,所以有些浏览器API无法使用,同样地 客户端一些生命周期在SSR也是不存在的。 - 部署和开发要求稍高,前后端耦合。
事件扩展符用过吗(…),什么场景下?
得分点 等价于apply的方式、将数组展开为构造函数的参数、数组字符串连接、浅拷贝 标准回答 展开语法(Spread syntax), 可以在函数调用/数组构造时, 将数组表达式或者string在语法层面展开;还可以在构造字面量对象时, 将对象表达式按key-value的方式展开。常见的场景:等价于apply的方式、将数组展开为构造函数的参数、字面量数组或字符串连接不需要使用concat等方法了、构造字面量对象时,进行浅克隆或者属性拷贝 加分回答 只能用于可迭代对象 在数组或函数参数中使用展开语法时, 该语法只能用于 可迭代对象: var obj = {‘key1’: ‘value1’}; var array = […obj]; // TypeError: obj is not iterable 剩余语法(剩余参数) 剩余语法(Rest syntax) 看起来和展开语法完全相同,不同点在于, 剩余参数用于解构数组和对象。从某种意义上说,剩余语法与展开语法是相反的:展开语法将数组展开为其中的各个元素,而剩余语法则是将多个元素收集起来并“凝聚”为单个元素。 function f(…[a, b, c]) { return a + b + c; } f(1) // NaN (b and c are undefined) f(1, 2, 3) // 6 f(1, 2, 3, 4) // 6 (the fourth parameter is not destructured)
说一说vue钩子函数?
得分点 : beforeCreate、created、beforeMounted、mounted beforeUpdate、updated 、 beforeDestroy、destroyed 标准回答 钩子函数用来描述一个组件从引入到退出的全过程中的某个过程,整个过程称为生命周期。 钩子函数按照组件生命周期的过程分为,挂载阶段=>更新阶段=>销毁阶段。 每个阶段对应的钩子函数 挂载阶段:beforeCreate、created、beforeMounted、mounted 更新阶段:beforeUpdate、updated 销毁阶段:beforeDestroy、destroyed 每个阶段特点与适合做什么 created:实例创建完成,可访问data、computed、watch、methods上的方法和数据,未挂载到DOM,不能访问到el属性,el属性,ref属性内容为空数组常用于简单的ajax请求,页面的初始化 beforeMount:在挂载开始之前被调用,beforeMount之前,会找到对应的template,并编译成render函数 mounted:实例挂载到DOM上,此时可以通过DOM API获取到DOM节点,$ref属性可以访问常用于获取VNode信息和操作,ajax请求 beforeupdate:响应式数据更新时调用,发生在虚拟DOM打补丁之前,适合在更新之前访问现有的DOM,比如手动移除已添加的事件监听器 updated:虚拟 DOM 重新渲染和打补丁之后调用,组件DOM已经更新,可执行依赖于DOM的操作避免在这个钩子函数中操作数据,可能陷入死循环 beforeDestroy:实例销毁之前调用。这一步,实例仍然完全可用,this仍能获取到实例,常用于销毁定时器、解绑全局事件、销毁插件对象等操作 加分回答 父子组件钩子函数在三个阶段的代码执行顺序 挂载:父亲created> 子created > 子mounted> 父亲mounted> 更新:父亲beforeUpdate > 子beforeUpdated > 子updated > 父亲updated 销毁:父亲beforeDestroy> 子beforeDestroy > 子destroyed> 父destroyed