岗位:前端开发工程师(媒体中心)岗位 类型:暑期实习 面试时间:2021-03-25 14 : 40 面试时长:大约 35 分钟
春招第一次进入面试,那是因为……雷火这边是直接不用笔试进面试😭 面试体验还不错,面我的面试官来得很准时,人也还不错,面试经验 +1。 虽然问的问题都很基础,从 CSS 到 JS 再到 Vue,但是我还有很多不会的,磕磕巴巴的,继续努力吧。
-
自我介绍(语速太慢,还需完善) 自我介绍需要熟悉,要能流利说出来。
-
面试官说:你确定你是面试前端的是吧?(可能看我实习项目跟前端无关吧)
-
CSS3 盒模型有哪些?
标准模型+IE模型。 包括margin,border,padding,content 盒模型又称框模型(Box Model),包含了元素内容(content)、内边距(padding)、边框(border)、外边距(margin)几个要素。 由于IE盒模型的怪异模式,IE模型和标准模型的内容计算方式不同。 和标准 W3C 盒子模型不同的是:IE 盒子模型的 content 部分包含了 border 和 pading。 宽高同理: IE模型元素宽度width=content+padding+border,高度计算相同 标准模型元素宽度width=content,高度计算相同 通过css3新增的属性 box-sizing: content-box | border-box分别设置盒模型为标准模型(content-box)和IE模型(border-box)。 Note: 对于新的web站点,你可能希望首先将box-sizing设置为border-box,如下所示: * { box-sizing: border-box; } 这使得处理元素大小的工作变得容易得多,并且通常消除了在布局内容时可能遇到的许多陷阱。然而,在某些情况下,你应谨慎使用这个属性。例如: 你正在编写一个将由其他人使用的共享组件库,如果他们网站的其余部分没有设置此值,他们可能会发现很难使用你的组件库。
4. 那么怎么去设置这个盒模型呢?
```
css盒子属性box-sizing,控制元素盒模型的解析模式。box-sizing:
(1)content-box(默认W3C标准盒模型):盒子大小=width(content)+padding+border。
(2)border-box(IE传统盒模型,IE8以下怪异模式):盒子大小= width(content+padding+border)
```
-
说一下定位?
# position相关属性 ## static 默认值。没有定位,元素出现在正常的流中(忽略 top, bottom, left, right 或者 z-index 声明)。 ## relative 相对于原来位置移动,元素仍然在文档流中,不影响其他元素的布局 ## absolute 元素会脱离文档流,如果设置偏移量,会影响其他元素的位置定位。 在父元素没有设置相对定位或绝对定位的情况下,元素相对于根元素定位(即html元素)(是父元素没有)。 父元素设置了相对定位或绝对定位,元素会相对于离自己最近的设置了相对或绝对定位的父元素进行定位(或者说离自己最近的不是static的父元素进行定位,因为元素默认是static) ## fixed 生成绝对定位的元素,相对于浏览器窗口进行定位。元素的位置通过 "left", "top", "right" 以及 "bottom" 属性进行规定。 ## sticky ***定义*** 粘性定位可以被认为是相对定位(relative)和固定定位(fixed)的混合。元素在跨越特定阈值前为相对定位,之后为固定定位。 也就是说sticky会让元素在页面滚动时如同在正常流中(relative定位效果),但当滚动到特定位置时就会固定在屏幕上如同 fixed ,这个特定位置就是指定 的top, right, bottom 或 left 四个阈值其中之一 ***作用域*** 设置了sticky定位的元素相对于第一个定位不为static的父级元素的位置,sticky的作用区域也是在该父级元素的内。也就是说粘性布局的效果只在该父元素内表现出来。**这一点与fixed布局有区别**。 当页面刚开始滚动时item元素并无特殊的表现(相当于relative定位),当滚动到特殊位置(top: 0)时出现粘性定位效果(相当于fixed定位),继续滚动页面发现item元素并不是一直定位在顶部,**当其父元素不在视窗内时item元素失去粘性效果。这一点与fixed的表现不同。fixed定位元素是相对于整个视窗定位**。
-
定位层级的关系?z-index 具体是怎么比较的?
z-index的工作原理及适用范围。 (1)z-index属性控制着元素在z轴上的堆叠顺序,数值越大越靠近屏幕。默认情况下,后来的元素的z-index的值大。 (2)适用范围:仅适用于定位元素,即有relative,absolute,fixed属性的position元素。
-
z-index 值越大就在上面吗?例:两个子 div 都有各自的父 div,层级关系是怎么样的?
z-index 属性设置元素的堆叠顺序。拥有更高堆叠顺序的元素总是会处于堆叠顺序较低的元素的前面。 注释:元素可拥有负的 z-index 属性值。 注释:Z-index 仅能在定位元素上奏效(例如 position:absolute;)! 堆叠上下文中的子元素按照前述顺序摆放。堆叠上下文内部的子堆叠上下文的z-index只在父堆叠上下文中有意义。 总而言之: z-index不为auto,opacity小于1的元素会构建堆叠上下文。 堆叠上下文可以嵌入其他堆叠上下文。 每个堆叠上下文和它的同级上下文是独立的。 每个堆叠上下文是自包含的。 先比较父 div 的 z-index https://www.zhangxinxu.com/wordpress/2016/01/understand-css-stacking-context-order-z-index/ 层叠领域的黄金准则。当元素发生层叠的时候,其覆盖关系遵循下面2个准则: 1. 谁大谁上:当具有明显的层叠水平标示的时候,如识别的z-indx值,在同一个层叠上下文领域,层叠水平值大的那一个覆盖小的那一个。通俗讲就是官大的压死官小的。 2. 后来居上:当元素的层叠水平一致、层叠顺序相同的时候,在DOM流中处于后面的元素会覆盖前面的元素。
-
遇到 CSS 兼容性问题你怎么去处理?在你的项目里用到吗?例:不同浏览器上有些属性是没有的,怎么处理?
浏览器CSS样式初始化、浏览器私有属性,CSS hack语法和自动化插件。 我们为了让页面形成统一的效果,要针对不同的浏览器/版本而写css的过程就叫做CSS hack (1)IE条件注释法 即在正常代码之外添加判别IE浏览器或对应版本的条件注释,符合条件的浏览器或者版本号才会执行里面的代码。 (2)CSS属性前缀法 即给css的属性添加前缀,比如*可以被IE6/IE7识别,但_只能被IE6识别,IE6-IE10都可以识别"\9",IE6不能识别!important ,Firefox不能识别 * _ \9 (3)选择器前缀法 IE6 可识别 *div{color:red} IE7 可识别 *+{color:red} @media screen \9{...} 只对IE6/IE7生效 @media \0screen {body {background:blue}} 只对IE6/7/8有效 浏览器CSS样式初始化 针对不同浏览器渲染效果的不同,可以进行浏览器css样式初始化,也就是加一个reset.css文件 自动化插件 Autoprefixer是一款自动管理浏览器前缀的插件,它可以解析CSS文件并且添加浏览器前缀到CSS内容里。 把Autoprefixe添加到资源构建工具(如webpack)后,可以完全忘记前面的东西,只需按照最新的W3C规范来正常书写CSS,剩下的工作交给插件来处理。另外,如果项目需要支持旧版浏览器,可修改browsers参数设置。
-
CSS3 的新特性有哪些?
新增各种CSS选择器 (:not(.input):所有class不是“input”的节点) 圆角 (border-radius:8px) 多列布局 (multi-columnlayout) 阴影和反射 (Shadow\Reflect) 文字特效 (text-shadow) 文字渲染 (Text-decoration) 线性渐变 (gradient) 旋转 (transform) 缩放, 定位, 倾斜, 动画, 多背景 例如:transform:\scale(0.85,0.90)\translate(0px,-30px)\skew(-9deg,0deg)\Animation: (1)完善了选择器,包括属性、关系、伪类和伪元素选择器。即能避免添加多余的html元素属性名,例如id、name、class等,还能保持页面整洁。 (2)CSS3新增的阴影、圆角、web字体、渐变能替代以往得用图像才能实现的效果,既能减少HTTP请求数,还能提升页面加载速度。 (3)CSS3对背景不再仅限于颜色、填充方式、定位,现在可以一次性使用多张背景图、控制背景尺寸、裁剪背景图像。 (4)引入过渡、动画、transform。 (5)两种全新布局:多列布局、伸缩盒布局。
-
如果页面有比较多的动画,怎么让它保持流畅(60 FPS )的效果?动画太多会造成页面卡顿,优化方案?
css动画启用GPU加速,应用GPU的图形性能对浏览器中的一些图形操作交给GPU完成。canvas2D,布局合成,css3转换,css3d变换,webGL,视频 2d加速 3d加速 使用这些css属性来实现动画:transformation, opacity, filter will-change will-change则是真正的行为触发之前告诉浏览器:“浏览器同学,我待会儿就要变形了,你心理和生理上都准备准备”。于是乎,浏览器同学把GPU给拉上了,从容应对即将到来的变形。 will-change虽然可以加速,但是,一定一定要适度使用。 GPU能够加速,但是也会增加手机耗电。 如果使用JS添加will-change, 事件或动画完毕,一定要及时remove. 比方说点击某个按钮,其他某个元素进行动画。点击按钮(click),要先按下(mousedown)再抬起才出发。因此,可以mousedown时候打声招呼, 动画结束自带回调,于是(示意,不要在意细节): dom.onmousedown = function() { target.style.willChange = 'transform'; }; dom.onclick = function() { // target动画哔哩哔哩... }; target.onanimationend = function() { // 动画结束回调,移除will-change this.style.willChange = 'auto'; }; - 精简DOM,合理布局 - 使用transform代替left、top减少使用引起页面重排的属性 - 开启硬件加速 - 尽量避免浏览器创建不必要的图形层 - 尽量减少js动画,如需要,使用对性能友好的requestAnimationFrame - 使用chrome performance工具调试动画性能 尽量减少重绘和重排。
-
BFC 你有了解过吗?触发情况?
BFC就是一个css的一个布局概念,是一个独立的区域,是一个环境。 满足下列条件之一就可触发BFC: (1)根元素,即html元素。 (2)float的值不为none的浮动元素。 (3)position的值为absolute或者fixed的定位元素。 (4)display的值为inline-block、伸缩盒的元素(inline-flex、flex)、table-caption(相当于caption元素)、table-cell(相当于td,th元素)。 (5)overflow的值不为visible。 用途: (1)清除浮动:在父元素上设置overflow: hidden样式; (2)解决外边距合并问题:盒子垂直方向的距离由margin决定,属于同一个BFC的两个相邻盒子的margin会发生重叠。 (3)左边固定右边自适应的两栏布局。 BFC(Block Formatting Context):块级格式化上下文。 BFC决定了元素如何对其内容进行定位,以及与其他元素的关系和相互作用。当设计到可视化布局的时候,BFC提供了一个环境,HTML元素在这个环境中按照一定的规则进行布局。一个环境中的元素不会影响到其他环境中的布局。 具有 BFC 特性的元素可以看作是隔离了的独立容器,容器里面的元素不会在布局上影响到外面的元素,并且 BFC 具有普通容器所没有的一些特性。 BFC的原理(渲染规则): BFC元素垂直方向的边距会发生重叠。属于不同BFC外边距不会发生重叠 BFC的区域不会与浮动元素的布局重叠。 BFC元素是一个独立的容器,外面的元素不会影响里面的元素。里面的元素也不会影响外面的元素。 计算BFC高度的时候,浮动元素也会参与计算(清除浮动) 如何创建BFC: body 根元素 overflow不为visible; float的值不为none; position的值不为static或relative; display属性为inline-blocks,table,table-cell,table-caption,flex,inline-flex;
-
弹性布局?项目里有用到过吗?
Flexible Box 模型,通常被称为 flexbox,是一种一维的布局模型。它给 flexbox 的子元素之间提供了强大的空间分布和对齐能力。 我们说 flexbox 是一种一维的布局,是因为一个 flexbox 一次只能处理一个维度上的元素布局,一行或者一列。作为对比的是另外一个二维布局 CSS Grid Layout,可以同时处理行和列上的布局。 当使用 flex 布局时,首先想到的是两根轴线 — 主轴和交叉轴。主轴由 flex-direction 定义,另一根轴垂直于它。
-
有关自己的项目,深度学习的一些东西有打包到web上吗?WebAssembly 相关接触过吗?
https://developer.mozilla.org/zh-CN/docs/WebAssembly/Concepts WebAssembly是一种新的编码方式,可以在现代的网络浏览器中运行 - 它是一种低级的类汇编语言,具有紧凑的二进制格式,可以接近原生的性能运行,并为诸如C / C ++等语言提供一个编译目标,以便它们可以在Web上运行。它也被设计为可以与JavaScript共存,允许两者一起工作。 对于网络平台而言,WebAssembly具有巨大的意义——它提供了一条途径,以使得以各种语言编写的代码都可以以接近原生的速度在Web中运行。在这种情况下,以前无法以此方式运行的客户端软件都将可以运行在Web中。 WebAssembly被设计为可以和JavaScript一起协同工作——通过使用WebAssembly的JavaScript API,你可以把WebAssembly模块加载到一个JavaScript应用中并且在两者之间共享功能。这允许你在同一个应用中利用WebAssembly的性能和威力以及JavaScript的表达力和灵活性,即使你可能并不知道如何编写WebAssembly代码。
-
项目有关视频流相关,问是否了解过网页上实时传输视频流有什么方式?了解过 WebRTC 吗?
HLS (基于HTTP的自适应码率流媒体传输协议) 常用的流媒体协议主要有 HTTP 渐进下载和基于 RTSP/RTP 的实时流媒体协议,这二种基本是完全不同的东西,目前比较方便又好用的是用 HTTP 渐进下载方法。 WebRTC(Web Real-Time Communication)是一项实时通讯技术,它允许网络应用或者站点,在不借助中间媒介的情况下,建立浏览器之间点对点(Peer-to-Peer)的连接,实现视频流音频流或者其他任意数据的传输。 WebRTC包含的这些标准使用户在无需安装任何插件或者第三方的软件的情况下,创建点对点(Peer-to-Peer)的数据分享和视频会议成为可能。 底层技术: 图像引擎 (VideoEngine) VP8编解码 jitter buffer: 动态抖动缓冲 Image enhancements: 图像增益 声音引擎 (VoiceEngine) iSAC/iLBC/Opus等编解码 NetEQ语音信号处理 回声消除和降噪 会话管理 (Session Management) iSAC 音效压缩 VP8 Google自家WebM项目的影片编解码器 APIs (Native C++ API, Web API)
-
知道前端方面怎么进行 GPU 优化吗?
以下样式将开启GPU加速: • transform 动画 • opacity 动画 • 设置translateZ()、translate3D() • 设置will-change等
-
简单描述一下变量提升?函数声明式,函数表达式会提升吗?提升的是什么?
在ES6之前,JavaScript没有块级作用域(一对花括号{}即为一个块级作用域),只有全局作用域和函数作用域。变量提升即将变量声明提升到它所在作用域的最开始的部分 JavaScript中,函数和变量的声明总是会被悄悄地“提升”到各自 作用域 的最顶部。 我们习惯将var a = 2;看作一个声明,而实际上JavaScript引擎并不这么认为。它将var a和 a = 2 当作两个单独的声明,第一个是预编译阶段的任务,而第二个则是执行阶段的任务。 这意味着无论作用域中的声明出现在什么地方,都将在代码本身被执行前首先进行处理。可以将这个过程形象地想象成所有的声明(变量和函数)都会被“移动”到各自作用域的最顶端,这个过程被称为提升。 变量”提升“实际上是在JavaScript预编译阶段完成的。 JS引擎会在正式执行代码之前进行一次”预编译“,预编译简单理解就是在内存中开辟一些空间,存放一些变量和函数。具体步骤如下(browser): 页面创建GO全局对象(Global Object)对象(window对象)。 加载第一个脚本文件脚本加载完毕后,进行语法分析。 开始预编译查找函数声明,作为GO属性,值赋予函数体(函数声明优先) 查找变量声明,作为GO属性,值赋予undefined 函数只有声明式函数才会被提升,字面量函数不会被提升。 即函数提升 只会提升函数声明,而 不会提升函数表达式。
-
继承的方式?原型链继承大概的实现方式?
/*JS中的四大继承方案:继承就是子类继承父类中的属性和方法。
方案一:原型继承:子类的原型指向父类的一个实例,继承原型上的内容。
缺点:因为改变原型指向实现继承的同时,初始化了属性,则继承过来的属性一样。并且会丢失原本的原型对象,包括子类constroctor,需要加一个Student.prototyper.constroctor = Student。Student.prototype = new People()会在子类原型对象上有父类的无用属性,则可以使用ES5方法Object.create(),Student.prototype = Object.create(People.prototype)
优点:可解决方法继承。
方案二:借用构造函数继承:在子类构造函数中借用父类构造函数,其执行的时候,方法中的this为子类实例。
缺点:父级类别的方法不能继承。
优点:可解决属性继承,并且值不重复。
方案三:组合继承:call继承+原型继承。
方案四:ES6中class类,extends继承和组合继承基本类似,在子类的constructor中须加上super()函数,相当于A.call(this)。*/
class People{
constructor(name){//constructor内定义的方法和属性是实例对象自己的
this.name = name
}
eat(){//constructor外定义的方法和属性是所有实例对象共享的
console.log(`${this.name} eat something`)
}
}
class Student extends People{
constructor(name, number){
super(name)//在this之前一定要调用super()
this.number = number
}
sayHi(){
console.log(
`姓名${this.name},学号${this.number}`
)
}
}
- 深拷贝具体是怎么实现的?应用场景是什么?
/*浅拷贝和深拷贝都只针对于引用数据类型。
浅拷贝只复制指向某个对象第一层属性值的指针,而不复制对象本身,新旧对象还是共享同一块内存;
但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改变原对象;
区别:浅拷贝只复制对象的第一层属性、深拷贝可以对对象的属性进行递归复制。*/
// 浅拷贝:把对象第一层属性或方法复制到另一个对象中。
// 法一:
function extend(a,b) {
for(var key in a){
b[key]=a[key] } }
// 法二:Object.assign()实现浅拷贝或者说一层的深拷贝:
let obj2 = Object.assign({},obj1)
// 深拷贝:在另一个对象中开辟空间,把对象中所有的属性或方法,进行递归复制。
// 法一:
function extend(obj1, obj2){
for(let key in obj1){
let item = obj1[key]
if(item instanceof Array){ //不能用typeof item这不能分辨出Array、Object
obj2[key] = []
extend(item, obj2[key])
}else if(item instanceof Object){
obj2[key] = {}
extend(item, obj2[key])
}else{
obj2[key] = item
}
}
}
// 法二:使用JSON.stringify()(把对象转成字符串),再JSON.parse()(把字符串转成新的对象)
// 这种写法非常简单,而且可以应对大部分的应用场景,但是它还是有很大缺陷的,比如拷贝其他引用类型、拷贝函数、循环引用等情况。
浅拷贝:
创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
深拷贝:
将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象
- 事件循环,宏任务,微任务?
主线程执行的时候,遇到异步任务,会将异步任务放在事件队列中。遇到同步任务会将其放入到主线程中执行。当主线程执行完毕后,会执行事件队列中异步任务。
单线程是必要的,也是javascript这门语言的基石,原因之一在其最初也是最主要的执行环境——浏览器中,我们需要进行各种各样的dom操作。试想一下 如果javascript是多线程的,那么当两个线程同时对dom进行一项操作,例如一个向其添加事件,而另一个删除了这个dom,此时该如何处理呢?因此,为了保证不会 发生类似于这个例子中的情景,javascript选择只用一个主线程来执行代码,这样就保证了程序执行的一致性。
js引擎遇到一个异步事件后并不会一直等待其返回结果,而是会将这个事件挂起,继续执行执行栈中的其他任务。当一个异步事件返回结果后,js会将这个事件加入与当前执行栈不同的另一个队列,我们称之为事件队列。被放入事件队列不会立刻执行其回调,而是等待当前执行栈中的所有任务都执行完毕, 主线程处于闲置状态时,主线程会去查找事件队列是否有任务。如果有,那么主线程会从中取出排在第一位的事件,并把这个事件对应的回调放入执行栈中,然后执行其中的同步代码...,如此反复,这样就形成了一个无限的循环。这就是这个过程被称为“事件循环(Event Loop)”的原因。
主线程执行的时候,遇到异步任务,会将异步任务放在事件队列中。遇到同步任务会将其放入到主线程中执行。当主线程执行完毕后,会执行事件队列中异步任务。
js单线程原因:防止DOM树修改冲突。JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
宏任务与微任务:
(1)宏任务:setTimeout, setInterval, requestAnimation, I/O,一些浏览器API
(2)微任务:Promise,prosess.nextTick,Object.observe,MutationObserver,JS自身API,或者node
(3)先微任务后宏任务,先取出第一个宏任务,执行完所有相关微任务,在下一个宏任务之前全部执行完。
-
Promise 为什么会出现?作用、应用场景是什么?Async,Await,用过吗?需要等待多个请求回来再进行操作,怎么用 Promise 或者 async,await 实现?
Promise模式 :解决异步问题而产生的,提供了异步编程的解决方式。 当XHR对象发起多个异步请求时,无法保证响应能按发起的顺序返回。如果要保证响应顺序,只能用回调函数的方式控制。这样就会有地狱回调的问题。 Promise是ES6一种异步编程解决方案,通常来表示一个异步操作的成功或失败,是一种代码组织模式,将异步操作用同步操作方式表达,解决了JavaScript地狱回调的问题。 Promise.prototype.then()说明Promise实例具有then(),为Promise实例添加状态改变时的回调函数。then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。 Promise.prototype.catch()方法用于指定发生错误时的回调函数。如果前面有任何的 Promise执行失败,则立即终止所有promise的执行,并马上进入 catch去处理 Promise中抛出的异常。 Promise.prototype.finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。不管promise最后的状态,在执行完then或catch指定的回调函数以后,都会执行finally方法指定的回调函数。finally本质上是then方法的特例。 除了串行执行若干异步任务外,Promise还可以并行执行异步任务。试想一个页面聊天系统,我们需要从两个不同的URL分别获得用户的个人信息和好友列表,这两个任务是可以并行执行的,用Promise.all()实现如下: var p1 = new Promise(function (resolve, reject) { setTimeout(resolve, 500, 'P1'); }); var p2 = new Promise(function (resolve, reject) { setTimeout(resolve, 600, 'P2'); }); // 同时执行p1和p2,并在它们都完成后执行then: Promise.all([p1, p2]).then(function (results) { console.log(results); // 获得一个Array: ['P1', 'P2'] }); Promise.all()接受一个promise对象对象的数组,待所有实例对象变成fulfilled之后,p1、p2、p3的返回值组成一个数组,传递给p的回调函数。只要有一个被rejected,第一个被reject的实例返回值会传给p的回调函数。 Async/Await:它就是 Generator 函数的语法糖,函数前面多了一个aync关键字,await关键字只能用在aync定义的函数内。async函数会隐式地返回一个promise,该promise的reosolve值就是函数return的值。 优点: (1)简洁,不需要写.then,不需要写匿名函数处理Promise的resolve值,也不需要定义多余的data变量,还避免了嵌套代码。 (2)Async/Await让try/catch可以同时处理同步和异步错误。
-
说一下 Vue 双向绑定的原理?详细说一下;设计模式是什么?
Vue实现数据双向绑定的原理:Object.defineProperty() vue实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调。 当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。 vue的数据双向绑定 将MVVM作为数据绑定的入口,整合Observer,Compile和Watcher三者,通过Observer来监听自己的model的数据变化,通过Compile来解析编译模板指令(vue中是用来解析 {{}}),最终利用watcher搭起observer和Compile之间的通信桥梁,达到数据变化 —>视图更新;视图交互变化(input)—>数据model变更双向绑定效果。
-
说一下你对虚拟 DOM 的理解?怎么知道某个结构需要渲染更新?(设置标签?)
虚拟dom优缺点及实现原理 优点:(1)保证性能下限,比DOM操作性能要好很多。(2)无需手动操作DOM,只需写好View-Model的代码逻辑,框架会根据虚拟DOM和数据双向绑定,以预期的方式更新视图,极大提高开发效率。(3)更方便地跨平台操作,因虚拟DOM本质上是JS对象,而DOM与平台强相关。 缺点:无法进行极致优化。 虚拟 DOM 实现原理: (1)用js对象模拟真实DOM树,对真实DOM进行抽象。 (2)diff算法:比较两棵虚拟DOM树的差异。 (3)pach算法:将两个虚拟DOM对象的差异应用到真正的DOM树。 虚拟DOM就是为了解决浏览器性能问题而被设计出来的。若一次操作中有10次更新DOM的动作,虚拟DOM不会立即操作DOM,而是将这10次更新的diff内容保存到本地一个JS对象中,最终将这个JS对象一次性attch到DOM树上,通知浏览器去绘制,避免大量无谓的计算量。 所以,用JS对象模拟DOM节点的好处是,页面的更新可以先全部反映在JS对象(虚拟DOM)上,操作内存中的JS对象的速度显然要更快,等更新完成后,再将最终的JS对象映射成真实的DOM,交由浏览器去绘制。 根据传统的api或JQuery操作DOM,浏览器会从构建DOM树开始从头到尾执行一遍浏览器渲染过程。比如:当你在一次操作时,需要更新10个DOM节点,理想状态是一次性构建完DOM树再去执行后续操作。但是浏览器没有这么智能,收到第一个更新DOM请求后,并不知道后续9次更新操作。因此会马上执行流程,最终会执行完10次流程。显然例如计算DOM节点的坐标值等都白白浪费性能,可能这次计算完,紧接着下一个DOM更新请求后,这个节点的坐标值就变化了,前面的一次计算就是无用功。操作真实DOM代价是很昂贵的,真实的DOM节点,哪怕一个最简单的div也会包含很多属性。 可问题来了,我怎么知道哪个节点更新了,哪个节点删除了,哪个节点替换了呢?——我们需要对DOM建模! VDOM建模 说是建模,简单点说就是用一个JS对象来表示VDOM。 如果我们可以用一个JS对象来表示VDOM,那么这个对象上多一个属性(增加节点),少一个属性(删除节点),或者属性值变了(更改节点),就一目了然了!
-
Vue 的路由有哪些方式?具体实现,依赖什么 API ?
Vue的路由实现:hash模式 和 history模式 hash模式:在浏览器中符号“#”,#以及#后面的字符称之为hash,用window.location.hash读取; 特点:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,hash不会重加载页面。 hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 http://www.xxx.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。 history模式:history采用HTML5的新特性;且提供了两个新方法:pushState(),replaceState()可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更。 history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 http://www.xxx.com/items/id。后端如果缺少对 /items/id 的路由处理,将返回 404 错误。Vue-Router 官网里如此描述:“不过这种模式要玩好,还需要后台配置支持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。” hash: 在浏览器中符号“#”,#以及#后面的字符称之为hash,用window.location.hash读取。特点:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,hash不会重加载页面,支持所有浏览器。 history : 依赖HTML5 History API和服务器配置。提供了两个新方法:pushState(),replaceState()可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更。history模式下,前端的 URL 必须和实际向后端发起请求的URL一致. 路由需要实现三个功能: 当浏览器地址变化时,切换页面; 点击浏览器【后退】、【前进】按钮,网页内容跟随变化; 刷新浏览器,网页加载当前路由对应内容; 在单页面web网页中, 单纯的浏览器地址改变, 网页不会重载,如单纯的hash网址改变网页不会变化,因此我们的路由主要是通过监听事件,并利用js实现动态改变网页内容,有两种实现方式: hash模式:监听浏览器地址hash值变化,执行相应的js切换网页; history模式:利用history API实现url地址改变,网页内容改变; 它们的区别最明显的就是hash会在浏览器地址后面增加#号,而history可以自定义地址。 hash模式 使用window.location.hash属性及窗口的onhashchange事件,可以实现监听浏览器地址hash值变化,执行相应的js切换网页。下面具体介绍几个使用过程中必须理解的要点: hash指的是地址中#号以及后面的字符,也称为散列值。hash也称作锚点,本身是用来做页面跳转定位的。如http://localhost/index.html#abc,这里的#abc就是hash; 散列值是不会随请求发送到服务器端的,所以改变hash,不会重新加载页面; 监听 window 的 hashchange 事件,当散列值改变时,可以通过 location.hash 来获取和设置hash值; location.hash值的变化会直接反应到浏览器地址栏; 浏览器地址栏散列值的变化(包括浏览器的前进、后退)会触发window.location.hash值的变化,从而触发onhashchange事件; 当浏览器地址栏中URL包含哈希如 http://www.baidu.com/#home,这时按下输入,浏览器发送http://www.baidu.com/请求至服务器,请求完毕之后设置散列值为#home,进而触发onhashchange事件; 当只改变浏览器地址栏URL的哈希部分,这时按下回车,浏览器不会发送任何请求至服务器,这时发生的只是设置散列值新修改的哈希值,并触发onhashchange事件; html中<a>标签的属性 href 可以设置为页面的元素ID如 #top,当点击该链接时页面跳转至该id元素所在区域,同时浏览器自动设置 window.location.hash 属性,地址栏中的哈希值也会发生改变,并触发onhashchange事件; history模式 概述 window.history 属性指向 History 对象,它表示当前窗口的浏览历史。当发生改变时,只会改变页面的路径,不会刷新页面。 History 对象保存了当前窗口访问过的所有页面网址。通过 history.length 可以得出当前窗口一共访问过几个网址。 由于安全原因,浏览器不允许脚本读取这些地址,但是允许在地址之间导航。 浏览器工具栏的“前进”和“后退”按钮,其实就是对 History 对象进行操作。 属性 History 对象主要有两个属性。 History.length:当前窗口访问过的网址数量(包括当前网页) History.state:History 堆栈最上层的状态值(详见下文)
-
简历上的项目都是你自己开发的吗?还是有合作的同学一起?
-
项目上你有遇到过什么问题吗?是怎么解决的?
-
反问环节
准备几个反问的问题: 1.公司前端现在做的项目和框架? 2.什么部门,实习生会做什么? 3.平时是怎么学习的,能分享一下吗? 4.怎么样去了解源码,学习源码?
- 首先是对于基本概念,还要熟悉,多理解记忆。
- 回答问题的时候语速比较慢,有些没有意义的词,比如“这个”,“那个”,“额”,“就是……”,“然后”。
- 自己项目方面的东西,无关前端的也可以做一些拓展,不需要太深入。
- 项目上遇到的问题,需要回顾总结一下,还不知道如何回答。
- 反问环节的问题提前准备好。
屡败屡战,继续努力吧。💻