目录
- 一、HTML
- 二、CSS
- 三、JS
- 1、什么是闭包?
- 2、call、apply、bind 作用和区别(改变this指向)
- 3、原型与原型链
- 4、JS基本数据类型
- 5、export和export default的区别
- 6、箭头函数和普通函数的区别
- 7、GET和POST的区别
- 8、forEach和map的区别
- 9、对象的继承
- 10、简述一下你理解的面向对象
- 11、 == 和 ===的区别
- 12、数组有哪些方法?
- 13、 数组去重(笔试一般都会有)
- 14、JS中new操作符有什么用?
- 15、JS获取HTML DOM元素的方法
- 16、事件捕获和事件冒泡
- 17、虚拟dom
- 18、排序方式
- 19、数组操作方法会改变原数组
- 20、JS有几种方法判断变量的类型?
- 21、null和undefined的区别?
- 22、什么是跨域?及跨域解决方法
- 23、防抖和节流的使用及区别
- 24、栈内存和堆内存的区别与原理
- 25、深拷贝和浅拷贝的区别和与原理
- 四、ES6新特性
- 五、计算机网络知识
- 六、VUE
- 1、MVC和MVVM的理解
- 2、数据双向绑定原理
- 3、vue生命周期
- 4、为什么使用虚拟DOM(常问)
- 5、v-if 和 v-show的作用和区别
- 5、用过vue的哪些组件?
- 6、vue-router 的模式有?
- 7、组件之间如何传值
- 8、路由之间如何传参
- 9、VUEX
- 10、如何解决vuex页面刷新数据丢失问题?
- 11、computed和watch的区别?
- 12、route和router的区别
- 13、vue中数据变了但是视图不跟新怎么解决?
- 14、vue中data为什么是函数而不是对象?
- 15、vue中父子组件传值,父组件异步请求,子组件不能实时更新怎么解决?(vue中数据不能实时更新怎么解决?)
- 16、Vue 路由跳转方式
- 17、Vue 中 for循环为什么加 key?
- 18、vue路由守卫有哪些?
- 19、vue常⽤的修饰符
- 20、vue的两个核⼼点
- 21、delete和Vue.delete删除数组的区别
- 22、vue常⽤的UI组件库
- 23、Vue首页白屏是什么问题引起的?如何解决呢?
- 24、性能优化篇
- 七、React
点击打开视频讲解面试技巧+面试全过程
一、HTML
1、语义话的目的是什么?
语义化的主要目的可以概括为用正确的标签做正确的事
HTMl语义化可以让页面的内容结构化,以便于浏览器解析和搜索引擎解析,
并且提高了代码的可读性便于代码维护,
2、HTML5新增元素
Canvas绘图以及SVG绘图。
拖放(Drag and drop)API
语义化标签(header、nav、footer、article、section)
音频、视频(audio、video)API
地理定位(Geolocation)
本地离线存储(localStorage),长期存储数据,关闭浏览器后不丢失。
会话储存(sessionStorage),数据在关闭浏览器后自动删除。
表单控件(calendar、date、time、email、url、search)
3、cookie与sessionStorage和localStorage的区别
保存方式
cookie存放在客户的浏览器上。
session都在客户端中保存,不参与服务器通讯。
生命周期
cookie可设置失效时间
localStorage除非手动清除否则永久保存
sessionStorage关闭当前页面或浏览器后失效
存储的大小
cookie 4kb左右
session 5M
易用性
cookie需自己封装
session可以接受原生接口
因为cookie每次请求都会携带在http请求中,所以它的主要用来识别用户登录,localStorage可以用
来跨页面传参,sessionStorage可以用来保留一些临时数据。
二、CSS
1、CSS有哪些基本的选择器,执行先后顺序?
id选择器 => #myId {}
类选择器 => .myClass {}
标签选择器 => p,h1 {}
后代选择器 => div h1 {}
子选择器 => div>h1 {}
兄弟选择器(所有的兄弟) => ul~h1 {}
相邻兄弟选择器 => ul+h1 {}
属性选择器 => li[name='sss'] {}
伪类选择器 => h1:hover {}
伪元素选择器 => h1::before{}
通配符选择器* => * {}
!important>内联样式(非选择器)>ID选择器>类选择器>标签选择器>通配符选择器(*)
2、垂直水平居中方式有哪些?
3、常用布局方式有哪些?什么是盒模型?
4、常用的块元素与行内元素有哪些?有什么特征
块元素:div、h1~h6、ul、li、table、p、br、form。
特征:独占一行,换行显示,可以设置宽高,可以嵌套块和行
行内元素:span、a、img、textarea、select、option、input。
特征:只有在行内显示,内容撑开宽、高,不可以设置宽、高(img、input、textarea等除外)。
5、清除浮动
父级div定义overflow:hidden(如果父级元素有定位元素超出父级,超出部分会隐藏,)
给浮动元素父级增加标签(由于新增标签会造成不必要的渲染,不建议使用)
伪元素清除浮动:给浮动元素父级增加 .clearfix::after(content: ‘’; display: table;
clear: both;)(不会新增标签,不会有其他影响,)
6、CSS3新特征
圆角(border-radius)
阴影(box-shadow)
文字特效(text-shadow)
线性渐变(gradient)
变换(transform)
更多的CSS选择器
更多背景设置(background)
色彩模式(rgba)
伪元素(::selection)
媒体查询(@media)
多栏布局(column)
图片边框(border-image)
7、CSS中有哪些长度单位?
绝对长度单位:px
百分比: %
相对父元素字体大小单位: em
相对于根元素字体大小的单位: rem
相对于视口*宽度的百分比(100vw即视窗宽度的100%): vw
相对于视口*高度的百分比(100vh即视窗高度的100%): vh
8、px、em、rem的区别
9、display:none和visibility:hidden的区别
display:none:隐藏元素,在文档布局中不在给它分配空间(从文档中移除),会引起回流(重排)
visibility:hidden: 隐藏元素,但是在文档布局中仍保留原来的空间(还在文档中),
不会引起回流(重绘)
10、用CSS 实现三角形
11、伪类和伪元素的区别
12、什么是重绘,重排?如何解决?
重绘(repaint/redraw)
某个dom节点的颜色,背景颜色变了,字体大小,只影响自己,不影响其他元素。
注意:table及其内部元素可能需要多次计算才能确定好其在渲染树中节点的属性,比同等元素要多花两倍时间,这就是我们尽量避免使用table布局页面的原因之一。
重排(回流/reflow/重构)
某个dom节点的宽高,布局,隐藏等发生改变,不仅自身发生了改变,而且其他元素也会受到影响随之发生改变。每个页面最少一次回流,就是页面第一次加载的时候。
触发重排的原因
页面初始化渲染(无可避免)
添加或删除可见的DOM元素
元素尺寸的改变------大小,外边距;边框
浏览器窗口尺寸的变化
填充内容的改变,比如文本的改变或图片大小改变而引起的计算值宽度和高度的改变
读取某些元素属性:(offsetLeft/Top/Height/Width, clientTop/Left/Width/Height,
scrollTop/Left/Width/Height, width/height, getComputedStyle(),
currentStyle(IE) )
1.重绘不一定重排,但是重排一定会重绘
2.重绘和重排的成本都是非常高的,要尽量减少dom的增删改
如何解决
不要直接操作样式,先设置好class,然后修改DOM的className;
position:absolute 与flex 不会导致重排
不要把DOM 节点的属性放在一个循环当成循环的变量;
需要动画的元素脱离文档流;
不使用table 布局,
尽量不修改影响范围比较大的DOM;
如果要多次添加DOM,先使用 document.createDocumentFragment() 创建一个盒子,
完盒子里面先添加子元素,添加完成在插入元素中;
13、transition 都有哪些过度属性?
14、link和@import的区别?
link属于html标签,而@import是css提供的。
页面被加载时,link会同时被加载,而@import引用的css会等到页面被加载完再加载的。
兼容性问题:@import只在IE5以上才能识别,而link是html标签,无兼容性问题。
权重问题:@import的权重要高于link。
DOM操作:DOM可以操作link中的样式,而不可以操作@import中的样式。
<!-- html 文件 -->
<!-- link 方式,推荐 -->
<link rel="stylesheet" type="text/css" href="style.css" media="screen" />
<!-- @import 方式 -->
<style type="text/css" media="screen">
@import url(style.css);
</style>
15、常用的动画库有哪些?
16、什么是BFC
17、href与src的区别
18、如何让 CSS 只在当前组件中起作用?
将当前组件的<style>修改为<style scoped>。
三、JS
1、什么是闭包?
2、call、apply、bind 作用和区别(改变this指向)
3、原型与原型链
4、JS基本数据类型
5、export和export default的区别
6、箭头函数和普通函数的区别
- 语法更加简洁、清晰,=>()
- 箭头函数是匿名函数,不能作为构造函数,不能使用new
- 箭头函数不能使用arguments,而用reat参数…解决
- 箭头函数没有自己的this,会捕获其所在的上下文的this值,并且不能通过call()和apply()来改变其this
- 箭头函数没有原型
7、GET和POST的区别
8、forEach和map的区别
9、对象的继承
常见的:
原型链继承
借用构造函数继承
原型链+借用构造函数的组合继承(使用 call 或 applay 方法)
ES6中class 的继承(class可以通过extends关键字实现继承)
10、简述一下你理解的面向对象
面向对象是基于万物皆对象这个哲学观点. 把一个对象抽象成类,具体上就是把一个对象的静态特征和动态特征抽象成属性和方法,也就是把一类事物的算法和数据结构封装在一个类之中,程序就是多个对象和互相之间的通信组成的。
面向对象具有封装性,继承性,多态性。
封装:隐蔽了对象内部不需要暴露的细节,使得内部细节的变动跟外界脱离,只依靠接口进行通信.封装性降低了编程的复杂性。
继承:使得新建一个类变得容易,一个类从派生类那里获得其非私有的方法和公用属性的繁琐工作交给了编译器。
多态:继承和实现接口和运行时的类型绑定机制所产生的多态,使得不同的类所产生的对象能够对相同的消息作出不同的反应,极大地提高了代码的通用性.。
11、 == 和 ===的区别
12、数组有哪些方法?
13、 数组去重(笔试一般都会有)
14、JS中new操作符有什么用?
- 创建临时对象,并将this指向临时对象
- 将构造函数的原型属性和方法挂载到新对象的__proto__(原型指针)上
- return 临时对象
15、JS获取HTML DOM元素的方法
- 通过ID获取(getElementById)
- 通过name属性(getElementsByName)
- 通过标签名(getElementsByTagName)
- 通过类名(getElementsByClassName)
- 获取html的方法(document.documentElement)
- 获取body的方法(document.body)
- 通过选择器获取一个元素(querySelector)
- 通过选择器获取一组元素(querySelectorAll)
16、事件捕获和事件冒泡
- 事件捕获和事件冒泡主要解决了页面事件流的问题。页面的事件流经过了三个阶段,分别是事件捕获、目标阶段和事件冒泡阶段。
- 事件捕获是由外向内;而事件冒泡则是由内向外。
- event.stopPropagation() 可以阻止事件流的进一步传播。
- 采用事件代理的方式,能够节省内存消耗,对于动态改变子元素的时候,也非常有利,避免了很多麻烦的步骤,比如重新绑定事件。(把子元素的事件委托给父元素来处理)
17、虚拟dom
定义: 虚拟DOM就是普通的js对象。用来描述真实dom结构的js对象,因为它不是真实的dom,所以才叫做虚拟dom。
作用: 虚拟dom可以很好地跟踪当前dom状态,因为它会根据当前数据生成一个描述当前dom结构的虚拟dom,然后数据发生变化时,有生成一个新的虚拟dom,而两个虚拟dom恰好保存了变化前后的状态。然后通过diff算法,计算出当前两个虚拟dom之间的差异,得出一个更好的替换方案。
18、排序方式
冒泡排序:比较所有相邻元素,如果第一个比第二个大,则交换它们。
选择排序:找到数组中的最小值,选中它并将其放置在第一位。
插入排序:从第二个数开始往前比,比它大就往后排。
归并排序:把数组劈成两半,再递归地对数组进行“分”操作,直到分成一个个单独的数。
快速排序:从数组中任意选择一个基准,所有比基准小的元素放到基准前面,比基准大的元素放到
基准的后面。
19、数组操作方法会改变原数组
会改变:push(),pop(),shift(),unshift() ,splice(),sort(),reverse()。
不变:concat(),split(),slice()。
js中substr()、substring()、slice()、splice()、split()的区别与作用
20、JS有几种方法判断变量的类型?
typeof:判断基本数据类型,对于引用数据类型除了function返回’function‘,其余全部
返回’object’。
instanceof:区分引用数据类型,检测方法是检测的类型在当前实例的原型链上,用其检测出来的
结果都是true,不太适合用于简单数据类型的检测,检测过程繁琐且对于简单数据类
型中的undefined, null, symbol检测不出来。
constructor:检测引用数据类型,检测方法是获取实例的构造函数判断和某个类是否相同,如果
相同就说明该数据是符合那个数据类型的,这种方法不会把原型链上的其他类也加入
进来,避免了原型链的干扰。
Object.prototype.toString.call():适用于所有类型的判断检测,检测方法是
Object.prototype.toString.call(数据) 返回的是该数据类型的字符串。
(举例:字符串返回的是[object String])
instanceof的实现原理:验证当前类的原型prototype是否会出现在实例的原型链__proto__上,只要在它的原型链上,则结果都为true。因此,instanceof 在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype,找到返回true,未找到返回false。
Object.prototype.toString.call原理:Object.prototype.toString 表示一个返回对象类型的字符串,call()方法可以改变this的指向,那么把Object.prototype.toString()方法指向不同的数据类型上面,返回不同的结果
21、null和undefined的区别?
null和undefined 区别为:undefined是表示变量声明过但并未赋过值,它是所有未赋值变量默认值;null表示一个变量将来可能指向一个对象,一般用于主动释放指向对象的引用。
(1)、null与undefined的异同点是什么呢?
共同点: 都是原始类型,保存在栈中变量本地
不同点:
(1)undefined——表示变量声明过但并未赋过值。
它是所有未赋值变量默认值。
例如:var a; //a自动被赋值为undefined
(2)null——表示一个变量将来可能指向一个对象。
一般用于主动释放指向对象的引用。
2、何时使用null?
当使用完一个比较大的对象时,需要对其进行释放内存时,设置为null
3、定义
(1)undefined:是所有没有赋值变量的默认值,自动赋值
(2)null:主动释放一个变量引用的对象,表示一个变量不再指向任何对象地址
22、什么是跨域?及跨域解决方法
23、防抖和节流的使用及区别
24、栈内存和堆内存的区别与原理
25、深拷贝和浅拷贝的区别和与原理
四、ES6新特性
新增块级作用域let定义变量和const定义常量
变量的解构赋值
模板字符串 (‘${}’)
默认参数(key=value)
箭头函数(=>)
扩展运算符(…)
模块(import/export)
类(class/extends)
Promise
Proxy
Symbol
Promise
含义: 异步编程的一种解决方案,用来解决回调地狱。
三种状态: pending(进行中)、fulfilled(已成功)和rejected(已失败) (Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。)
resolved函数作用: 将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved)。
reject函数的作用: 将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected)。
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
then: Promise 实例添加状态改变时的回调函数。可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。
缺点: 无法取消Promise,一旦新建它就会立即执行,无法中途取消。如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
五、计算机网络知识
1、HTTP与HTTPS
HTTP:客户端与服务器之间数据传输的格式规范,表示“超文本传输协议”。
HTTPS:在HTTP与TCP之间添加的安全协议层。
默认端口号:HTTP:80,HTTPS:443。
传输方式:http是明文传输,https则是具有安全性的ssl加密传输协议。
连接方式:http的是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证
的网络协议,比http协议安全。
2、TCP与UDP的区别
TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接。
TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的
每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
TCP首部开销20字节;UDP的首部开销小,只有8个字节。
TCP提供可靠的服务。UDP适用于一次只传少量数据、对可靠要求不高的环境。
3、HTTP常见的状态码
1开头的状态码(信息类)
100,接受的请求正在处理,信息类状态码
2开头的状态码(成功类)
2xx(成功)表示成功处理了请求的状态码
200(成功)服务器已成功处理了请求。
3开头的状态码(重定向)
3xx(重定向)表示要完成请求,需要进一步操作。通常这些状态代码用来重定向。
301,永久性重定向,表示资源已被分配了新的 URL
302,临时性重定向,表示资源临时被分配了新的 URL
303,表示资源存在另一个URL,用GET方法获取资源
304,(未修改)自从上次请求后,请求网页未修改过。服务器返回此响应时,不会返回网页内容
4开头的状态码(客户端错误)
4xx(请求错误)这些状态码表示请求可能出错,妨碍了服务器的处理
400(错误请求)服务器不理解请求的语法
401表示发送的请求需要有通过HTTP认证的认证信息
403(禁止)服务器拒绝请求
404(未找到)服务器找不到请求网页
5开头的状态码(服务器错误)
5xx(服务器错误)这些状态码表示服务器在尝试处理请求时发生内部错误。这些错误可能是服务器
本身的错误,而不是请求的错误
500,(服务器内部错误)服务器遇到错误,无法完成请求
503,表示服务器处于停机维护或超负载,无法处理请求
4、浏览器从输入url到页面加载发生了什么
1、浏览器的地址栏输入URL并按下回车。
2、浏览器查找当前URL是否存在缓存,并比较缓存是否过期。
3、DNS解析URL对应的IP。
4、根据IP建立TCP连接(三次握手)。
5、HTTP发起请求。
6、服务器处理请求,浏览器接收HTTP响应。
7、渲染页面,构建DOM树。
8、关闭TCP连接(四次挥手)。
5、HTTP 传输过程
含义:从建立连接到断开连接一共七个步骤,就是三次招手四次挥手
TCP 建立连接
浏览器发送请求命令
浏览器发送请求头
服务器应答
服务器回应信息
服务器发送数据
断开TCP连接
6、浏览器如何渲染页面的?
浏览器解析html源码,将HTML转换成dom树,
将CSS样式转换成stylesheet(CSS规则树),
浏览器会将CSS规则树附着在DOM树上,并结合两者生成渲染树(Render Tree)
生成布局(flow),浏览器通过解析计算出每一个渲染树节点的位置和大小,在屏幕上画出渲染树的
所有节点
合成绘制生成页面。
7、性能优化
减少http请求次数
减少DNS查找
避免重定向
使用Ajax缓存
少用全局变量、减少DOM操作的使用
优化图片大小,通过CSS Sprites(精灵图)优化图片,
将css放在顶部,将js放在底部
8、webpack是怎么打包的,babel又是什么
Webpack:把所有依赖打包成一个 bundle.js文件,通过代码分割成单元片段并按需加载。Webpack是以公共JS的形式来书写脚本的,但对AMD/CMD的支持也很全面,方便旧项目进行代码迁移。
把项目当做一个整体,通过一个给定的主文件(如:index.js),Webpack将从这个文件开始找到项目的所有依赖文件,使用loaders处理它们,最后打包为一个(或多个)浏览器可识别的JavaScript文件。
babel将es6、es7、es8等语法转换成浏览器可识别的es5或es3语法。
9、git 和 svn的区别
SVN是集中式版本控制系统,版本库是集中放在中央服务器的,首先要从中央服务器哪里得到最新的版本,干完活后,需要把自己做完的活推送到中央服务器。集中式版本控制系统是必须联网才能工作(如果在局域网还可以,带宽够大,速度够快,如果在互联网下,如果网速慢的话,就纳闷了)
Git是分布式版本控制系统,没有中央服务器的,每个人的电脑就是一个完整的版本库,这样,工作的时候就不需要联网了,因为版本都是在自己的电脑上。自己在电脑上改了文件A,其他人也在电脑上改了文件A,这时,只需把各自的修改推送给对方,就可以互相看到对方的修改了。
10、webSocket
webSocket: 可以让服务器主动向客户端发送消息,适合开发聊天室,多人游戏等协作应用。
WebSocket协议是基于TCP的一种新的网络协议。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
11、require和import区别
-
调用时间
require运行时调用,理论上可以运用在代码任何地,甚至不需要赋值给某个变量之后再使用。
lmport是编译时候调用,必须放在文件开头,而且使用格式也是确定的。 -
遵循规范
require 是 AMD规范引入方式
import是es6的一个语法标准,如果要兼容浏览器的话必须转化成es5的语法 -
本质
require是赋值过程,其实require 的结果就是对象、数字、字符串、函数等,再把require的结果赋值给某个变量。
import是解构过程。
通过require 引入基础数据类型时,属于复制该变量。
通过require 引入复杂数据类型时,数据浅拷贝该对象。
出现模块之间的循环引用时,会输出已经执行的模块,而未执行的模块不输出(比较复杂)。CommonJS模块默认export的是一个对象,即使导出的是基础数据类型。
ES6 模块语法是 JavaScript 模块的标准写法,坚持使用这种写法,取代 Node.js 的 CommonJS 语法。
使用import取代require()。
// CommonJS 的写法
const moduleA = require('moduleA');
const func1 = moduleA.func1;
const func2 = moduleA.func2;
// ES6 的写法
import { func1, func2 } from 'moduleA';
使用export取代module.exports。
// commonJS 的写法
var React = require('react');
var Breadcrumbs = React.createClass({
render() {
return <nav />;
}
});
module.exports = Breadcrumbs;
// ES6 的写法
import React from 'react';
class Breadcrumbs extends React.Component {
render() {
return <nav />;
}
};
export default Breadcrumbs;
12、事件循环(Event Loop)
原因:JavaScript是单线程,所有任务需要排队,前一个任务结束,才会执行后一个任务。
所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。
同步任务:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
异步任务:不进入主线程、而进入"任务队列"的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
同步和异步任务分别进入不同的执行环境, 先执行同步任务,把异步任务放入循环队列当中挂起,等待同步任务执行完,再执行队列中的异步任务。异步任务先执行微观任务,再执行宏观任务。一直这样循环,反复执行。
微任务:Promise.then、catch、finally、async/await。
宏任务:整体代码 Script、UI 渲染、setTimeout、setInterval、Dom事件、ajax事件。
宏任务、微任务是怎么执行的?
执行顺序:先执行同步代码,遇到异步宏任务则将异步宏任务放入宏任务队列中,遇到异步微任务则将异步微任务放入微任务队列中,当所有同步代码执行完毕后,再将异步微任务从队列中调入主线程执行,微任务执行完毕后再将异步宏任务从队列中调入主线程执行,一直循环直至所有任务执行完毕。
13、什么是单页面应用(SPA)
一个系统只加载一次资源,之后的操作交互、数据交互是通过路由、ajax来进行,页面并没有刷新。
在一个页面上集成多种功能,甚至整个系统就只有一个页面,所有的业务功能都是它的子模块,通过特定的方式挂接到主界面上。
优点:
- 前后端分离
- 良好的交互体验——用户不用刷新页面,页面显示流畅
- 减轻服务器压力——服务器只出数据
- 共用一套后端代码——多个客户端可共用一套后端代码
- 加载速度快,内容的改变不需要重新加载整个页面,对服务器压力小
缺点:
- SEO难度高——数据渲染在前端进行
- 页面初次加载比较慢,页面复杂提高很多
多页面: 一个应用多个页面,页面跳转时整个页面都刷新,每次都请求一个新的页面
有点:SEO效果好
缺点: 页面切换慢,每次切换页面需要选择性的重新加载公共资源
14、什么叫优雅降级和渐进增强?
渐进增强(Progressive Enhancement): 一开始就针对低版本浏览器进行构建页面,完成基本的功能,然后再针对高级浏览器进行效果、交互、追加功能达到更好的体验。
优雅降级(Graceful Degradation): 一开始就构建站点的完整功能,然后针对浏览器测试和修复。
在传统软件开发中,经常会提到向上兼容和向下兼容的概念。渐进增强相当于向上兼容,而优雅降级相当于向下兼容。向下兼容指的是高版本支持低版本的或者说后期开发的版本支持和兼容早期开发的版本,向上兼容的很少。大多数软件都是向下兼容的。
二者区别:
1、优雅降级和渐进增强只是看待同种事物的两种观点。
2、优雅降级观点认为应该针对那些最高级、最完善的浏览器来设计网站。
3、渐进增强观点则认为应关注于内容本身。
六、VUE
1、MVC和MVVM的理解
M:model(数据模型),V:view(视图),C:controller(逻辑处理),VM:(连接model和view)
MVC:单向通信。必须通过controller来承上启下。
MVVM:数据双向绑定,数据改变视图,视图改变数据。
2、数据双向绑定原理
答:通过数据劫持结合发布—订阅模式,通过Object.defineProperty()为各个属性定义get、set
方法,在数据发生改变时给订阅者发布消息,触发相应的事件回调。
3、vue生命周期
Vue 实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。通俗说就是 Vue 实例从创建到销毁的过程,就是生命周期。
- beforeCreate:创建前。此时,组件实例刚刚创建,还未进行数据观测和事件配置,拿不到任何数据。
- created:创建完成。vue 实例已经完成了数据观测,属性和方法的计算(比如props、methods、data、computed和watch此时已经拿得到),未挂载到DOM,不能访问到el属性,el属性,ref属性内容为空数组常用于简单的ajax请求,页面的初始化。
- beforeMount:挂载前。挂在开始之前被调用,相关的render函数首次被调用(虚拟DOM)。编译模板,把data里面的数据和模板生成html,完成了el和data 初始化,注意此时还没有挂在html到页面上。
- mounted:挂载完成。也就是模板中的HTML渲染到HTML页面中,此时可以通过DOM API获取到DOM节点,$ref属性可以访问常用于获取VNode信息和操作,ajax请求,mounted只会执行一次。
- beforeUpdate:在数据更新之前被调用,发生在虚拟DOM重新渲染和打补丁之前,不会触发附加地重渲染过程。
- updated:更新后。在由于数据更改导致地虚拟DOM重新渲染和打补丁之后调用,
beforeDestroy;销毁前。在实例销毁之前调用,实例仍然完全可用。(一般在这一步做一些重置的操作,比如清除掉组件中的定时器 和 监听的dom事件) - destroyed:销毁后。在实例销毁之后调用,调用后,vue实列指示的所有东西都会解绑,所有的事件监听器会被移除。
其他:
activated:在keep-alive组件激活时调用
deactivated:在keep-alive组件停用时调用
4、为什么使用虚拟DOM(常问)
- 创建真实DOM的代价高:真实的 DOM 节点 node 实现的属性很多,而 vnode 仅仅实现一些必要的属性,相比起来,创建一个 vnode 的成本比较低。
- 触发多次浏览器重绘及回流:使用 vnode ,相当于加了一个缓冲,让一次数据变动所带来的所有 node 变化,先在 vnode 中进行修改,然后 diff 之后对所有产生差异的节点集中一次对 DOM tree 进行修改,以减少浏览器的重绘及回流。
- 虚拟dom由于本质是一个js对象,因此天生具备跨平台的能力,可以实现在不同平台的准确显示。
- Virtual DOM 在性能上的收益并不是最主要的,更重要的是它使得 Vue 具备了现代框架应有的高级特性。
5、v-if 和 v-show的作用和区别
无论v-show的值为true或者false,元素都会存在于html页面中; 而v-if的值为true时,元素才会
存在于html页面中。
v-show指令是通过修改元素的display属性来实现的。
v-if是动态地向DOM树内添加或者删除DOM元素,v-show是通过设置DOM元素的display样式属性控制
显隐的。
v-if是"真正的"条件渲染,因为它会确保在奇幻过程中条件快内的事件监听器和子组件适当的销毁和
重建。
v-if也是惰性的,如果在初始渲染时条件为假,则什么也不做,直到条件第一次变为真时,才开始渲染
条件块。
v-show不管初始条件是什么,元素总会背渲染,并且只是简单地基于css进行切换。
一般来说,v-if有更高的切换开销,而v-show则有更高的初始渲染开销
因此,如果需要非常频繁地切换,使用v-show比较好,如果在运行时条件不会改变时,用v-if比较好
切换时生命周期函数的执行
v-if
初始渲染
初始值为false时,不会渲染(所谓惰性),生命周期函数不会执行
初始值为true时,组件开始渲染,依次执行beforCreate,created,beforeMount,mounted等生命周期
切换时
从false到true:依次执行beforeCreate,created,beforeMount,moynted
从true到false:依次执行beforeDestroy,destroyed
v-show
初始渲染
无论初始状态为true还是false,组件都会渲染,依次执行beforeCreate,created,beforeMount,mounted
切换
基于css进行切换,对生命周期函数无影响.
5、用过vue的哪些组件?
1、vue-route
2、axios
3、vuex
4、Element UI
5、swiper
6、vue-echarts
7、vue-video-player
8、vue-photo-preview等等
6、vue-router 的模式有?
1、hash模式,用URL hash值来做路由,支持所有浏览器;该模式实现的路由,在通过链接后面
添加““#”+路由名字”。
2、history模式,由h5提供的history对象实现,依赖H5 History API和服务器配置。
3、abstract模式,支持所有JS运行环境,如Node服务器端,如果发现没有浏览器的API,路由会
自动强制进入该模式。
7、组件之间如何传值
Vue父子 组件之间传值
子组件通过props来接受数据和通过$emit来触发父组件的自定义事件
兄弟组件之间的传值
建一个公共组件bus.js.。传递方通过事件触发bus.$emit。接收方通过在mounted(){}生命周期
里触发bus.$on。
可以通过VUEX 来跨组件传参
父孙传值 $attrs
(向下)$listeners
(向上)
祖先和子孙传值provide/inject
获取父组件实例this.$parent
8、路由之间如何传参
- 通过router-link路由导航跳转传递
<router-link to=`/a/${id}`>routerlink传参</router-link>
- 跳转时使用push方法拼接携带参数。
this.$router.push({
path: `/getlist/${id}`,
})
- 通过路由属性中的name来确定匹配的路由,通过params来传递参数
this.$router.push({
name: 'Getlist',
params: {
id: id
}
})
- 使用path来匹配路由,然后通过query来传递参数。
this.$router.push({
path: '/getlist',
query: {
id: id
}
})
注意:query有点像ajax中的get请求,而params像post请求。
params在地址栏中不显示参数,刷新页面,参数丢失,
其余方法在地址栏中显示传递的参数,刷新页面,参数不丢失。
9、VUEX
原理:Vuex是专门为vue.js应用程序设计的状态管理工具。
1、state 保存vuex中的数据源,通过this.$store.state获取
2、getters 用于监听state中的值的变化,返回计算后的结果。getter的返回值会根据它的依赖
被缓存起来
3、mutations 是修改store中的值得唯一方式 //this.$store.commit('add')
4、action 官方建议提交一个actions,在actions中提交mutations再去修改状态值。
this.$store.dispatch('add')
5、modules 模块化
10、如何解决vuex页面刷新数据丢失问题?
原因:因为vuex里的数据是保存在运行内存中的,当页面刷新时,页面会重新加载vue实例,vuex里面的数据就会被清空。
解决方法:将vuex中的数据直接保存到浏览器缓存中。(一般是用sessionStorage)
11、computed和watch的区别?
computed值有缓存、触发条件是依赖值发生更改、 watch无缓存支持异步、监听数据变化
computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生
改变,下一次获取 computed 的值时才会重新计算 computed 的值;
watch: 更多的是观察的作用,支持异步,类似于某些数据的监听回调 ,每当监听的数据变化时都
会执行回调进行后续操作;
computed应用场景:需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以
利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
watch应用场景:需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch
选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结
果前,设置中间状态。这些都是计算属性无法做到的。
12、route和router的区别
- route:是路由信息对象,包括“path,parms,hash,name“等路由信息参数。
- router:是路由实例对象,包括了路由跳转方法,钩子函数等。
13、vue中数据变了但是视图不跟新怎么解决?
vue不能检测以下变动的数组:
1、当你利用索引直接设置一个项时,vm.items[indexOfItem] = newValue
2、当你修改数组的长度时,例如: vm.items.length = newLength
对象属性的添加或删除
由于 Vue 会在初始化实例时对属性执行 getter/setter 转化过程,所以属性必须在 data 对象上存在才能让 Vue 转换它,这样才能让它是响应的。
解决办法:
使用 Vue.set(object, key, value) 方法将响应属性添加到嵌套的对象上
Vue.set(vm.someObject, ‘b’, 2) 或者 this.$set(this.someObject,‘b’,2) (这也是全局 Vue.set 方法的别名)
异步更新队列
在最新的项目中遇到了这种情况,数据第一次获取到了,也渲染了,但是第二次之后数据只有在再一次渲染页面的时候更新,并不能实时更新。
Vue 异步执行 DOM 更新。只要观察到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。如果同一个 watcher 被多次触发,只会被推入到队列中一次。
解决办法:
可在数据变化之后立即使用 Vue.nextTick(callback)。这样回调函数在 DOM 更新完成后就会调用
14、vue中data为什么是函数而不是对象?
vue组件是可复用的vue实例,一个组件被创建好之后,就可能被用在各个地方,而组件不管被复用了多少次,组件中的data数据都应该是相互隔离,互不影响的.
基于这一理念,组件每复用一次,data数据就会被复制一次,之后,当某一处复用的地方组件内data数据被改变时,其他复用地方组件的data数据不受影响。
如果data是对象的话,每当被复用是,复用的对象和源对象都会指向同一个内存地址(浅拷贝),互相之间相互影响,所以要是函数
15、vue中父子组件传值,父组件异步请求,子组件不能实时更新怎么解决?(vue中数据不能实时更新怎么解决?)
首先了解父子组件生命周期执行顺序
加载渲染数据过程
父组件 beforeCreate
父组件 created
父组件 beforeMount
子组件 beforeCreate
子组件 created
子组件 beforeMount
子组件 mounted
父组件 mounted
原因:因为生命周期只会执行一次,数据是要等到异步请求以后才能拿到,那么子组件的mounted钩子执行的时候,还没有拿到父组件传递过来的数据,但是又必须要打印出来结果,那这样的话,就只能去打印props中的默认值空字符串了,所以打印的结果是一个空字符串。
解决办法:
1、使用v-if控制组件渲染的时机
初始还没拿到后端接口的异步数据的时候,不让组件渲染,等拿到的时候再去渲染组件。使用v-if="变量"去控制,初始让这个变量为false,这样的话,子组件就不会去渲染,等拿到数据的时候,再让这个变量变成true,
举例:
data() {
return {
isTrue:false // 初始为false
};
},
monted(){
this.$post.a.b.c.getData(res=>{
if(res.result){
this.isTrue = true
}
})
}
2、使用watch监听数据的变化
举例:
props: {
tableData: {
type: Array,
default: [],
},
},
watch: {
tableData(val){
console.log(val)
}
},
3、使用VueX
16、Vue 路由跳转方式
router-link 标签跳转
this.$router.push()
this.$router.replace()
this.$router.go(n):(0:当前页,-1上一页,+1下一页,n代表整数)
17、Vue 中 for循环为什么加 key?
为了性能优化, 因为vue是虚拟DOM,更新DOM时用diff算法对节点进行一一比对,比如有很多li元素,要在某个位置插入一个li元素,但没有给li上加key,那么在进行运算的时候,就会将所有li元素重新渲染一遍,但是如果有key,那么它就会按照key一一比对li元素,只需要创建新的li元素,插入即可,不需要对其他元素进行修改和重新渲染。
key也不能是li元素的index,因为假设我们给数组前插入一个新元素,它的下标是0,那么和原来的第一个元素重复了,整个数组的key都发生了改变,这样就跟没有key的情况一样了。
18、vue路由守卫有哪些?
路由守卫分为三种 ——分别是:全局路由守卫、组件路由守卫、独享路由守卫。
全局守卫
全局守卫又分为全局前置守卫、和后置守卫
1. router.beforeEach((to,from,next)=>{})
回调函数中的参数,to:进入到哪个路由去,from:从哪个路由离开,next:函数,决定是否展示你要看到的路由页面。
从名字全局前置守卫不难理解,它是全局的,即对 整个单页应用(SPA) 中的所有路由都生效,所以当定义了全局的前置守卫,在进入每一个路由之前都会调用这个回调,那么如果你在回调中对路由的跳转条件判断出错,简单点就是死循环…因为你遗漏了某种路由跳转的情况,守卫会一直执行。所以在使用全局前置守卫的时候一定要判断清楚可能会出现的路由跳转的情况。
2.router.afterEach((to, from) => {})
只有两个参数,to:进入到哪个路由去,from:从哪个路由离。
理解了全局前置守卫,那么全局后置守卫也就那么一回事。全局后置守卫是整个单页应用中每一次路由跳转后都会执行其中的回调。所以多用于路由跳转后的相应页面操作,并不像全局前置守卫那样会在回调中进行页面的重定向或跳转。
组件内的守卫
组件路由守卫分为到达这个组件时,离开这个组件时
1. beforeRouteEnter:(to,from,next)=>{}——到达
to,from参数与上面使用方法一致。next回调函数略有不同。
2.beforeRouteUpdate:(to,from,next)=>{}——更新
3. beforeRouteLeave:(to,from,next)=>{}——离开
点击其他组件时,判断是否确认离开。确认执行next();取消执行next(false),留在当前页面。
路由独享的守卫
1. beforeEnter:(to,from,next)=>{}
与全局路由守卫用法一致,但是只能针对一个页面使用
19、vue常⽤的修饰符
.stop:等统⼀JavaScript中的event.stopPropagation(),防⽌事件冒泡
.prevent:等同于JavaScript中的event。preventDefault(),防⽌执⾏预设的⾏为(如果事件可
取消,则取消该事件,⽽不停⽌事件的进⼀步传播);
.capture:与事件冒泡的⽅向相反,事件捕获由外到内
.self:只会触发⾃⼰范围内的事件,不包含⼦元素;
.once:只会触发⼀次。
20、vue的两个核⼼点
数据驱动:ViewModel,保证数据和视图的⼀致性
组件系统:应⽤类UI可以看做全部是由组件树构成的
21、delete和Vue.delete删除数组的区别
delete只是被删除的元素变成了empty/undefined其他的元素的键值还是不变。
Vue.delete直接删除了数组 改变了数组的键值
22、vue常⽤的UI组件库
Mint UI,element,VUX
23、Vue首页白屏是什么问题引起的?如何解决呢?
24、性能优化篇
七、React
1、react的生命周期
生命周期是什么?
react 实例的生命周期,就是react实例从初始化,更新,到销毁的过程
react实例生命周期经历三个阶段
初始化阶段:完成从react组件创建到首次渲染的过程
更新阶段:当调用setState函数时,会引起组件的重新渲染
销毁阶段:完成组件的销毁
三个阶段分别对应的构造函数有
初始化阶段:
constructor
构造函数里,可以做状态的初始化,接收props的传值
componentWillMount: 在渲染前调用,相当于vue中的beforeMount
render
渲染函数,不要在这里修改数据。 vue中也有render函数。
componentDidMount
渲染完毕,在第一次渲染后调用。
运行中阶段(更新)
当组件的 props 或 state 发生变化时会触发更新(严谨的说,是只要调用了setState()或者改变了props时)。组件更新的生命周期调用顺序如下:
shouldComponentUpdate
是否更新? 需要返回true或者false。如果是false,那么组件就不会继续更新了。
componentWillUpdate
即将更新。
componentWillReceiveProps(nextProps): 在组件接收到一个新的 prop (更新后)时被调用。
这个方法在初始化render时不会被调用。nextProps 是props的新值,而 this.props是旧值。
render
不要在这里修改数据
componentDidUpdate
在组件完成更新后立即调用。在初始化时不会被调用。 相当于vue中的updated
销毁阶段(卸载)
componentWillUnmount()
即将卸载,可以做一些组件相关的清理工作,例如取消计时器、网络请求等
2、为什么虚拟DOM 会提高性能
首先,(虚拟DOM是什么) 虚拟DOM就是一个JavaScript对象。通过这个JavaScript对象来描述真实DOM
如:
{
tagName:"p",
style:"width:200px;height: 100px;",
innerHTML:"我是p"
},
其次,操作虚拟DOM,就是在操作javascript对象,所以,并不会引起页面的重绘和重排。而操作真实DOM是会引起页面的重绘和重排的。
3、React的diff原理
传统diff算法
需要遍历整棵树的节点然后进行比较,是一个深度递归的过程,运算复杂度常常是O(n^3)
react diff的优化策略
DOM节点跨层级的操作不做优化,因为很少这么做,这是针对的tree层级的策略;
对于同一个类的组件,会生成相似的树形结构,对于不同类的组件,生成不同的树形结构,这是针对
conponent层级的策略;
对于同一级的子节点,拥有同层唯一的key值,来做删除、插入、移动的操作,这是针对element层级
的策略;
4、调用setState之后,发生了什么?
合并state
把传入setState()里的参数对象和当前的state进行(属性)合并。 触发调和过程
(Reconciliation)
重新渲染组件
React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个 UI 界面
React 会自动计算出新的树与老树的节点差异(用diff算法),然后根据差异对界面进行最小化重
渲染
5、state和props的区别
state是组件的状态,也叫组件内部的数据
props是组件的属性,也可以认为是外部给组件传入的数据
6、React中的setState是同步执行还是异步执行?如果是异步的?怎么拿到执行后的state?
setState是异步的。
如果要拿到修改后的状态,需要使用回调函数的方式,如下:
//改变状态后想做一些事情:
this.setState({
属性名:属性值
}, () => {
//一般是用于在setState之后做一些操作
//this.state == 修改之后的state
})
7、为什么不能直接用以下办法更新state
this.state.msg = “hello”;
因为,这样不会引起组件的重新渲染,所以,数据修改后没法 呈现在页面上。
而调用setState()函数,会引起组件的重新渲染,这样更新的数据就会呈现在页面上。