前端面试题汇总
目录
- 1.vue-router是怎么传递参数的
- 2.v-if和v-for一起使用的弊端以及解决办法
- 3.beforeDestroyed里面一般进行什么操作
- 4.vue同级组件间怎么传值
- 5.vue中父组件如何获取子组件的属性和方法
- 6.watch和computed的区别
- 7.vue父组件和子组件生命周期的顺序
- 8.vue中父组件能监听到子组件的生命周期吗
- 9.vue中的事件修饰符主要有哪些?分别是什么作用
- 10.介绍下什么是<keep-alive />
- 11.watch能监听到数组的pop行为吗
- 12.watch如何实现深度监听
- 13.vue中如何解决页面不重新渲染问题
- 14. link和@import的区别
- 15. 如何理解js中的原型链
- 16. 怎么理解js中的内存泄露
- 17. 跨域问题
- 18. Vuex:Vue的状态管理模式 + 库
- 19. Vue的生命周期
- 20. Computed和Watch的区别
- 21. Json和Xml数据的区别
- 22. http和https的区别
- 23. Vue-Router 跳转方式
- 24. 什么是闭包?
- 25. 什么是事件代理(事件委托)?有什么好处?
- 26. JS原型,原型链相关知识点
- 27. 组件通信方式有哪些?
- 28. 子组件为什么不可以修改父组件传递的Prop?
- 29. v-model是如何实现双向绑定的?
- 30. Vuex和单纯的全局对象有什么区别?
- 31. 为什么 Vuex 的 mutation 中不能做异步操作?
- 32. vue组件有哪些生命周期钩子?
- 33. Vue 的父组件和子组件生命周期钩子执行顺序是什么?
- 34. v-show 和 v-if 有哪些区别?
- 35. computed 和 watch 有什么区别?
- 36. computed vs methods
- 37. keep-alive 的作用是什么?
- 38. Vue 中 v-html 会导致什么问题
- 39. Vue 的响应式原理
- 40. Object.defineProperty有哪些缺点?
- 41. Vue中如何检测数组变化?
- 42. 组件的 data 为什么要写成函数形式?
- 43. nextTick是做什么用的,其原理是什么?
- 44. Vue 的模板编译原理
- 45. v-for 中 key 的作用是什么?
- 46. 为什么 v-for 和 v-if 不建议用在一起
- 47. Vue-router 导航守卫有哪些
- 48. vue-router hash 模式和 history 模式有什么区别?
- 49. vue-router hash 模式和 history 模式是如何实现的?
- 了解哪些ES6内容
- promise用过哪些方法(then,all,catch)
- all方法是干啥的(记串了,记成了race方法,面试官纠正)
- 低版本浏览器怎么兼容这些ES6的代码
- vue响应式原理
- get和post
- diff算法
- 使用vue遇到的比较难的问题
- 说一下webpack的常用配置
- 用过哪些loader
- 兼容低版本的CSS用什么(autofix)
- babel的了解
- 有没有写过plugin或者loader
- 说一下实践过的性能优化
- 路由懒加载怎么实现的(这个不知道是想问webpack的魔法注释还是啥,我只说了cli自带处理)
- 设计模式有哪些,在实践中使用过哪些(说了几个,然后面试官补充了一个他们做工程的一些例子,但是我一脸懵逼 )
- 深入讲一下项目(凉透了,项目深度不够,流水线式介绍,难点不知道咋表述出来,后面面试官还教我怎么描述一个项目的难点)
1. Vue-Router有两种传参方式
(1)通过在router.js文件中配置path的地方动态传递参数 eg: path: '/detail/:id' 然后在组件内通过this.$route.params.id即可获取
(2).在router-link标签中传递参数
路由跳转传递参数有两种方式:
动态路由方式:
* 在index.js里设置路由处path: '/detail/:iid', (这时候,iid就被当成的该路由对象的params里的一个参数),
之后用的时候, 通过在后面拼接上id来传入iid,eg. this.$router.push('/detail/'+1111111)
* 在跳转后的路由里接收参数的方式:this.$route.params.iid
1. 通过query的方式:this.$router.push({
path: '/detail/',
query: {
id: 111111}
})//这就是使用query传参:给路由传入参数id
然后在跳转到的路由中,再以query的方式接收参数:
this.$route.query.id
2. 通过params的方式:this.$router.push({
name: '/detail/',
params: {
id: 111111}
})//这就是使用params传参:给路由传入参数id
然后在跳转到的路由中,再以qparams的方式接收参数:
this.$route.params.id
注意:
(1) param和query传参的区别:params传参--->只能通过name而不是path来引入路由!!!!
(2) 接受参数是:this.route!!!!!
(3) * route是路由信息对象,包括path,params,hash,query,fullPath,matched,name等路由信息参数
3. 使用vue里的<router-link>标签来传递参数
3.1 使用query:
1) 传值:
<router-link :to="{path:'/login',query:{userId: "33333"}}">
</router-link>
2) 取值: this.$route.query. userId
3.2 使用params:
1) 传值
<router-link :to="{name:'/test',params: {id: id}}">跳转</router-link>
2) 取值:this.$route.params.id
注意:router-link中链接如果是‘/'开始就是从根路由开始,如果开始不带‘/',则从当前路由开始。
也通过this.$route.params获取
注意:这里通过router-link传递参数的方式是隐式传参
label标签作用
label 标签为 input 元素定义标注(标签)。
用于绑定一个表单元素, 当点击label标签的时候, 被绑定的表单元素就会获得输入焦点。当我们鼠标点击 label标签里面的文字时, 光标会定位到指定的表单里面。
如何绑定元素呢?
- 第一种用法就是用label直接包括input表单。
<label> 用户名: <input type="radio" name="usename" value="请输入用户名"> </label>
- 第二种用法 for 属性与表单的id关联,用于规定 label 与哪个表单元素绑定。
<label for="sex">男</label>
<input type="radio" name="sex" id="sex">
v-if与v-show的区别以及使用场景
(1) 手段
- v-if是通过创建和销毁dom元素来控制元素的显示与隐藏;
- v-show是通过设置DOM元素的display样式,block为显示,none为隐藏
(2)编译过程 - v-if切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;
- v-show只是简单的基于css切换;
(3)编译条件: - v-if是惰性的,如果初始条件为假,则什么也不做;只有在条件第一次变为真时才开始局部编译(编译被缓存?编译被缓存后,然后再切换的时候进行局部卸载);
- v-show是在任何条件下(首次条件是否为真)都被编译,然后被缓存,而且DOM元素保留;
(4) 性能消耗: - v-if有更高的切换消耗;
- v-show有更高的初始渲染消耗;
使用场景:
基于以上区别,因此,如果需要非常频繁地切换,则使用 v-show 较好;
如果在运行时条件很少改变,则使用 v-if 较好。
总结:
- v-if判断是否加载,可以减轻服务器的压力,在需要时加载,但有更高的切换开销;
- v-show调整DOM元素的CSS的dispaly属性,可以使客户端操作更加流畅,但有更高的初始渲染开销。
- 如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。
“visibility:hidden”和“display:none”之间的不同:
- “visibility:hidden”将元素设置为不可见,但是同样在布局上占领一定空间(例如,它会被渲染成为空盒子)
- 但是“display:none”的元素是将节点从整个render tree中移除,所以不是布局中的一部分 。
3. V-if和V-for一起使用的弊端以及解决办法
由于v-for的优先级比v-if高,所以导致每循环一次就会去v-if一次,而v-if是通过创建和销毁dom元素来控制元素的显示与隐藏,所以就会不停的去创建和销毁元素,造成页面卡顿,性能下降。
解决办法:在v-for的外层或内层包裹一个元素来使用v-if
render tree、CSSOM tree、DOM tree
CSSOM树和DOM树连接在一起形成一个render tree,渲染树用来计算可见元素的布局并且作为将像素渲染到屏幕上的过程的输入。
- DOM树和CSSOM树连接在一起形成render tree .
- render tree只包含了用于渲染页面的节点
- 布局计算了每一个对象的准确的位置以及大小
- 绘画是最后一步,绘画要求利用render tree来将像素显示到屏幕上
第一步是结合DOM树和CSSOM树形成“render tree”,渲染树用来描述所有可见的DOM内容,并且将CSSOM样式信息附加到节点上。
为了形成渲染树,浏览器大致做的事情有:
- 从DOM树根节点开始,遍历每一个可见的节点
- 一些节点是完全不可见的(比如 script标签,meta标签等),这些节点会被忽略,因为他们不会影响渲染的输出
- 一些节点是通过CSS样式隐藏了,这些节点同样被忽略——例如上例中的span节点在render tree中被忽略,因为span样式是display:none;
- 对每一个可见的节点,找到合适的匹配的CSSOM规则,并且应用样式
- 显示可见节点(节点包括内容和被计算的样式)
总结:
- Render tree的构建其实就是DOM Tree和CSSDOM Attach的过程,在webkit中,解析样式和创建呈现器的过程称为“附加”,每个DOM节点都有一个“attach”方法。
- render tree 其实就是相当于一个计算好样式(CSS)、与HTML对应的Tree
记住“visibility:hidden”和“display:none”之间的不同:
- “visibility:hidden”将元素设置为不可见,但是同样在布局上占领一定空间(例如,它会被渲染成为空盒子);
- 但是“display:none”的元素是将节点从整个render tree中移除,所以不是布局中的一部分 。
DOM树和渲染树的对应关系如下图:
image布局阶段:
我们知道了哪些元素应该被显示以及元素的样式,但是我们还没有计算元素在设备中的确切的位置和大小——这是“布局”阶段,同样也被叫做“reflow”。
所以Render tree创建后,就是开始渲染/布局了(根据render tree进行布局渲染render layer)
- 创建渲染树后,layout根据渲染树中的渲染对象的信息,计算好每一个渲染对象的位置和尺寸
- 将其放在浏览器窗口的正确位置
- (某些时候会在文档布局完成之后进行DOM修改,重新布局的过程就称为回流。)
- 根据计算的布局信息进行绘制:绘制阶段则会遍历呈现树,并调用呈现器的paint方法,将呈现器(display属性)的内容显示在屏幕上,绘制的顺序其实就是元素进入堆栈样式上下文的顺序,例如块呈现器(display:block)的堆栈顺序如下:1. 背景颜色 2. 背景图片 3.边框 4.子代 5. 轮廓
- 最后输出的是一个render包括了屏幕上可见内容的样式信息和内容信息。
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Critial Path: Hello world!</title>
</head>
<body>
<div style="width: 50%">
<div style="width: 50%">Hello world!</div>
</div>
</body>
</html>
上面的页面展示了两个div:第一个div是整个视图的一半,第二个div是父亲宽度的一半——也就是说是整个视图的25%。
布局的输出是“盒子模型”,并且将相对定位转化成屏幕上的绝对像素。
最后,我们只差将render tree上的所有节点转化成屏幕上的确切像素——这个步骤通常被称为“painting”或者“rasterizing”。
每个步骤都要花费一些时间,谷歌浏览器开封工具为我们描述了一些步骤所花费的时间:
- 构造render tree和计算位置以及大小信息被捕捉在时间轴上的“Layout”时间中
- 一旦布局完成,浏览器计算"Paint Setup"和“Paint”事件用来描述render tree转化成屏幕上确切像素的时间。
显示构造render tree以及布局和paint的时间受到页面的大小,被应用的样式和正在运行的设备影响。
页面越大,浏览器将要做更多工作;样式越复杂,painting阶段所花费的时间也越多。
但是,我们的页面完成了!WOOOO!
让我们快速的浏览下浏览器所做的事情:
- 处理HTML标签建立DOM树
- 处理CSS标签建立CSSOM树
- 连接CSSOM树和DOM树形成一个render树
- 在render树上运行布局来计算每个节点的形状
- 在屏幕上画每一个节点
`
3. BeforeDestroy里面一般进行什么操作
beforedestoryed是组件销毁之前执行的一个生命周期,在这个生命周期里,我们可以进行回调函数或定时器的清除,不用的dom元素的清除等
4. Vue同级组件间怎么传值
1.如果是兄弟组件,可通过父元素作为中间组件进行传值 2.通过创建一个bus,进行传值
// 创建一个文件,定义bus中间件,并导出const bus = new Vue()// 在一个组件中发送事件bus.$emit('事件名称', 传递的参数)// 在另一个组件中监听事件bus.$on('事件名称', 得到传过来的参数)
5. Vue中父组件如何获取子组件的属性和方法
vue中通过在子组件上定义ref属性来获取子组件的属性和方法,代码如下:
// 这里是父组件<templete> <child ref="child"/></templete><script>method: { getChild () { this.$refs.child.属性名(方法名) }}</script>
6. Watch和Computed的区别
- watch作用通常是由一个值影响多个值的改变并且能监听到这个值变化时,会去执行一个回调函数,此时我们可以在这个回调函数中做一些逻辑处理
- computed是根据依赖的值衍生出一个新的值,并且依赖的值可以有多个,只有当被依赖的值发生改变时,才会重新去执行计算
7. Vue父组件和子组件生命周期的顺序
1.渲染过程顺序:
父组件beforeCreate() -> 父组件created() -> 父组件beforeMount() -> 子组件beforeCreate() ->子组件created() -> 子组件beforeMount() -> 子组件mounted() -> 父组件mounted()
2.更新过程顺序:
父组件更新过程:
父组件beforeUpdate() -> 父组件updated()
子组件更新过程:
父组件beforeUpdate() -> 子组件beforeUpdate() -> 子组件updated() -> 父组件updated()
3.销毁过程
父组件beforeDestroy() -> 子组件beforeDestroy() -> 子组件destroyed() -> 父组件destroyed()
8. Vue中父组件能监听到子组件的生命周期吗
父组件能够监听到子组件的生命周期,通过@hook:进行监听代码如下:
// 这里是父组件<template> <child @hook:mounted="getChildMounted" /></template><script>method: { getChildMounted () { // 这里可以获取到子组件mounted的信息 }}</script>
9. Vue中的事件修饰符主要有哪些?分别是什么作用
- .stop: 阻止事件冒泡
- .native: 绑定原生事件
- .once: 事件只执行一次
- .self:将事件绑定在自身身上,相当于阻止事件冒泡
- .prevent: 阻止默认事件
- .caption: 用于事件捕获
10.介绍下什么是keep-alive
keep-alive是用于做组件缓存的,只会执行一次,不会被销毁。被keep-alive包裹的组件,没有create和beforeDestroyed等方法,但是有activated和deactivated方法。
11.watch能监听到数组的pop行为吗
对于有返回值的数据,watch就能监听到,比如数组的pop,push, unshift,map等行为。
12. Watch如何实现深度监听
watch: { obj: { handler: function(val) { }, deep: true // 深度监听 }}
13. Vue中如何解决页面不重新渲染问题
(1).修改对象属性后页面未重新渲染可以使用 this.$set(对象名称, '属性名', '属性值')
(2).使用this.$forceUpdate()方法可重新渲染页面
14. link和@import的区别
- 1、link是html的标签,不仅可以加载css还可以定义Rss , rel连接属性;@import是css的语法规则,只能引入样式;
- 2、加载页面时,link是同时加载的,@impor是页面加载完后才加载
- 3、link没有兼容性的问题,而@import只在较高版本的浏览器才可以识别
- 4、link可以通过js插入操作dom,@import 不可以!
15. 如何理解js中的原型链
1;每个构造函数都有一个原型对象2;每个原型对象都有一个指向构造函数的指针3;每个实例函数都有一个指向原型对象的指针。4;查找方式是一层一层查找,直至顶层。Object.prototype
16. 怎么理解js中的内存泄露
定义:程序不需要的内存,由于某些原因其不会返回到操作系统或者可用内存池中。内存泄露会导致(运行缓慢 ,高延迟,崩溃)的问题,常见的导致内存泄露的原因有:
- 1;意外的全局变量
- 2;被遗忘的计时器或回调函数
- 3;脱离文档的DOM的引用
- 4;闭包
17. 跨域问题
由于浏览器的同源策略会导致跨域,同源策略又分为
- 一:DOM同源策略:禁止对不同源页面的DOM进行操作,主要是不同域名的ifram是限制互相访问的
- 二:xmlHttpRequest同源策略:禁止使用XHR对象向不同源的服务器地址发起http请求,只要 "协议:域名:端口" 有一个不同都被当做不同的域之间的请求,即跨域请求
解决方式:
- 1、CORS跨域资源共享 后端需要设置Access--Control-Allow-Credentials:true
- 2、jsonp实现跨域:动态创建script,利用src属性进行跨域
- 3、 nginx代理跨域
- 4、nodejs中间件代理跨域
- 5、WebSokect协跨域
- 6、window.name+ifram跨域
18. Vuex:Vue的状态管理模式 + 库
1、state
*保存vuex中的数据源,通过this.$store.state获取 *
2、getters
用于监听state中的值的变化,返回计算后的结果。getter的返回值会根据它的依赖被缓存起来
3、mutations
是修改store中的值得唯一方式
4、action
官方建议提交一个actions,在actions中提交mutations再去修改状态值。 this.$store.dispatch('add')
//this.$store.commit('add')
5、modules 模块化
19. Vue的生命周期
Vue 实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。通俗说就是 Vue 实例从创建到销毁的过程,就是生命周期。
- beforeCreate: vue元素的挂载元素el和数据都为undefined,还未初始化;
- created:vue实例的数据对象data有了,el还没有;
- beforeMount:vue实例的$el和data都初始化了,但是还挂载在之前的虚拟dom节点上,data.message还未替换;
- mounted:vue实例挂载完成,data.message成功渲染。
- 更新前后:data变化时会触发beforeUpdate和updated方法;
- 销毁前后:beforeDestory和destoryed,在执行destoryed方法后,对data的改变不会触发周期函数,说明vue实例已经解除了事件监听以及dom绑定,但是dom结构依然存在;
vue生命周期的作用:
他的生命周期中有多个事件钩子,让我们控制整个vue实例的过程时更容易形成良好的逻辑。
生命周期钩子的一些使用方法:
- beforeCreate:loading事件,在加载实例时触发。
- created:初始化完成事件,异步请求。
- mounted:挂载元素,获取dom节点
- uptaded:对数据统一处理
- beforeDestory:确认事件停止。
- nextTick:更新数据后立即操作dom。
20. Computed和Watch的区别
computed
计算结果并返回,只有当被计算的属性发生改变时才会触发(即:计算属性的结果会被缓存,除非依赖的响应属性变化才会重新及孙)
watch
监听某一个值,当被监听的值发生变化时,执行相关操作。(与computed的区别是,watch更加适用于监听某一个值得变化,并做对应操作,比如请求后太接口等。而computed适用于计算已有的值并返回结果。)
监听简单数据类型:
data(){ return{ 'first':2 } }, watch:{ first(){ console.log(this.first) } }, ...}
21. Json和Xml数据的区别
1、数据体积方面:xml是重量级的,json是轻量级的,传递的速度更快些。。2、数据传输方面:xml在传输过程中比较占带宽,json占带宽少,易于压缩。3、数据交互方面:json与javascript的交互更加方便,更容易解析处理,更好的进行数据交互4、数据描述方面:json对数据的描述性比xml较差5、xml和json都用在项目交互下,xml多用于做配置文件,json用于数据交互。
22. http和https的区别
Http:超文本传输协议(Http,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。设计Http最初的目的是为了提供一种发布和接收HTML页面的方法。它可以使浏览器更加高效。Http协议是以明文方式发送信息的,如果黑客截取了Web浏览器和服务器之间的传输报文,就可以直接获得其中的信息。
Https:是以安全为目标的Http通道,是Http的安全版。Https的安全基础是SSL。SSL协议位于TCP/IP协议与各种应用层协议之间,为数据通讯提供安全支持。SSL协议可分为两层:SSL记录协议(SSL Record Protocol),它建立在可靠的传输协议(如TCP)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持。SSL握手协议(SSL Handshake Protocol),它建立在SSL记录协议之上,用于在实际的数据传输开始前,通讯双方进行身份认证、协商加密算法、交换加密密钥等。
HTTP与HTTPS的区别
- 1、HTTP是超文本传输协议,信息是明文传输,HTTPS是具有安全性的SSL加密传输协议。
- 2、HTTPS协议需要ca申请证书,一般免费证书少,因而需要一定费用。
- 3、HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样。前者是80,后者是443。
- 4、HTTP连接是无状态的,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,安全性高于HTTP协议。
https的优点
尽管HTTPS并非绝对安全,掌握根证书的机构、掌握加密算法的组织同样可以进行中间人形式的攻击,但HTTPS仍是现行架构下最安全的解决方案,主要有以下几个好处:
- 1)使用HTTPS协议可认证用户和服务器,确保数据发送到正确的客户机和服务器;
- 2)HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全,可防止数据在传输过程中不被窃取、改变,确保数据的完整性。
- 3)HTTPS是现行架构下最安全的解决方案,虽然不是绝对安全,但它大幅增加了中间人攻击的成本。
- 4)谷歌曾在2014年8月份调整搜索引擎算法,并称“比起同等HTTP网站,采用HTTPS加密的网站在搜索结果中的排名将会更高”。
Https的缺点
- 1)Https协议握手阶段比较费时,会使页面的加载时间延长近。
- 2)Https连接缓存不如Http高效,会增加数据开销,甚至已有的安全措施也会因此而受到影响;
- 3)SSL证书通常需要绑定IP,不能在同一IP上绑定多个域名,IPv4资源不可能支撑这个消耗。
- 4)Https协议的加密范围也比较有限。最关键的,SSL证书的信用链体系并不安全,特别是在某些国家可以控制CA根证书的情况下,中间人攻击一
23. Vue-Router 跳转方式
1. this.$router.push
//跳转到不同的url,但这个方法会向history栈添加一个记录,点击后退会返回到上一个页面。this.$router.push({path: '/home/sort/detail',query:{id: 'abc'}}) //获取参数 {{this.$route.query.userId}}//this.$router.push({name: 'detail',params:{id: 'abc'}})获取参数:{{this.$route.params.userId}}
2. query和params 的区别
a) 用法上,query要用path来引入,params要用name来引入:eg
this.$router.push({ name:"detail", params:{ name:'nameValue', code:10011 }});
b) 展示上,query更加类似于我们ajax中get传参,params则类似于post,说的再简单一点,前者在浏览器地址栏中显示参数,后者则不显示
3. this.$router.replace
同样是跳转到指定的url,但是这个方法不会向history里面添加新的记录,点击返回,会跳转到上上一个页面。上一个记录是不存在的。
4. this.$router.go
相对于当前页面向前或向后跳转多少个页面,类似 window.history.go(n)。n可为正数可为负数。正数返回上一个页面
5. router-link标签使用
- 根据路由路径(/home/sort/detail)跳转 <router-link :to="{path: '/home/sort/detail', query:{id: 'abc'}}">点击查看子页面</router-link>
- 根据路由名称(detail)跳转 <router-link :to="{name: 'detail', params:{id: 'abc'}}">点击查看子页面</router-link> :to="" 可以实现绑定动态的 路由 和 参数
24. 什么是闭包?
闭包是指有权访问其他函数所定义的变量的函数。
使用闭包的话只需要将一个函数定义在另一个函数的内部,并将它给暴露出来,比如可以直接返回或者给他传递给其他函数,由于内部函数对外部函数变量还存在着应用,所以在外部函数执行完毕后不会主动将该内存销毁。
闭包最重要的用途是实现对象的私有数据。 对象的内部数据无法被轻易的修改,保证了程序的安全性。
但同时大量的使用闭包会导致内存泄漏。
25. 什么是事件代理(事件委托)?有什么好处?
JS高级程序上的定义:利用事件冒泡,只绑定一个函数就可以处理一类事件。
在JS中,添加到页面上的事件数量直接关乎着页面的性能,多次与dom发生交互访问dom会导致页面重绘和重排,导致拖慢整个页面,这就是为什么优化里中的减少dom操作的原因。而且,每个函数都是一个对象,对象就要占据内存,内存大了性能自然就慢了。
事件委托的实现原理就是利用事件冒泡。
冒泡的顺序 :
- 目标元素
- dom结构 直至 body
- body
- html
- document
- windon
事件代理的好处:
- 优化性能
- 当新元素绑添加进来的时候不需要再次绑定事件,通过冒泡就可以触发
26. JS原型,原型链相关知识点
在JS中,每创建出来一个函数js就会默认创建一个prototype的属性,这个属性指向函数的原型对象。
在原型对象里面有一个属性constructor属性指向了构造函数。
每个对象都有一个隐式原型 proto ,指向的是构造该对象的构造函数的原型对象。
原型对象也是对象,所以如果我们让原型对象指向另外的一个实例,这个实例也有自己的原型对象,如果这个实例的原型对象又等于了另外的实例,一层层下去就形成了一个链条。这就是所谓的原型链。
函数也是对象,所以函数也有隐式原型,函数的隐式原型为Function.prototype
Function.prototype的隐式原型为Object.prototype
Object.prototype 的隐式原型为null 这里也就是原型链的顶端
image组件通信相关问题
27. 组件通信方式有哪些?
父子组件通信:
props
和 event
、v-model
、 .sync
、 ref
、 $parent
和 $children
非父子组件通信:
$attr
和 $listeners
、 provide
和 inject
、eventbus
、通过根实例$root
访问、vuex
、dispatch
和 brodcast
通信方式属于较基础的面试题,具体的可参考我的文章—— vue 组件通信看这篇就够了
28. 子组件为什么不可以修改父组件传递的Prop?
Vue提倡单向数据流,即父级 props
的更新会流向子组件,但是反过来则不行。这是为了防止意外的改变父组件状态,使得应用的数据流变得难以理解。如果破坏了单向数据流,当应用复杂时,debug 的成本会非常高。
29. v-model是如何实现双向绑定的?
v-model
是用来在表单控件或者组件上创建双向绑定的,他的本质是 v-bind
和 v-on
的语法糖,在一个组件上使用 v-model
,默认会为组件绑定名为 value
的 prop
和名为 input
的事件。
文章—— vue 组件通信看这篇就够了 中也有其详细介绍
30. Vuex和单纯的全局对象有什么区别?
Vuex和全局对象主要有两大区别:
- Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
- 不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
31. 为什么 Vuex 的 mutation 中不能做异步操作?
Vuex中所有的状态更新的唯一途径都是mutation,异步操作通过 Action 来提交 mutation实现,这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
每个mutation执行完成后都会对应到一个新的状态变更,这样devtools就可以打个快照存下来,然后就可以实现 time-travel 了。如果mutation支持异步操作,就没有办法知道状态是何时更新的,无法很好的进行状态的追踪,给调试带来困难。 参考尤大大回答: www.zhihu.com/question/48…
生命周期相关问题
32. vue组件有哪些生命周期钩子?
beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed。
<keep-alive>
有自己独立的钩子函数 activated 和 deactivated。
33. Vue 的父组件和子组件生命周期钩子执行顺序是什么?
渲染过程:
父组件挂载完成一定是等子组件都挂载完成后,才算是父组件挂载完,所以父组件的mounted在子组件mouted之后
父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted
子组件更新过程:
- 影响到父组件: 父beforeUpdate -> 子beforeUpdate->子updated -> 父updted
- 不影响父组件: 子beforeUpdate -> 子updated
父组件更新过程:
- 影响到子组件: 父beforeUpdate -> 子beforeUpdate->子updated -> 父updted
- 不影响子组件: 父beforeUpdate -> 父updated
销毁过程:
父beforeDestroy -> 子beforeDestroy -> 子destroyed -> 父destroyed
看起来很多好像很难记忆,其实只要理解了,不管是哪种情况,都一定是父组件等待子组件完成后,才会执行自己对应完成的钩子,就可以很容易记住。
相关属性的作用 & 相似属性对比
34. v-show 和 v-if 有哪些区别?
v-if
会在切换过程中对条件块的事件监听器和子组件进行销毁和重建,如果初始条件是false,则什么都不做,直到条件第一次为true时才开始渲染模块。
v-show
只是基于css进行切换,不管初始条件是什么,都会渲染。
所以,v-if
切换的开销更大,而 v-show
初始化渲染开销更大,在需要频繁切换,或者切换的部分dom很复杂时,使用 v-show
更合适。渲染后很少切换的则使用 v-if
更合适。
35. computed 和 watch 有什么区别?
computed
计算属性,是依赖其他属性的计算值,并且有缓存,只有当依赖的值变化时才会更新。
watch
是在监听的属性发生变化时,在回调中执行一些逻辑。
所以,computed
适合在模板渲染中,某个值是依赖了其他的响应式对象甚至是计算属性计算而来,而 watch
适合监听某个值的变化去完成一段复杂的业务逻辑。
36. computed vs methods
计算属性是基于他们的响应式依赖进行缓存的,只有在依赖发生变化时,才会计算求值,而使用 methods
,每次都会执行相应的方法。
37. keep-alive 的作用是什么?
keep-alive
可以在组件切换时,保存其包裹的组件的状态,使其不被销毁,防止多次渲染。
其拥有两个独立的生命周期钩子函数 actived 和 deactived,使用 keep-alive
包裹的组件在切换时不会被销毁,而是缓存到内存中并执行 deactived 钩子函数,命中缓存渲染后会执行 actived 钩子函数。
38. Vue 中 v-html 会导致什么问题
在网站上动态渲染任意 HTML,很容易导致 XSS 攻击。所以只能在可信内容上使用 v-html,且永远不能用于用户提交的内容上。
原理分析相关题目
这部分的面试题,只看答案部分是不够的,最好结合源码来分析,可以有更深的理解。我在之前的文章对某些源码做过分析的,会给出链接。
39. Vue 的响应式原理
如果面试被问到这个问题,又描述不清楚,可以直接画出 Vue 官方文档的这个图,对着图来解释效果会更好。
Vue 的响应式是通过 Object.defineProperty
对数据进行劫持,并结合观察者模式实现。 Vue 利用 Object.defineProperty
创建一个 observe
来劫持监听所有的属性,把这些属性全部转为 getter
和 setter
。Vue 中每个组件实例都会对应一个 watcher
实例,它会在组件渲染的过程中把使用过的数据属性通过 getter
收集为依赖。之后当依赖项的 setter
触发时,会通知 watcher
,从而使它关联的组件重新渲染。
40. Object.defineProperty有哪些缺点?
这道题目也可以问成 “为什么vue3.0使用proxy实现响应式?” 其实都是对Object.defineProperty 和 proxy实现响应式的对比。
-
Object.defineProperty
只能劫持对象的属性,而Proxy
是直接代理对象
由于Object.defineProperty
只能对属性进行劫持,需要遍历对象的每个属性。而Proxy
可以直接代理对象。 -
Object.defineProperty
对新增属性需要手动进行Observe
, 由于Object.defineProperty
劫持的是对象的属性,所以新增属性时,需要重新遍历对象,对其新增属性再使用Object.defineProperty
进行劫持。 也正是因为这个原因,使用 Vue 给data
中的数组或对象新增属性时,需要使用vm.$set
才能保证新增的属性也是响应式的。 -
Proxy
支持13种拦截操作,这是defineProperty
所不具有的。 - 新标准性能红利
Proxy
作为新标准,长远来看,JS引擎会继续优化Proxy
,但getter
和setter
基本不会再有针对性优化。 -
Proxy
兼容性差 目前并没有一个完整支持Proxy
所有拦截方法的Polyfill方案
更详细的对比,可以查看我的文章 为什么Vue3.0不再使用defineProperty实现数据监听?
41. Vue中如何检测数组变化?
Vue 的 Observer
对数组做了单独的处理,对数组的方法进行编译,并赋值给数组属性的 __proto__
属性上,因为原型链的机制,找到对应的方法就不会继续往上找了。编译方法中会对一些会增加索引的方法(push
,unshift
,splice
)进行手动 observe。 具体同样可以参考我的这篇文章 为什么Vue3.0不再使用defineProperty实现数据监听?,里面有详细的源码分析。
42. 组件的 data 为什么要写成函数形式?
Vue 的组件都是可复用的,一个组件创建好后,可以在多个地方复用,而不管复用多少次,组件内的 data
都应该是相互隔离,互不影响的,所以组件每复用一次,data
就应该复用一次,每一处复用组件的 data
改变应该对其他复用组件的数据不影响。
为了实现这样的效果,data
就不能是单纯的对象,而是以一个函数返回值的形式,所以每个组件实例可以维护独立的数据拷贝,不会相互影响。
43. nextTick是做什么用的,其原理是什么?
能回答清楚这道问题的前提,是清楚 EventLoop 过程。
在下次 DOM 更新循环结束后执行延迟回调,在修改数据之后立即使用 nextTick
来获取更新后的 DOM。
nextTick
对于 micro task 的实现,会先检测是否支持 Promise
,不支持的话,直接指向 macro task,而 macro task 的实现,优先检测是否支持 setImmediate
(高版本IE和Etage支持),不支持的再去检测是否支持 MessageChannel,如果仍不支持,最终降级为 setTimeout
0;
默认的情况,会先以 micro task
方式执行,因为 micro task 可以在一次 tick 中全部执行完毕,在一些有重绘和动画的场景有更好的性能。
但是由于 micro task 优先级较高,在某些情况下,可能会在事件冒泡过程中触发,导致一些问题(可以参考 Vue 这个 issue:github.com/vuejs/vue/i…),所以有些地方会强制使用 macro task (如 v-on
)。
注意:之所以将 nextTick
的回调函数放入到数组中一次性执行,而不是直接在 nextTick
中执行回调函数,是为了保证在同一个tick内多次执行了 nextTcik
,不会开启多个异步任务,而是把这些异步任务都压成一个同步任务,在下一个tick内执行完毕。
44. Vue 的模板编译原理
vue模板的编译过程分为3个阶段:
- 第一步:解析
将模板字符串解析生成 AST,生成的AST 元素节点总共有 3 种类型,1 为普通元素, 2 为表达式,3为纯文本。 - 第二步:优化语法树
Vue 模板中并不是所有数据都是响应式的,有很多数据是首次渲染后就永远不会变化的,那么这部分数据生成的 DOM 也不会变化,我们可以在 patch 的过程跳过对他们的比对。
此阶段会深度遍历生成的 AST 树,检测它的每一颗子树是不是静态节点,如果是静态节点则它们生成 DOM 永远不需要改变,这对运行时对模板的更新起到极大的优化作用。
- 生成代码
const code = generate(ast, options)复制代码
通过 generate
方法,将ast生成 render
函数。 更多关于 AST,Vue 模板编译原理,以及和 AST 相关的 Babel 工作原理等,我在 掌握了AST,再也不怕被问babel,vue编译,Prettier等原理 中做了详细介绍。
45. v-for 中 key 的作用是什么?
清晰回答这道问题,需要先清楚 Vue 的 diff 过程,关于 diff 原理,推荐一篇文章 my.oschina.net/u/3060934/b…
key
是给每个 vnode
指定的唯一 id
,在同级的 vnode
diff 过程中,可以根据 key
快速的对比,来判断是否为相同节点,并且利用 key
的唯一性可以生成 map
来更快的获取相应的节点。
另外指定 key
后,就不再采用“就地复用”策略了,可以保证渲染的准确性。
46. 为什么 v-for 和 v-if 不建议用在一起
当 v-for
和 v-if
处于同一个节点时,v-for
的优先级比 v-if
更高,这意味着 v-if
将分别重复运行于每个 v-for
循环中。如果要遍历的数组很大,而真正要展示的数据很少时,这将造成很大的性能浪费。
这种场景建议使用 computed
,先对数据进行过滤。
路由相关问题
47. Vue-router 导航守卫有哪些
全局前置/钩子:beforeEach、beforeResolve、afterEach
路由独享的守卫:beforeEnter
组件内的守卫:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave
完整的导航解析流程如下:
- 导航被触发。
- 在失活的组件里调用离开守卫。
- 调用全局的 beforeEach 守卫。
- 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
- 在路由配置里调用 beforeEnter。
- 解析异步路由组件。
- 在被激活的组件里调用 beforeRouteEnter。
- 调用全局的 beforeResolve 守卫 (2.5+)。
- 导航被确认。
- 调用全局的 afterEach 钩子。
- 触发 DOM 更新。
- 用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。
48. vue-router hash 模式和 history 模式有什么区别?
区别:
- url 展示上,hash 模式有“#”,history 模式没有
- 刷新页面时,hash 模式可以正常加载到 hash 值对应的页面,而 history 没有处理的话,会返回 404,一般需要后端将所有页面都配置重定向到首页路由。
- 兼容性。hash 可以支持低版本浏览器和 IE。
49. vue-router hash 模式和 history 模式是如何实现的?
-
hash 模式:
后面 hash 值的变化,不会导致浏览器向服务器发出请求,浏览器不发出请求,就不会刷新页面。同时通过监听 hashchange 事件可以知道 hash 发生了哪些变化,然后根据 hash 变化来实现更新页面部分内容的操作。 -
history 模式:
history 模式的实现,主要是 HTML5 标准发布的两个 API,pushState
和replaceState
,这两个 API 可以在改变 url,但是不会发送请求。这样就可以监听 url 变化来实现更新页面部分内容的操作。
50. 对 MVVM 的理解
MVVM分为Model、View、ViewModel三者。
- Model:代表数据模型
- View:代表视图
- ViewModel:连接视图和模型,实现数据的双向绑定
51. Vue 实现数据双向绑定的原理
采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。当把一个普通的 JavaScript 对象传给 Vue 作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 将它们转化为 getter/setter,用户看不到getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。
1、实现一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者
2、实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
3、实现一个Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图
52. 对 webpack 的了解
前端打包工具
53. 对前端工程化的理解
54. 对模块化,组件化的理解
55. 使用 vue-lic 搭建一个 Vue 的项目
56. 对 npm 的了解
第三方库的管理工具
57. ES6 提出了哪些新概念
> 1.var let
> 2.箭头函数
> 3.模板字符串(反引号)
> 4.解构赋值
> 5.import,export
> 6.class
> …
58. HTTP 常见的状态码
> 400 客户端请求有语法错误,不能被服务器所理解
> 403 服务器收到请求但是拒绝提供服务|
> 200 客户端请求成功|
> 404 请求资源不存在 eg:输入错误的URL |
> 500 服务器发生不可预期的错误 |
> 503 服务器当前不能处理客户端请求,一段时间后可能恢复正常
59. 数组的常用方法,及使用
60. let ,var ,const 之间的区别
> var声明变量可以重复声明,而let不可以重复声明
> var是不受限于块级的,而let是受限于块级
> var会与window相映射(会挂一个属性),而let不与window相映射
> var可以在声明的上面访问变量,而let有暂存死区,在声明的上面访问变量会报错
> const声明之后必须赋值,否则会报错
> const定义不可变的量,改变了就会报错
> const和let一样不会与window相映射、支持块级作用域、在声明的上面访问变量会报错
61. 跨域问题 如何解决
> jsonp
62. 如何解决浏览器的兼容问题
63. 对于 数据安全 的措施
> md5,对称加密,非对称加密
64. Vue 全家桶——vue,vue-router,vuex
65. vuex 的五个核心属性
> state,getter,mutations,actions,modules
66. v-show 和 v-if 的区别
67. 父子组件的传值
> 父组件传值给子组件:
> 第一步:在父组件中 v-bind:parentmsg=“父组件的data值”(parentmsg 这个名字可自定义)
> 第二步:在子组件中 props:[‘parentmsg’]
> {{parentmsg}}这样就可以使用 父组件的值
> 父组件把方法传递给子组件:
> 第一步:在父类组件中 @func=父组件方法名 (方法名不带‘()’,func可自定义)
> 第二步:在子组件方法中写 this.$emit(‘func’,方法参数) (func可自定义,无参方法‘方法参数’就别写)
68. 路由跳转的方式
> 1.`<router-link to='home'> router-link`
> 2\. `router.push('/home')`
69. Vue 实例的生命周期
70. vue-router 中 hash 模式和 history 模式的区别
> 最直观的区别是在 url 中 hash 带了一个 # 而 history 是没有的;
> history 需要后端提供支持;
> history 跳转后刷新或者回跳会报错
71. 对 ui 框架的使用
> element-ui,iview,ionic
72. angular 和 vue 的对比
73. 原生 Ajax 如何发送请求
74. 闭包的概念
> 函数嵌套函数,内部函数引用外部函数变量,外部函数调用之后,其对象本该被销毁,但闭包的存在,使我们仍然可访问外部函数的变量对象,导致垃圾回收机制没有生效,变量被保存下来,内存泄漏。
75. Vue 中发送请求的方式
> vue-router
76. axios 怎样发送请求
77. 前端缓存
> cookie, WebStorage
78. CSS 常用的布局
79. 你用过的 CSS 框架
> less
80. 对单向数据流的理解
81. 计算属性(computed)和 watch 的区别
82. 回调函数
83. Promise 三种状态
> pending、fulfilled、rejected(未决定,履行,拒绝),同一时间只能存在一种状态,且状态一旦改变就不能再变。promise是一个构造函数,promise对象代表一项有两种可能结果(成功或失败)的任务,它还持有多个回调,出现不同结果时分别发出相应回调。
> 1.初始化,状态:pending
> 2.当调用resolve(成功),状态:pengding=>fulfilled
> 3.当调用reject(失败),状态:pending=>rejected
84. 同步和异步的概念
85. Get 请求和 Post 请求的区别
> 1. Get是不安全的,因为在传输过程,数据被放在请求的URL中;Post的所有操作对用户来说都是不可见的。
> 2 Get传送的数据量较小,这主要是因为受URL长度限制;Post传送的数据量较大,一般被默认为不受限制。
> 3.Get限制Form表单的数据集的值必须为ASCII字符;而Post支持整个ISO10646字符集。
> 4\. Get执行效率却比Post方法好。Get是form提交的默认方法。
86. 自定义过滤器
87. 如何用原生js给一个按钮绑定多个 onclick 事件
88. VUE如果注册全局组件
89. 列举css 选择器及优先级
90. 权重计算规则
91. 阐述清除浮动的几种方式及优缺点
92. 简述 cookies sessionStorage 和 localStorage 区别
93. 数组去重
94. mvvm,mvc 原理及区别
以下是笔试题
在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述