2022年中旬面试高频题及个人总结答案
开源
个人开源的leno-admin
后台管理项目,前端技术栈:reactHooks
、ant-design
;后端技术栈:koa
、mysql
、redis
,整个项目包含web端
、electron客户端
、mob移动端
、template基础模板
,能够满足你快速开发一整套后台管理项目;如果你觉得不错,就为作者点个✨star
✨吧,你的支持就是对我最大的鼓励;
前言
这是我个人这一段时间的面试遇到的高频题进行了一个总结,因为我个人主要使用的vue技术栈,所以题目都是有关于vue与js的,里面也有些错误,因为个人技术有限,所以以下题目仅供参考,如果有用的话,希望可以给一个收藏和点赞呀~!
1、在项目中做过哪些优化(webpack打包优化)?
1、搜索框的防抖设置:
- 减少对后台的数据请求次数,达到优化的效果;
2、设置节流阀:
- 以防止因为用户快速连续点击造成js逻辑业务反复的触发,消耗性能;
3、Vue中v-if与v-show区分使用场景:
- 因为v-if在切换过程,会销毁组件,引起浏览器的重排与重绘,比较消耗性能;v-show则只是简单的改变css的display来达到隐藏显示的效果,只是引起浏览器的重绘,相比较v-if来说,节约浏览器性能;
4、减少data中的数据监管:
- 因为Vue底层是数据驱动dom元素,会对data中的数据用 Object.defineProperty 进行数据劫持 ,占用内存,所以在代码中可以尽量减少data中的数据挟持;
5、在v-for遍历的时候添加唯一的标识key:
为v-for中的key添加唯一的标识key;因为Vue中的diff算法,会对新旧数据进行对比,是通过key的标识,进行对比的,未修改的数据进行保留,修改了的数据重新插入到虚拟dom中,最终重新渲染,标识能够减少dom的重排与重绘;
6、长列表性能优化:
因为Vue会通过 Object.defineProperty 对数据进行劫持 ,来实现视图响应数据的变化 ,有时候我们的组件就是纯粹的数据展示,不会有任何的改变,这时候我们可以不需要Vue劫持我们的数据;可以通过 Object.freeze 方法来冻结一个对象,一旦被冻结的对象就再也不能被修改了,这样能够节省内存 。
7、事件的销毁:
Vue 组件销毁时,会自动清理它与其它实例的连接,解绑它的全部指令及事件监听器,但是仅限于组件本身的事件; 如果在 js 内使用 addEventListene 等方式,vue是不会自动销毁的,我们需要在组件销毁时手动移除这些事件的监听 ;
created() {
addEventListener('click', this.click, false)
},
beforeDestroy() {
removeEventListener('click', this.click, false)
}
8、图片资源懒加载:
对于图片过多的页面,为了加速页面加载速度,所以很多时候我们需要将页面内未出现在可视区域内的图片先不做加载, 等到滚动到可视区域后再去加载。这样对于页面加载性能上会有很大的提升,也提高了用户体验。我们在项目中使用 Vue 的 vue-lazyload 插件:
9、路由懒加载:
Vue 是单页面应用,可能会有很多的路由引入 ,这样使用 webpcak 打包后的文件很大,当进入首页时,加载的资源过多,页面会出现白屏的情况,不利于用户体验。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应的组件,这样就更加高效了。这样会大大提高首屏显示的速度,但是可能其他的页面的速度就会降下来。
const Foo = () => import('./Foo.vue')
const router = new VueRouter({
routes: [
{ path: '/foo', component: Foo }
]
})
10、第三方插件按需引入:
我们在项目中经常会需要引入第三方插件,如果我们直接引入整个插件,会导致项目的体积太大,我们可以借助 babel-plugin-component
,然后可以只引入需要的组件,以达到减小项目体积的目的。
npm install babel-plugin-component -D
11、使用 Chrome Performance 查找性能瓶颈:
- 打开 Chrome 开发者工具,切换到 Performance 面板;
- 点击 Record 开始录制;
- 刷新页面或展开某个节点;
- 点击 Stop 停止录制;
12、浏览器缓存:
为了提高用户加载页面的速度,对静态资源进行缓存是非常必要的;
13、提取公共代码:
所以我们需要将多个页面的公共代码抽离成单独的文件,来优化以上问题 。
14、webpack对图片进行压缩:
在 vue 项目中除了可以在 webpack.base.config.js
中 url-loader 中设置 limit 大小来对图片处理,对小于 limit 的图片转化为 base64 格式,其余的不做操作。所以对有些较大的图片资源,在请求资源的时候,加载会很慢,我们可以用 image-webpack-loader
来压缩图片;
15、CDN在线引入第三方插件包资源
2、TCP三次握手,TCP四次握手(挥手)?
TCP三次握手: 是客户端向服务端请求数据需要经过三次;
第一次握手:客户端向服务器进行通信,需要发送连接请求信号;
第二次握手:服务端收到客户端的请求连接,会给客户端发一个确认信息;
第三次握手: 客户端收到服务端的确认连接信息后,会再发送一个信息告知服务端开始联通;
四次握手则是: 四次握手也叫作四次挥手;
第一次挥手:就是客户端数据请求完毕,接下来需要断开通信连接,所以会通知服务器我请求完毕;
第二次挥手:服务端接收到了客户端的请求,便向客户端发送ACK确认;
第三次挥手:客户端即接收到确认信息,并处于等待结束的状态;服务端ACK发送完后,自身便处于等待关闭连接的状态,便再次对客户端发送断开信息(FIN);
第四次挥手:客户端接收到最终确认断开的信息后,便告诉服务端(ACK),因为连接和断开都需要双方操作才可以;客户端在第四次信息发送完毕之后,会给自己设置定时器,在默认时间之后关闭连接,而服务端一旦接收到客户端发过来的信息后会立即关闭连接;
3、Vue源码怎么实现双向绑定?
-
vue2.0:Object.defineProperty() + 发布订阅模式实现( v-bind +监听事件)
- Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
- Vue 内部通过
Object.defineProperty
方法属性拦截的方式,把data
对象里每个数据的读写转化成getter
/setter
,当数据变化时通知视图更新。 - Vue 双向绑定,使用数据劫持和发布订阅模式实现的 ;重点时数据改变视图,视图改变数据dom操作都可完成(如:input事件,change事件);
4、浏览器从输入url到页面渲染完毕这一过程都做了什么?
三个过程:
1- DNS解析URL的过程;
- DNS解析过程就是寻找哪个服务器上有资源,因为一般我们使用的都是URL域名地址,而不是ip地址(ip地址比较难记),DNS解析就是将域名翻译成IP地址的过程;
2- 浏览器发送请求与服务器交互的过程;
- 首先是浏览器利用tcp协议通过三次握手与服务器建立起连接;
- 浏览器根据解析的IP地址和端口号发起http的get请求;
- 服务器接收到http请求后,便开始寻找相应的资源,找到后利用http返回响应报文;
- 若状态码显示为200表示响应成功,浏览器接收资源,便开始进行页面渲染;
3- 浏览器对接收到html页面渲染的过程;
- 浏览器深度遍历资源数据,把html的节点遍历成dom树;
- 将css解析成css ,生成样式规则;
- 将dom树和CSS 生样式规则,渲染生成render树(render树是根据可视化节点和css样式表结合诞生出来的树);
- JS根据得到的render树 计算所有的节点在屏幕中的位置,进行布局(重排);
- 遍历render树并调用硬件开始绘制渲染所有的节点(重绘);
5、重排(回流)与重绘?
重排:浏览器的重排,是改动了网页的html中的dom节点时,浏览器会重新渲染这一块的html的dom结构,如果频繁的触发重排的话,会非常消耗浏览器的性能;改动重排的话,一定会触发重绘;
重排优化方法:
-
减少重排的范围:1、减少使用table布局,因为table一个小的改动会造整个table重新布局;2、减少通过父元素影响子元素;
-
减少重排的次数:
-
样式集中改变,不要频繁操作样式;
-
分离读写操作;
-
将DOM离线:
- 例子:我们给一个大的元素设置display:none,只触发一次重排重绘,在做出足够多的变更之后,再通过display:block显示,这种方式即使大量变更也只会触发两次重排;
- visibility:hidden的元素对重绘有影响,不影响重排;
-
使用absolute或者fixed脱离文档流;
-
优化动画:
- 把动画效果应用到absolute和fixed上;
- 启用GPU硬件加速:比如多用transitions,transform,Canvas2D;
-
重绘:重绘,是某些元素的外观被改变了,浏览器会重新渲染css样式,相比较重排来说,重绘是比较节省浏览器的性能的。
6、Css3有哪些新的属性?
- grid栅格布局,最新的css布局方式,将网页分割成网格状,可以进行x与y轴两个方向布局,常用的flex布局,仅仅只能在x轴一个方向布局;
- boder-color 边框颜色设置;
- text-shadow 文本阴影
- text-overflow 超出文本显示省略号
- 伪元素选择器 :before和after;
- border-radius 圆角;
- box-shadow 边框阴影,和文本阴影类似 ;
- transition过渡属性;
- animation动画属性;
- transform2d或3d转换;
- 渐变
- flex弹性布局;
- 媒体查询;
7、讲一下登录的实现过程(身份验证实现)?token失效怎么办?
登录功能的过程:
主要原因:因为http是一种无状态的协议。客户端每次请求时,都需要与服务器建立连接,在完成请求后又会断开这个连接;优点:能节省传输时占用的连接资源;缺点:每次请求都是独立的,服务器无法判断本次和上次是否来自同一个用户,无法判断用户的登录状态。
Token登录
Token 是服务端生成的一串字符串,以作为客户端请求的一个令牌。当第一次登录后,服务器会生成一个 Token 并返回给客户端,客户端后续访问时,只需带上这个 Token 即可完成身份认证。流程机制:
- 用户输入账号密码,并点击登录。
- 服务器端验证账号密码无误,创建 Token。
- 服务器端将 Token 返回给客户端,由客户端自由保存。
-
特点:
- 服务器端不需要存放 Token,所以不会对服务器端造成压力,即使是服务器集群,也不需要增加维护成本。
- Token 可以存放在前端任何地方,可以不用保存在 Cookie 中,提升了页面的安全性。
- Token 下发之后,只要在生效时间之内,就一直有效,如果服务器端想收回此 Token 的权限,并不容易。
-
Token的生成方式:JWT是核心
- 最常见的 Token 生成方式是使用 JWT(Json Web Token),它是一种简洁的,自包含的方法用于通信双方之间以 JSON 对象的形式安全的传递信息。
- JWT 算法主要分为 3 个部分:header(头信息),playload(消息体),signature(签名)。
token失效怎么办:
- 第一种方案是:服务器端保存token状态,用户每次操作都会自动推迟token的过期时间,session就是采用这种策略保持token的有效期,但是当前后端分离,单页面的时候,每秒钟的请求发起多次,每次都去刷新一下过期时间会非常消耗性能的;
- 第二种方案:使用refresh token,避免频繁的刷新token,此时服务端只要在token过期的时候反馈给前端,前端使用refresh token申请一个全新的token继续使用即可(refresh token可以设置过期时间)
8、vue生命周期有哪些,分别有什么区别?
- vue2.0生命周期:
- beforeCreate : 在vc实例还未创建之前便调用;
- created:在创建vc实例后触发,一般初始化渲染页面请求数据都是在这个钩子处触发;
- beforeMounte:vue中虚拟dom数据对比完成后,将虚拟dom上树之前触发;
- mounted:在虚拟dom上树后触发;
- beforeUpdate:在数据修改之前触发;
- updated:在数据修改之后触发;
- beforeDestroy:组件销毁之前调用;
- destroyed:组件销毁之后调用;
-
vue3.0生命周期:
-
beforeCreate
===>setup()
-
created
=======>setup()
-
beforeMount
===>onBeforeMount
-
mounted
=======>onMounted
-
beforeUpdate
===>onBeforeUpdate
-
updated
=======>onUpdated
-
beforeUnmount
==>onBeforeUnmount
-
unmounted
=====>onUnmounted
-
9、vue中v-for中的key有什么作用?
- key的作用主要是为了高效的更新虚拟DOM,他的原理是vue在diff对比的过程中通过key可以精准判断两个节点是否是同一个,从而避免频繁更新相同元素,使整个diff对比的过程更加高效,减少dom操作量,提高性能;
10、说一下对虚拟dom的理解?
虚拟DOM是什么:
- 虚拟DOM(virtual DOM),使用JS模拟的DOM结构,将DOM变化的对比放在JS层面来做,其实虚拟DOM就是JS对象;
11、跨域的问题是什么原因引起的,如何解决?
引起的原因:因为浏览器的同源策略导致的;
首先什么是跨域:
- 浏览器规定了,相同协议,相同地址,相同端口的为同源,可以直接连接,如果这三个有一个不一样就是跨域,会触发浏览器的保护机制;
解决:
- jsonp解决办法,只能发送get请求方法,因为jsonp是利用script的src属性来完成跨域的;
- crows解决方法,这是现在主流的跨域解决办法,是后端处安装第三方crows包解决跨域问题;
vue中开发环境跨域解决:
- vue2.0:
proxyTable: { // -- 修改vue.config文件夹下的index.js文件,在proxyTable中加上如下代码:
'/apis': {
target: 'http://localhost:8080/', //要解决跨域的接口的域名
secure:false, //如果是https接口,需要配置这个参数
changeOrigin: true, // 如果接口跨域,需要进行这个参数配置
pathRewrite: {
'^/apis': '' // 路径重写
}
},
},
// 然后在请求中axios中这样写 =>
axios.get('apis/getData')
.then(res => {
console.log(res)
})
.catch(err => {
console.error(err);
})
- vue3.0:
// 新建一个vue.config.js,配置以下信息:
module.exports = {
devServer: {
proxy: {
'^/api': {
target: 'http://localhost:8080/',//接口的前缀
ws:true,//代理websocked
changeOrigin:true,//虚拟的站点需要更管origin
pathRewrite:{
'^/api':''//重写路径
}
}
}
}
}
12、闭包是什么?闭包有什么优缺点,如何清理内存?
手写有一个闭包:
function a() {
let i = 0
function b() {
return i++
}
return b
}
let x
x = a()
console.log(x())
console.log(x())
console.log(x())
闭包是什么?
- 函数嵌套函数,内部的函数就是闭包,并且闭包不会被js垃圾回收机制回收;
- 闭包是js为了解决全局变量污染性问题的解决方案,能够很好的提升函数内数据的独立性;
闭包优点:
- 闭包能够延伸变量的作用范围;
- 闭包可以避免全局变量的污染;
闭包的缺点:
- 因为闭包产生的变量都会常驻在内存中,对内存的消耗非常大,如果滥用闭包,会造成内存泄漏,降低网页的性能;
解决办法:
在退出函数之前,将一些不使用的局部变量全部删除,减轻内存的压力; - 在函数的内部再去创建一层函数,其实是不好的做法,因为闭包本身就会影响电脑的处理速度和内存消耗,如果不是非必须使用闭包,尽量减少使用闭包;
如何清理内存:
- 在函数处理完毕后,将函数销毁,在vue中经常于beforeDestroy中将函数销毁,下次需要时,再重新创建函数;
13、Vue的data,为什么要是函数?
- Vue 的组件都是可复用的,一个组件创建好后,可以在多个地方复用,而不管复用多少次,组件内的
data
都应该是相互隔离,互不影响的,所以组件每复用一次,data
就应该复用一次,每一处复用组件的data
改变应该对其他复用组件的数据不影响。 - data写为函数,其实就是把数据复制一份,各个功能操作的都是一份全新的数据对象; 所以每个组件实例可以维护独立的数据拷贝,不会相互影响。
- 涉及到了深拷贝和浅拷贝的问题;
14、Vue是如何监听数组变动?
- vue2中监听数组,就是将数组的方法重写;
01. 先获取原生 Array 的原型方法,因为拦截后还是需要原生的方法帮我们实现数组的变化。
02. 对 Array 的原型方法使用 Object.defineProperty 做一些拦截操作。
03. 把需要被拦截的 Array 类型的数据原型指向被改造后的原型。
-
为什么不对数组也实现响应式数据;
- Vue 中是通过对每个键设置 getter/setter 来实现响应式的,开发者使用数组,目的往往是遍历,此时调用 getter 开销太大了,所以 Vue 不在数组每个键上设置 ,会非常消耗性能;
vue3中监听数组是reactive函数;
- 原理是利用了es6新特性中的Proxy(代理):拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。get拦截读取属性,set拦截设置属性值或添加新属性,deleteProperty拦截删除属性;
- 通过Reflect(反射): 对源对象的属性进行操作。 语法:window.reflect.get(对象名,属性,修改属性)
15、组件缓存?
-
keep-alive是vue中的组件缓存技术,在实际项目中经常使用,vue为keep-alive配置了两个全新的生命周期钩子:
activated
路由组件被激活时触发。--显示的时候会触发deactivated
路由组件失活时触发。 -- 消失的时候会触发- 举个例子:浏览一个新闻网页,比如浏览到了100条,看到喜欢的新闻点击进去后退出来发现页面重新刷新回到了首页,这会带来非常糟糕的用户体验,这时就要使用组件缓存,其实就是让这个页面在初始化一次后便不再刷新,除非主动刷新,这样用户从哪里进去就从哪里出来;
-
keep-alive储存组件会占用内存空间,还是比较消耗性能的,一般都会设置max属性,设定缓存的最大数据量,比如5,当缓存第6个的时候,最早的一个就会被干掉;
-
keep-alive可以利用include(要缓存哪些)和exclude(不要缓存哪些)进行组件缓存选择,也可以写v-if与v-else进行组件缓存选择;
-
缓存滚动条位置到浏览器中,用ref设置元素的标识;
this.$refs.listRef.onscroll = function (e) {
localStorage.setItem('scrollTop', e.target.scrollTop)
}
// 给.article-list元素赋值滚动条的位置数据
this.$refs.listRef.scrollTop = localStorage.getItem('scrollTop')
16、This指向?
口诀: 箭头函数、new、bind、apply 和 call、欧比届点(obj.)、直接调用、不在函数里。
分场景:
-
箭头函数:
- 创建箭头函数时,就已经确定了它的 this 指向。
- 箭头函数内的 this 指向外层的 this,在哪里创建的函数,this就指向他。
-
new:( 箭头函数不能当做构造函数,所以不能与 new 一起执行。 )
- 当使用 new 关键字调用函数时,函数中的 this 一定是 JS 创建的新对象。
-
obj .
- obj的this指向他自身,类似于构造函数;
-
直接调用:
- 在函数不满足前面的场景,被直接调用时,this 将指向全局对象。在浏览器环境中全局对象是 Window,在 Node.js 环境中是 Global。
-
不在函数里:
- 在 `` 标签里,this 指向 Window。
- 在 Node.js 的模块文件里,this 指向 Module 的默认导出对象,也就是 module.exports。
17、函数节流,函数防抖的区别是什么?
-
函数节流
- 设定一个时间间隔,比如1000ms, 在1000ms无论触发多少次事件,但是只会执行一次函数。
- 原理解析:一开始先设置
setTimeout
, 如果setTimeout
还没执行的话,下次会return
。直到执行setTimeout
里的函数后才会设置下一个setTimeout
,后面依此类推。这就保证在设定的时间间隔里面,只会执行一次。
-
函数防抖
- 设定一个时间间隔,如果前后2次事件的时间间隔大于设定的时间间隔,会执行对应的函数,否则不会执行。
- 原理解析: 一开始先设置
setTimeout
,如果下个事件的时间间隔没有超过设置的delay
就触发了,则会clearTimeout
, 然后重新设置个setTimeout
,直到时间间隔超过设置的delay
才会执行,后面依此类推。
-
区别
- 防抖函数跟触发事件的频率有关,只要你触发的时间间隔超过设定的时间间隔,那么就会执行函数,否则就不会。
- 而节流函数跟时间有关,在设定的时间间隔里面,只会执行一次函数。
18、JS为什么单线程?
- 这主要和js的用途有关,js是作为浏览器的脚本语言,主要是实现用户与浏览器的交互,以及操作dom;这决定了它只能是单线程,否则会带来很复杂的同步问题。
- js使用的事件循环机制,同步非阻塞,无需创建更多的线程。
- 单线程降低了内存开销。
- 举个例子:如果js被设计了多线程,如果有一个线程要修改一个dom元素,另一个线程要删除这个dom元素,此时浏览器就会一脸茫然,不知所措。所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变;
19、Div水平垂直居中?
- 定位法:top:50%;left:50%,tranfrom:translatex(y)-50%;
- flex:justifty-content:center;align-items: center;
- 设置宽高,定位为绝对定位,设置上下左右定位值都为0,margin:auto;(前提-父元素也要设置高);
- 另外,如果父元素设置了flex布局(父元素需要设置高),只需要给子元素加上
margin:auto;
就可以实现垂直居中布局 -
.parent{ display:flex; } .child{ margin: auto; }
20、父子通信?
-
父子直接在子組件身上進行傳值
<Demo name="xxx"/>
,子组件利用props来接收,如果是数字或者string类型数据,不可以更改,如果更改vue报错;如果是数组或对象这类的数据则子组件可以更改,vue不会报错,但是不推荐在子组件内更改父组件;props : { type:数据类型, required: Boolean, 数据是否必传 default:默认传值 }
-
子向父:子组件:设置$emit('自定义事件名称',数据);父组件在子组件标签上绑定@自定义事件名称='回调函数';父组件:methods:{回调函数(){//逻辑处理}};
-
通过$parent传值,
-
其他的全局事件总线emit设置通道传参给中转站,弟通过$on接受来自中转站的参数;;
-
ref与$refs,
ref
:这个属性用在子组件上,它的引用就指向了子组件的实例。可以通过实例来访问组件的数据和方法。- this.$refs.ref设置的名,(父组件)
-
vuex;
21、祖孙通信?
-
vuex;
-
$bus; 挂载到vm身上
- 提供数据 $emit('方法名',数据)
- 接受数据 $on('方法名',数据)
- $off 解绑当前组件所用到的事件
-
project/inject 依赖注入;
project
钩子用来发送数据或方法;写法与vue2 data一样inject
钩子用来接收数据或方法;接收:inject:['num']
-
listeners 实现组件之间的跨代通信 ;
$attrs
:继承所有的父组件属性(除了prop传递的属性、class 和 style ),一般用在子组件的子元素上;$listeners
:该属性是一个对象,里面包含了作用在这个组件上的所有监听器,可以配合v-on="$listeners"
将所有的事件监听器指向这个组件的某个特定的子元素。(相当于子组件继承父组件的事件);
22、兄弟通信?
-
兄弟间通信则可以利用全局事件总线emit设置通道传参给中转站,弟通过$on接受来自中转站的参数;
- 在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件;
-
vuex;
23、父子组件的生命周期?
- 父亲:beforecreate;
- 父亲:created;
- 父亲:beforeMount;
- 孩子:beforecreatet;
- 孩子:created;
- 孩子:beforeMount;
- 孩子:mounted;
- 父亲:mounted;
- 父亲:beforeDestroy;
- 孩子:beforeDestroy;
- 孩子:destroyed;
- 父亲:destroyed;
24、Vue中的this.$set & Vue.set方法,有什么用?原理是什么?
-
作用:
- 当生成vue实例后,对象和数组当再次给赋值时,有时候并不会自动更新到视图上去;
- this.$set是专门用来向嵌套对象添加响应式属性 ;
- 写法: this.$set(this.data,”key”,value') ; this.data:要添加到data中的哪个obj属性,key:键,value:值;
-
原理:
-
都是利用了set函数;this.$set是将set函数绑定在Vue原型上,Vue.set是将set绑定在Vue的构造函数上;
-
set是一个构造函数,每个值在 Set 中只能出现一次。一个 Set 可以容纳任何数据类型的任何值;
-
set上有add-添加新元素,clear-删除所有元素,delete-删除由其指定的元素等等方法;
-
25、Vue的过滤器怎么使用?
- 定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。
- 语法:1.注册过滤器:全局配置:Vue.filter(name,callback) 或 局部配置:new Vue{filters:{}} 2.使用过滤器:{{ xxx | 过滤器名}} 或 v-bind:属性 = "xxx | 过滤器名"
- 备注:1.过滤器也可以接收额外参数、多个过滤器也可以串联 2.并没有改变原本的数据, 是产生新的对应的数据;
26、Vue自定义指令有没有做过?
-
注册自定义指令
-
全局注册:
Vue.directive( id, [definition] )
方式注册全局指令,第一个参数为自定义指令名称(指令名称不需要加v-
前缀,默认是自动加上前缀的,使用指令的时候一定要加上前缀),第二个参数可以是对象数据,也可以是一个指令函数。-
Vue.directive("focus", { inserted: function(el){ el.focus(); } })
-
局部注册:
- 通过在Vue实例中添加
directives
对象数据注册局部自定义指令。 -
directives: { focus2: { inserted: function(el){ el.focus(); } }
- 通过在Vue实例中添加
-
-
指令对象提供的钩子函数:
- bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设
- inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
- update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新。
- componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
- unbind:只调用一次,指令与元素解绑时调用。
-
指令钩子函数传参:
- el: 指令所绑定的元素,可以用来直接操作 DOM,就是放置指令的那个元素。
- binding: 一个对象,里面包含了几个属性,这里不多展开说明,官方文档上都有很详细的描述。
- vnode:Vue 编译生成的虚拟节点。
- oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。
-
<script> Vue.directive('demo', function (el, binding) { console.log(binding.value.color) // "white" console.log(binding.value.text) // "hello!" }) </script>
27、Vue组件是什么?封装过程?Vue组件封装的思路是什么?
-
什么是组件?
-
组件(Component)是Vue.js最强⼤的功能之⼀;
- 组件可以扩展HTML元素,封装可重⽤代码;
- 在较⾼层⾯上,组件是⾃定义元素,Vue.js的编译器为他添加特殊功能;
- 某些情况下,组件也可以表现⽤
js
特性进⾏了扩展的原⽣的HTML元素; - 所有的Vue组件同时也都是Vue实例,所以可以接受相同的选项对象(除了⼀些根级特有的选项),并提供相同的⽣命周期钩⼦函数;
-
-
vue组件的功能?
-
能够把页⾯抽象成多个相对独⽴的模块;
- 实现代码重⽤,提⾼开发效率和代码质量,使得代码易于维护;
-
-
vue组件的封装过程:
-
⾸先,使⽤Vue.extend()创建⼀个组件;
- 然后,使⽤Vue.component()⽅法注册组件;
- 接着,如果⼦组件需要数据,可以在props中接受定义;
- 最后,⼦组件修改好数据之后,想把数据传递给⽗组件,可以使⽤emit()⽅法;
-
-
智能组件
- 和一切数据打交道,发生各种请求。
- 只接受父组件的参数。返回给父组件需要的值。
-
木偶组件
- 不依赖父组件的实例,不受父组件影响(css)。
- 接受父组件的一切,不返回任何值。
- 渲染确定的结果。
-
页面渲染通过智能组件。它们专门做数据相关的应用逻辑,和各种数据打交道、和 Ajax 打交道,然后把数据通过 props 传递给木偶组件,它们带领着 木偶组件组件完成了复杂的应用程序逻辑
28、Vue导航守卫有哪些?在项目中如何使用?
-
全局前置守卫 router.beforeEach : 当一个导航触发时,全局前置守卫按照创建顺序调用。
-
全局解析守卫 router.beforeResolve : 和 router.beforeEach 类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。
-
全局后置钩子 afterEach :
-
路由独享的守卫 (路由内钩子) : beforeEnter: (to, from, next) => { // ... }
-
组件内的守卫 (组件内钩子) :
- beforeRouteEnter : 在渲染该组件的对应路由被 确认前调用 ;
- beforeRouteUpdate: 在当前路由改变,但是该组件被复用时调用 ;
-
beforeRouteLeave: 导航离开该组件的对应路由时调用 ;
-
完整导航解析流程:
(1)导航被触发。
(2)在失活的组件里调用离开守卫。
(3)调用全局的 beforeEach 守卫。
(4)在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
(5)在路由配置里调用 beforeEnter。
(6)解析异步路由组件。
(7)在被激活的组件里调用 beforeRouteEnter。
(8)调用全局的 beforeResolve 守卫 (2.5+)。
(9)导航被确认。
(10)调用全局的 afterEach 钩子。
(11)触发 DOM 更新。
(12)用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。
-
应用场景:
- 清除当前组件中的定时器 : 当一个组件中有一个定时器时, 在路由进行切换的时候, 可使用 beforeRouteLeave 将定时器进行清楚, 以免占用内存;
- 当页面中有未关闭的窗口, 或未保存的内容时, 阻止页面跳转 : 如果页面内有重要的信息需要用户保存后才能进行跳转, 或者有弹出框的情况. 应该阻止用户跳转;
- 保存相关内容到Vuex中或Session中 : 当用户需要关闭页面时, 可以将公用的信息保存到session或Vuex中 ;
- 判定用户是否登录展示相应内容: beforeEach路由内进行判定;
29、computed与watch的区别,分别在什么情况下使用?
-
区别:
- computed看上去是方法,但是实际上是计算属性,它会根据你所依赖的数据动态显示新的计算结果。计算结果会被缓存,computed的值在getter执行后是会缓存的,只有在它依赖的属性值改变之后,下一次获取computed的值时才会重新调用对应的getter来计算
- watcher 更像是一个 data 的数据监听回调,当依赖的 data 的数据变化,执行回调,在方法中会传入 newVal 和 oldVal。可以提供输入值无效,提供中间值 特场景。Vue 实例将会在实例化时调用 $watch(),遍历 watch 对象的每一个属性。
-
应用场景:
-
计算属性computed:适合用在模板渲染中,某个值是依赖了其它的响应式对象甚至是计算属性计算而来;
- 支持缓存,只有依赖数据发生改变,才会重新进行计算
- 不支持异步,当computed内有异步操作时无效,无法监听数据的变化
- computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data中声明过或者父组件传递的props中的数据通过计算得到的值
- 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用computed
- 如果computed属性值是函数,那么默认会走get方法;函数的返回值就是属性的属性值;在computed中的,属性都有一个get和一个set方法,当数据变化时,调用set方法。
-
侦听属性watch:适用于观测某个值的变化去完成一段复杂的业务逻辑(例如执行异步或开销较大的操作);
- 不支持缓存,数据变化,直接会触发相应的操作;
- watch支持异步;
- 监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
- 当一个属性发生变化时,需要执行对应的操作;一对多;
- 监听数据必须是data中声明过或者父组件传递过来的props中的数据,当数据变化时,触发其他操作;
30、This.$nextTick有什么用?
-
$nextTick是在下次DOM更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新之后的DOM ;
-
有什么用?
- 在created中dom还没渲染中,如果要进行dom操作的话,可以在$nextTick回调中写dom操作;
- 处理一些异步,有时候一些方法因为异步的原因,dom结构还没渲染出来,方法就执行了,导致报错;
-
原理:
- js执行宏任务时候遇到微任务(promise)或者settimeout等的时候就会把微任务放到下一轮执行而不是立刻执行,当前宏任务执行完之后再执行挂起的微任务,之后再执行下一轮宏任务;这是浏览器执行的基础;
- $nexttick就类似于被挂起微任务或者挂到下一轮宏任务的settimeout一样;
31、Mvvm和Mvc的理解?
都是框架的一种设计模式;
-
Mvvm:在
MVVM
框架下视图和模型是不能直接通信的,只能通过ViewModel
进行交互,它能够监听到数据的变化,然后通知视图进行自动更新,而当用户操作视图时,VM
也能监听到视图的变化,然后通知数据做相应改动,这实际上就实现了数据的双向绑定。并且V
和VM
可以进行通信。-
Model`(模型)
-
模型是指代表真实状态内容的领域模型(面向对象),或指代表内容的数据访问层(以数据为中心)。
-
View
(视图)
- 就像在MVC和MVP模式中一样,视图是用户在屏幕上看到的结构、布局和外观(UI)。
-
ViewModel`(视图模型)
- 视图模型是暴露公共属性和命令的视图的抽象。MVVM没有MVC模式的控制器,也没有MVP模式的presenter(主持人),有的是一个绑定器。在视图模型中,绑定器在视图和数据绑定器之间进行通信。
-
-
优点:
- 低耦合:
View
可以独立于Model
变化和修改,一个ViewModel
可以绑定到不同的View
上,当View
变化的时候Model
可以不变,当Model
变化的时候View
也可以不变。 - 可重用性: 可以把一些视图逻辑放在一个
ViewModel
里面,让很多View
重用这段视图逻辑。 - 独立开发: 开发人员可以专注于业务逻辑和数据的开发,设计人员可以专注于页面的设计。
- 可测试:界面素来是比较难于测试的,而现在测试可以针对
ViewModel
来写。
- 低耦合:
-
Mvc:
MVC
是应用最广泛的软件架构之一,一般MVC分为:Model
(模型),View
(视图),Controller
(控制器)。 这主要是基于分层的目的,让彼此的职责分开.View
一般用过Controller
来和Model
进行联系。Controller
是Model
和View
的协调者,View
和Model
不直接联系。基本都是单向联系。M和V指的意思和MVVM中的M和V意思一样。C即Controller
指的是页面业务逻辑。MVC是单向通信。也就是View
跟Model
,必须通过Controller
来承上启下。- Model
(模型)表示应用程序核心(如数据库)。
- View
(视图)显示效果(HTML页面)。
Controller
(控制器)处理输入(业务逻辑)。
- Model
-
优点:
- 低耦合
- 重用性高
- 生命周期成本低
- 部署快
- 可维护性高
- 有利软件工程化管理
32、v-if,v-show,v-html的原理是什么?
- v-if使用数据生成dom结构或销毁dom结构,会造成重排与重绘,比v-show更加消耗性能;
- v-show是利用display来操作dom的显示与隐藏,不会造成重排,只会造成重绘,较为节省性能;
- v-html是利用了原生的innerHTML标签,能够识别html标签;
33、Computd的原理是什么?
- 当组件初始化的时候,
computed
和data
会分别建立各自的响应系统,Observer
遍历data
中每个属性设置get/set
数据拦截 - 初始化
computed``initComputed
- 注册一个
watcher
实例,并在内实例化一个Dep
消息订阅器用作后续收集依赖(比如渲染函数的watcher
或者其他观察该计算属性变化的watcher
) - 调用计算属性时会触发其
Object.defineProperty
的get
访问器函数 - 调用
watcher.depend()
方法向自身的消息订阅器dep
的subs
中添加其他属性的watcher
- 调用
watcher
的evaluate
方法(进而调用watcher
的get
方法)让自身成为其他watcher
的消息订阅器的订阅者,首先将watcher
赋给Dep.target
,然后执行getter
求值函数,当访问求值函数里面的属性(比如来自data
、props
或其他computed
)时,会同样触发它们的get
访问器函数从而将该计算属性的watcher
添加到求值函数中属性的watcher
的消息订阅器dep
中,当这些操作完成,最后关闭Dep.target
赋为null
并返回求值函数结果。
- 当某个属性发生变化,触发
set
拦截函数,然后调用自身消息订阅器dep
的notify
方法,遍历当前dep
中保存着所有订阅者wathcer
的subs
数组,并逐个调用watcher
的update
方法,完成响应更新。
34、同步和异步是什么?
- 因为js时单线程执行的代码,所以js将代码执行分为了同步异步;;同步可以保证顺序一致,但是容易导致阻塞;异步可以解决阻塞问题,但是会改变顺序性,根据不同的需要去写你的代码。
- 同步是一般代码的执行方式,函数,定时器,延时器等都是异步,js规定是同步执行完再执行异步,所以js通常都会面对同步异步的执行问题;
- 解决方案:现在都是用js官方的同步异步最终解决方案promise的async,await来解决同步异步的问题的;
35、Vuex的存储方式以及其他三种存储方式(cookie、localStorage、sessionStorage )的区别?
-
actions:发起异步请求,调用commit,操作mutations;
-
mutations:操作state数据源;
-
state:数据源;
-
getters:加工数据场所,类似computed;
-
modules——> 模块,每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块
-
组件内调用:
- this.$store.(actions,mutations,state,getters),通过dispatch,commit等操作;
-
辅助函数:
- mapactions,mapmutations,mapstate,mapgetters;
-
三种储存方式的区别:
-
cookie:可设置失效时间,没有设置的话,默认是关闭浏览器后失效
- cookie:4KB左右
-
localStorage:除非被手动清除,否则将会永久保存。
- 可以保存5MB的信息。
-
sessionStorage: 仅在当前网页会话下有效,关闭页面或浏览器后就会被清除。
- 可以保存5MB的信息。
-
-
区别:
- vuex存储在内存中,页面刷新便销毁掉了;localstorage(本地存储)则以文件的方式存储在本地,永久保存(不主动删除,则一直存在);sessionstorage( 会话存储 ) ,临时保存。localStorage和sessionStorage只能存储字符串类型,对于复杂的对象可以使用ECMAScript提供的JSON对象的stringify和parse来处理;
36、v-if和v-for的优先级,能否连用?
-
v-for的优先级要高于v-if:
-
能否连用?
- 可以操作但不允许这样操作,会造成性能浪费;
-
注意事项:
- 永远不要把 v-if 和 v-for 同时用在同一个元素上,带来性能方面的浪费(每次渲染都会先循环再进行条件判断);
- 如果条件判断是在循环外部,则在外层嵌套 template (页面渲染不生成 dom 节点),在这一层进行 v-if 判断,然后在内部进行 v-for 循环;
- 如果条件出现在循环内部,可通过计算属性 computed 提前过滤掉那些不需要显示的项;
37、异步组件(按需加载)是什么?
-
异步,是相对于同步而言的。我们在使用Vue时,使用到的组件大多为同步组件。在vue实例第一次执行渲染的过程中,已经生成了组件构造器。而异步组件则是在用到该组件时,异步通过请求去拉取对应组件的js(这里需要和webpack的import相结合)。当对应的js加载完成后,获取到异步组件的配置项,从而创建组件构造器。再通过forceRender,强制依赖它的vm实例重新触发渲染函数。
-
作用:
- 在初始页面加载时,如果组件全部是同步的,加载的数量非常多,会导致加载时间过长,降低用户体验;异步组件则是用到哪个组件加载哪个组件;
-
如何使用?
-
局部注册:
new Vue({ ...rest, components: { a: () => import('./components/a.vue') } })
-
全局注册:
Vue.component('async-comp', (resolve, reject) => ({ component: () => imort('./components/a.vue'), loading: loadingComp, error: errorComp, delay: 200, timeout: 3000 }));
-
38、动态路由是?
-
为什么要使用动态路由?
- 为了进行全面的权限控制,会需要后台给出路由表,前端渲染。不用在前端配置。 比如:根据用户的角色,登录进来显示不同的菜单 ;
-
是什么?
- 动态路由就是后端提供路由表,但是是字符串,前端利用处理方法将路由表由字符串转化成对象,存储到vuex中;为的是后端区分不同用户进来时显示不同页面,用权限管理用户访问的页面;
39、es6新特性 (对2015+统称)
-
新增了let与const的块级作用域,在之前的js中只有全局作用域与函数作用域;
-
数组与对象的解构写法 ;
// 数组解构写法 const arr = [100, 200, 300] // 都获取 const [a, b, c] = arr // a = 100 b = 200 c = 300 // 只获取特定的值 const [, , c] = arr // c = 300 // 对于的数组长度超过解构的数组长度时,会返回undefined const [a,b,c,d,e] = arr // a = 100, b = 200 c = 300 d = undefined e = undefined // 还可以设置默认值 const [a,b,c,d = 400,e = 500] = arr // a = 100, b = 200 c = 300 d = 400 e = 500 // 对象解构写法 const obj = {name: 'bob', age: 23} // 首先是根据属性获取值 const {name, age} = obj console.log(name) console.log(age) // 如果外面有一个相同的属性名,可以使用重命名的方式获取,并且可以设置默认值 const {name:setName = 'sale', age} = obj let name = 'reset' console.log(name) console.log(age) // ... 展开运算符写法 const obj = {name: 'bob', age: 23} ...obj
-
` 模板字符串中${}插值写法`
-
箭头函数:
-
1、简化的函数的书写格式,使函数看起来更加容读和书写;
2、它没有this的机制,不会改变this的指向,任何情况下都不会改变;
-
-
promise、proxy、object的assign、is 等全新的方法对象功能的增加;
-
全新的数据类型: symbol
-
全新的数据方法: set、map (会生成新的数组)
- aysnc await 是ES7 出来的, ES7还有includes
40、箭头函数和普通函数的区别?
- 箭头函数没有this,箭头函数中使用的this是上级作用域的调用者;
- 写法不一样,箭头函数不用function,写法是:()=>{} ,普通函数写法:function 函数名() {};
- 箭头函数不可以用call,apply,bind改变this指向,普通函数可以;
41、H5新增特性有哪些?
- 语义化标签: header、nav、footer、main、artical、section等 ;
- Canvas、SVG -- 用于绘画的元素,canvas绘制的图片会失真而SVG绘制的不会失真。
- video、audio -- 用于播放视频和音频的媒体。
- Drag 、Drop -- 用于拖放的 。
- Geolocation -- 用于获取地理位置。
- localStorage、sessionStorage -- 用于本地离线存储。
- 新的特殊内容元素 -- 如:article、footer、header、nav、section。
- 新的表单控件 -- 如:date、time、email、url、search。
42、npm是什么?有哪些常用指令?
-
npm是管理node包的工具,npm包能够有效的提升前端开发效率,现在前端开发离不开npm;
-
指令:
- npm -v :查看版本号;
- npm init :npm工具初始化;
- npm i(install) 模块名 模块名 ...;下载安装第三方模块;
- npm un( uninstall ) 模块名 模块名 ...:卸载模块;
- npm run dev(serve):vue 中项目热加载执行;
- npm run build :项目生产文件打包;
- npm config set registry registry.npm.taobao.org/ : 切换镜像源为npm ;
- npm login : 登录 npm 账号 ;
- npm publish : 将包发布到 npm 上 ;
- npm unpublish 包名 --force : 从 npm 删除已发布的包 ;
- npm i 模块名 -g :多个g表示;
43、Git有哪些常用指令?工作中git的使用流程是?
-
git 有哪些常用指令
-
git init 初始化git ;
-
git push +仓库地址/代号英文(origin,gitee)+ 分支(例:master) 推送到、远程仓库的master(分支可选)分支;
-
git add 提交文件;
-
git commit -m 提交文件到本地仓库的分支(默认master);
-
git pull/Fetch +分支(origin/master) 拉取文件;
-
git clone +仓库地址 克隆github文件;
-
git branch 查询分支;
-
git log 查看提交的日志;
-
git diff 查看工作区与暂存区的不同;
- git diff --cached 查看暂存区与仓库去的不同;
- git diff HEAD 查看工作区与仓库区的不同,HEAD表示最新的那次提交;
- git diff c265262 de4845b 查看两个版本之间的不同
-
git reset 版本回退,将代码恢复到已经提交的某一个版本中
-
- `git reset --hard 版本号` 将代码回退到某个指定的版本(版本号只要有前几位即可) - `git reset --hard head~1`将版本回退到上一次提交 - ~1:上一次提交 - ~2:上上次提交 - ~0:当前提交 - 当使用了`git reset`命令后,版本会回退,使用`git log`只能看到当前版本之前的信息。使用`git reflog`可以查看所有的版本信息
-
-
.gitignore
忽视文件 -
git分支命令
-
创建分支
git branch 分支名称
创建分支,分支中的代码,在创建时与当前分支的内容完全相同。- git在第一次提交时,就有了一个叫
master
的主分支。 git branch dev
,创建了一个叫做dev的分支
查看分支
git branch
可以查看所有的分支,- 在当前分支的前面会有一个
*
- 在git中,有一个特殊指针
HEAD
,永远会指向当前分支
切换分支
git checkout 分支名称
切换分支 HEAD指针指向了另一个分支- 在当前分支的任何操作,都不会影响到其他的分支,除非进行了分支合并。
- 提交代码时,会生产版本号,当前分支会指向最新的版本号。
创建并切换分支
-
git checkout -b 分支名称
创建并切换分支 -
切换分支会做两件事情
- 创建一个新分支
- 把head指针指向当前的分支
删除分支
git branch -d 分支名称
可以删除分支- 注意:不能在当前分支删除当前分支,需要切换到其他分支才能删除。
- 注意:
master
分支是可以删除的,但是不推荐那么做。
合并分支
git merge 分支名称
将其他分支的内容合并到当前分支。- 在
master
分支中执行git merge dev
将dev
分支中的代码合并到master
分支 - 分支合并
git合并冲突
- 对于同一个文件,如果有多个分支需要合并时,容易出现冲突。
- 合并分支时,如果出现冲突,只能手动处理,再次提交,一般的作法,把自己的代码放到冲突代码的后面即可。
-
git remote
# 给远程仓库设置一个别名 git remote add 仓库别名 仓库地址 git remote add autumnFish git@github.com:autumnFish/test.git # autumnFish git remote remove autumnFish # 检查是否关联成功 git remote -v # 一般情况需要先pull一下:git pull origin master # push到远程库: git push -u autumnFish master # git clone的仓库默认有一个origin的别名
-
-
工作中使用流程
- 1、公司项目技术负责人创建初始项目并提交到git仓库之中;
- 2、从git仓库中获取项目源码;
- 3、按照负责的项目模块创建名称分支;
- 4、负责的模块完成,提交到git仓库的分支中;
- 5、公司测试没有任何问题,公司项目技术负责人将该分支合并到主干上;
44、遍历数组有哪些方法?
-
for
-
forEach
- 基本语法:
数组.forEach(function(数组[i],索引值){ 业务处理 });
功能类似于for循环,但是比for循环更加简洁明了,更易于使用,效率更高;
- 基本语法:
-
filter
- 基本语法:
let newArr(新数组) = 数组.filter(function(数组[i],索引值){return 条件1 === 条件2});
将数组中满足条件的筛选出来,生成一个新的数组,所以需要用一个变量去接受,常用于筛选&删除业务;
- 基本语法:
-
map
- 基本语法:
let newArr(新数组) = 数组.map(function(数组[i],索引值){return 数组[i]+2});
map的使用和forEach类似,常用来操作原数组里面的内容,会生成新的数组;
- 基本语法:
-
find
- 基本语法:
let item = 数组.find(function(数组[i],索引值)){return 数组[i]===条件};
找到第一个满足条件的值,会返回数值;
- 基本语法:
-
findIndex
- 基本语法:
let i = 数组.findIndex(function(数组[i],索引值)){return 数组[i]===条件};
找到第一个满足条件的值,会返回数值的索引值;
- 基本语法:
-
some
- 基本语法::
let sta = 数组.some(function(数组[i],索引值)){return 数组[i]===条件};
检测数组中是否有元素满足指定条件,返回布尔值;
- 基本语法::
-
every
- 基本语法::
let sta = 数组.every(function(数组[i],索引值)){return 数组[i]===条件};
用于检测数组所有元素是否都符合指定条件,返回布尔值;
- 基本语法::
-
reduce
- 基本语法::
let res = 数组.reduce(callback(返回reduce内部上一次回调return的值/上次回调没值,则返回数组第一个值(即初始值),当前的数组[i],索引值,数组),设置初始值){return 上一次的值+当次的值};
reduce功能和forEach、map类似,但个人感觉reduce在多层解构数组合并上比较好用;
- 基本语法::
45、Vue中有什么方法防止样式覆盖?
- vue中防止样式覆盖的是scoped,能够封闭组件内的样式,污染全局样式及其他组件样式;
- 本质上是随机生成唯一字符串的类名;
46、Vue有哪些常用指令?
-
v-bind 数据绑定
-
v-model 双向数据绑定
-
v-on,@ 命令绑定 click,keyup,mouseenter,mouseleave,keydown...
-
v-if,v-show;
-
v-for
-
v-html,v-text;
-
v-pre: 跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签。跳过大量没有指令的节点会加快编译。
-
v-once: 只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。
-
v-cloak:
-
这个指令保持在元素上直到关联实例结束编译。和 CSS 规则如 [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕。
-
防止刷新页面,网速慢的情况下出现{{ message }}等数据格式
-
47、http请求里面包含什么?
-
通用部分(General):
- 请求URL地址
- 请求方法(get、post...)
- 请求状态码(200)
-
请求报文:
- 请求头:请求方法,请求协议等等;
- 请求体:请求参数;
-
响应报文:
- 响应头:响应时间,实体长度,响应持续时间等等...
- 响应体:响应的数据;
48、ajax和axios的区别?
- ajax是html原生技术,实现了网页的局部数据刷新 ;
- axios是 axios是通过promise实现对ajax技术的一种封装,就像jQuery实现ajax封装一样;
49、浏览器的缓存机制?
1、是浏览器对之前请求过的文件进行缓存,以便下次访问时重复使用,节省带宽,提高访问速度,降低服务器压力;
2、 http缓存机制主要在http响应头中设定,响应头中相关字段为Expires、Cache-Control、Last-Modified、Etag; Expires因为是对时间设定的,且时间是Greenwich Mean Time (GMT),而不是本地时间,所以对时间要求较高 ;
3、浏览器缓存也就是http缓存机制,就是告诉浏览器在约定的时间之前,可以直接从缓存中获取资源,而无需去服务器获取;
4、浏览器分为强制缓存和协商缓存:
强制缓存:浏览器不会像服务器发送任何请求,直接从本地缓存中读取文件并返回;css,js,图片:强缓存,文件名带上hash。
协商缓存: 向服务器发送请求,服务器会根据这个请求的request header的一些参数来判断是否命中协商缓存,如果命中,则返回304状态码并带上新的response header通知浏览器从缓存中读取资源;html使用;
50、null==undefined,null===undefined,的结果以及为什么?
-
结果:true,false
-
为什么:
- 在js设计之初,undefined这个值就是派生自null值的,因为用户长期使用的原因,在以后的js中都规范中定义了他们之间相等,==是判断值之间是否相等,并不会比较数据类型,undefined代表不明确,所以undefined==null;但是===要数据类型相等,null和undefined并不属于一种数据类型;
51、点数计算和解决方法;
JS点数计算:因为十进制转二进制会存在数据转换时存在小数点,导致计算会存在误差,会存在细小的偏差,几乎所有的编程语言都会存在这个问题;
解决方法:1、使用Math.floor()向下取整,2、to.fixed()不保留小数点,或preseInt,3、将小数点后面的位数去掉;4、 把需要计算的数字升级(乘以10的n次幂)成计算机能够精确识别的整数,等计算完毕再降级(除以10的n次幂)。
52、Vue的混入,混入的执行顺序,混入的生命周期顺序?
-
什么是混入mixin?
- 将组件的公共逻辑或者配置提取出来,哪个组件需要用到时,直接将提取的这部分混入到组件内部即可,混入有自己的data,method,生命周期。
- 这样既可以减少代码冗余度,也可以让后期维护起来更加容易。
- 数据与方法出现了重复了,以数组为主, 如果是生命周期,则都执行。mxin先执行,组件后执行;
-
混入的执行顺序;
- beforeCreate=>create=>beforeMount=>mounted;
-
混入的生命周期顺序;
- beforeCreate;
- create;
- beforeMount;
- mounted;
-
优点:
- 提高代码复用性
- 无需传递状态
- 维护方便,只需要修改一个地方即可
-
缺点:
- 命名冲突
- 滥用的话后期很难维护
- 不好追溯源,排查问题稍显麻烦
- 不能轻易的重复代码
53、http状态码200~500是什么意思?
- 2开头 (请求成功)表示成功处理了请求的状态代码。
- 3开头 (请求被重定向)表示要完成请求,需要进一步操作。 通常,这些状态代码用来重定向。
- 4开头 (请求错误)这些状态代码表示请求可能出错,妨碍了服务器的处理。
- 5开头(服务器错误)这些状态代码表示服务器在尝试处理请求时发生内部错误。 这些错误可能是服务器本身的错误,而不是请求出错。
54、 如何判断一个对象是不是空对象
-
用for in遍历对象,看能否遍历出key;
-
JSON.stringify() 方法用于将 JavaScript 值转换为 JSON 字符串。看是否等于{}
-
ES6 新增的方法 Object.keys(obj):
- Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组。 如果我们的对象为空,他会返回一个空数组,如下: var obj = {} Object.keys(obj) // [] 判断Object.keys(obj).length===0;
55、判断数据类型的方法以及判断方法的区别?
-
typeof 语法:typeof 数据
-
typeof '';// string 有效
typeof 1;// number 有效
typeof Symbol();// symbol 有效
typeof true;//boolean 有效
typeof undefined;//undefined 有效
typeof null;//object 无效
typeof
[]
;//object 无效typeofnew Function();// function 有效
typeof newDate();//object 无效
typeof newRegExp();//object 无效
-
-
instanceof 语法: 数据 instanceof 类型(例:Array,Obejct)
1. [] instanceof Array;// true 2. {} instanceof Object;// true 3. newDate() instanceof Date;// true 4. function Person(){}; 5. newPerson() instanceof Person; 6. [] instanceof Object;// true 7. newDate() instanceof Object;// true 8. newPerson instanceof Object;// true
-
constructor 语法:数据.constructor == 数据类型 返回Boolean
- 例外: null 和 undefined 是无效的对象,因此是不会有 constructor 存在的
-
toString 语法:Object.prototype.toString.call(数据) 返回: [object 数据类型]
1. Object.prototype.toString.call('') ; // [object String] 2. Object.prototype.toString.call(1) ; // [object Number] 3. Object.prototype.toString.call(true) ;// [object Boolean] 4. Object.prototype.toString.call(Symbol());//[object Symbol] 5. Object.prototype.toString.call(undefined) ;// [object Undefined] 6. Object.prototype.toString.call(null) ;// [object Null] 7. Object.prototype.toString.call(newFunction()) ;// [object Function] 8. Object.prototype.toString.call(newDate()) ;// [object Date] 9. Object.prototype.toString.call([]) ;// [object Array] 10. Object.prototype.toString.call(newRegExp()) ;// [object RegExp] 11. Object.prototype.toString.call(newError()) ;// [object Error] 12. Object.prototype.toString.call(document) ;// [object HTMLDocument] 13. Object.prototype.toString.call(window) ;//[object global] window 是全局对象 global 的引用
56、NaN是什么,用typeof输出会是什么?
-
NaN是什么:
- not a number 不是一个数字,数据不是number就是NaN, 使用isNaN( )可以判断数据是否为数字,返回布尔值;
- isNaN() 函数通常用于检测 parseFloat() 和 parseInt() 的结果,以判断它们表示的是否是合法的数字 ;
-
typeof输出:number;
57、AJAX原理?
-
Ajax是一种异步请求数据的web开发技术 , 是在不需要重新刷新页面的情况下,Ajax 通过异步请求加载后台数据 ;
-
核心是通过浏览器提供的 XMLHttpRequest对象 ,它扮演中间角色,浏览器发出http请求与接受http响应交由XMLHttpRequest后便去做其他事情去了,等收到XHR返回来的数据再渲染页面;
-
步骤:
- 第一步:实例化XMLHttpRequest;
- 第二步:xhr.open(method,url,同步还是异步设置为true/false)
- 第三步: xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded"); 设置请求文本的格式;
- 第四步:xhr.send(),此处里面放置请求参数,即请求体;
- 第五步:xhr.responseText 获得字符串形式的响应数据。xhr.responseXML 获得XML 形式的响应数据。
58、路由跳转有哪些方法?
-
第一种router-link声明式,本质上是a标签;用to完成路径跳转;
-
第二种编程式,写到函数里
- this.$router.push =>直接跳转到指定的路由组件,会存在历史路径;
- this.$router.replace => 也是直接跳转到指定的路由组件,但不存在历史路径;
- this.$router.go => 指定跳到此路由多少层之前;
59、js垃圾回收机制?
-
标记清除算法
-
此算法分为
标记
和清除
两个阶段,标记阶段即为所有活动对象做上标记,清除阶段则把没有标记(也就是非活动对象)销毁 -
流程:
- 垃圾收集器在运行时会给内存中的所有变量都加上一个标记,假设内存中所有对象都是垃圾,全标记为0
- 然后从各个根对象开始遍历,把不是垃圾的节点改成1
- 清理所有标记为0的垃圾,销毁并回收它们所占用的内存空间
- 最后,把所有内存中对象标记修改为0,等待下一轮垃圾回收
-
优点:
- 标记清除算法的优点只有一个,那就是实现比较简单,打标记也无非打与不打两种情况,这使得一位二进制位(0和1)就可以为其标记,非常简单
-
缺点:
- 标记清除算法有一个很大的缺点,就是在清除之后,剩余的对象内存位置是不变的,也会导致空闲内存空间是不连续的,出现了
内存碎片
(如下图),并且由于剩余空闲内存不是一整块,它是由不同大小内存组成的内存列表,这就牵扯出了内存分配的问题
- 标记清除算法有一个很大的缺点,就是在清除之后,剩余的对象内存位置是不变的,也会导致空闲内存空间是不连续的,出现了
-
60、原型链
- 什么是原型:几乎每个JavaScript对象都有另一个与之关联的对象。这另一个对象被称为原型(prototype),第一个对象从这个原型继承属性。
-
- prototype => 显示原型 构造函数,函数都有 - __proto__ => 隐式原型 obj都有 obj对象我们并没有给他赋任何值,他身上是没有hasOwnProperty这个属性的,之所以能够打印出来,是因为obj的__proto__属性是从Object.prototype身上取的; 当一个对象的某个属性没有时,它会顺着它的__proto__(也是它的构造函数的prototype)里去寻找属性:
61、深克隆(拷贝)、浅克隆(拷贝),深克隆的几种方案?
-
深拷贝和浅拷贝是只针对Object和Array这样的复杂类型的
-
javascript中存储对象都是存地址的,所以浅拷贝是都指向同一块内存区块,而深拷贝则是另外开辟了一块区域。
-
浅拷贝,也就是说a和b指向了同一块内存,所以修改其中任意的值,另一个值都会随之变化
-
深拷贝,也就是说a和b指向不同的内存,所以修改其中任意的值,另一个值不会随之变化
-
浅克隆的方法:
-
解构写法
-
直接复制
-
数组的方法
- slice,concat
-
Object.assign()
-
-
for in 遍历赋值可行(但是有缺点,只能拷贝一层,无法完成深层次的拷贝);
-
深克隆的方法:
-
JSON.parse(JSON.stringify(obj))
- 先把 obj 用 JSON.stringify 转为 JSON 字符串,然后再用 JSON.parse 将 JSON 字符串转为对象,这样就可以实现深克隆;
-
递归(可以拷贝多层结构对象)
-
62、如何使用CDN实现项目优化?
63、http和https有什么区别?
-
HTTP(超文本传输协议)是网络上最为广泛的传输协议,被用于在web浏览器和网站服务器之间的传输协议。HTTP是一个简单的请求-响应协议,它通常运行在TCP之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。
-
http的缺点:
- 通信使用明文传输
- 不验证通信方身份
- 无法验证报文的完整性
-
-
HTTPS(安全套接字层超文本传输协议)是以安全为目标的HTTP通道。
-
HTTPS 是用 ssl证书进行加密
-
端口号不同:http端口号是80,https是443;
64、标准盒子模型与怪异盒子模型
- 在标准模式下,一个块的总宽度= width + margin(左右) + padding(左右) + border(左右) W3C规定的盒子模型
- 在怪异模式下,一个块的总宽度= width + margin(左右)(即width已经包含了padding和border值)IE规定的盒子模型;
65、小程序的生命周期有哪些?组件之间如何传值?
-
小程序的生命周期
-
应用生命周期
- onLaunch:小程序初始化时触发;💙
- onShow:小程序启动或切到前台时触发;💙
- onHide:小程序切到后台触发;💙
- onError:错误监听函数;
- onThemeChange:系统主题变化时触发;
-
页面生命周期
- onLoad:页面加载时触发;💙
- onShow:页面显示时触发;💙
- onReady:页面初次渲染完成时触发;
- onHide:页面隐藏时触发;💙
- onUnload:页面卸载时触发;💙
- onPullDownRefresh:用户下拉动作时触发;💙
- onReachBottom:上拉,页面触底时触发;💙
- onShareAppMessage:点击右上角转发时触发;
- onShareTimeline:店家右上角转发到朋友圈触发;
- onAddToFavorites:点击右上角收藏时触发;
- onPageScroll:页面滚动事件触发;
- onResize:页面尺寸变化时触发;
-
组件生命周期
-
lifetimes:{ // 组件实例进入页面节点树时执行 attached:function(){}, // 组件实例被从页面节点树移除时执行 detached:function(){} }
-
-
-
组件之间的传值
- 父传子:
-
整体流程与vue相似,略微地方不一样:
-
父组件: <view src="父组件"></view> 子组件接收数据:properties:{ src:{ // 数据类型 type:String, // 默认值 value:'' } } 子组件使用数据:<view src="{{src}}"></view>
-
-
子组件传值给父组件
-
// 子组件绑定点击事件 <view bind:tap="handleFn"></view> methods:{ handleFn(){ const src = this.properties.src } } // 触发父组件自定义事件 methods:{ // 这是子组件里的方法 handleFn(){ const src = this.properties.src // 触发父组件的自定义事件”srcChangeFn“ this.triggerEvent("srcChangeFn",src) } } // 父组件绑定自定义事件 <view bind:srcChangeFn="fatherSrcChangeFn"></view> // 父组件内部设置自定义事件函数 methods:{ fatherSrcChangeFn(e){ e.detail => 子组件传的值 } }
-
66、小程序有什么限制?
- 小程序开发的所有包加起来不能超过8M,单个分包/主包大小不能超过 2M
- 请求接口限制,小程序请求的接口必须是https协议
- 小程序支付(小程序支付)和公众号H5支付(jsapi支付)不属于同一支付体系
- 小程序下载文件到本地,资源限制最大文件为50M
- 不能打开超过10个层级的页面;
67、uni-app有什么优势?
- 多端开发,一套代码可以生成ios、安卓、微信小程序、H5等。
- 学习成本低,uniapp封装的组件与微信小程序相似,并且基于vue.js,上手快。
68、微信小程序获取用户个人信息?
- uni.app 是调用了 uni.getUserProfile 方法
-
uni.getUserProfile({ desc: '用于完善会员资料', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写 success: (res) => { this.showDialogBtn();//调用一键获取手机号弹窗(自己写的) } })
69、小程序的数据绑定和vue有什么区别?
-
小程序单项绑定事件:bind:tap = 事件名(参数); catch:tap = 事件名(参数)
-
他们的不同点主要是bindtap是不会阻止冒泡事件的,catchtap是阻值冒泡的
-
小程序获取数据:this.data.属性名
-
小程序设置数据:
-
this.setData({ 属性名:属性值 })
-
70、小程序的跳转方式?
// 保留当前页面,跳转到应用内的某个页面,使用wx.navigateBack可以返回到原页面。
// 注意:调用 navigateTo 跳转时,调用该方法的页面会被加入堆栈
wx.navigateTo({
url: 'page/home/home?user_id=111'
})
// 关闭当前页面,返回上一页面或多级页面。可通过 getCurrentPages() 获取当前的页面栈,决定需要返回几层。
wx.navigateTo({
url: 'page/home/home?user_id=111' // 页面 A
})
wx.navigateTo({
url: 'page/detail/detail?product_id=222' // 页面 B
})
// 跳转到页面A
wx.navigateBack({
delta: 2
})
// 关闭当前页面,跳转到应用内的某个页面。
wx.redirectTo({
url: 'page/home/home?user_id=111'
})
// 跳转到tabBar页面(在app.json中注册过的tabBar页面),同时关闭其他非tabBar页面。
wx.switchTab({
url: 'page/index/index'
})
// 关闭所有页面,打开到应用内的某个页面。
wx.reLanch({
url: 'page/home/home?user_id=111'
})
71、路由的hash 和 history 之间的区别?
-
hash模式
- 即地址栏 URL 中的
#
符号(此 hash 不是密码学里的散列运算)。 比如这个 URL:http://www.abc.com/#/hello
,hash 的值为#/hello
。它的特点在于:hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。
- 即地址栏 URL 中的
-
history模式
- 利用了 HTML5 History Interface 中新增的
pushState()
和replaceState()
方法。(需要特定浏览器支持) - history模式上线的话,后端需要在nginx配置一些东西才可以正常使用;
- 这两个方法应用于浏览器的历史记录栈,在当前已有的
back
、forward
、go
的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会立即向后端发送请求。
- 利用了 HTML5 History Interface 中新增的
72、组件化、模块化、路由之间的区别?
- 组件化 :将一个功能单独打包,这个功能内部含有HTML、css、js、font、img等等,以达到高复用,使用这种方式开发的项目就是组件化开发;
- 模块化 :将js根据功能分成一个个js文件,然后用这种方式操作的项目就叫模块化项目;
- 路由:只是一个中间件,用来做不同组件之间的通讯的中间件罢了;
73、 vue和uni-app的区别
- 1、uni-app可以通过打包实现一套代码多端运行,而vue不行;
- 2、uni-app有自动的框架预载,加载页面的速度更快,vue没有;
- 3、uniapp使用小程序的标签,vue使用web端的标签;
- 4、uni不支持vue-router,使用自带的路由。
74、微信小程序支付流程
- 获取用户信息,通过uni.getUserProfile() 获取用户信息,然后调用uni.login()拿到微信的临时验证code;
- 将用户信息与验证的code发送到后端,后端到微信后端服务器换取 token 返回到我这边;
- 我用token 发送到后端请求创建订单,并且生成订单编号返回到我这边;
- 我用生成的订单编号创建支付流程,然后用订单编号与用户token发送后端请求支付参数;
- 最后用支付参数调用 uni.requestPayment () ,调起微信支付;
- 根据回调成功或失败做出响应的回调;
75、之前有打包过app吗?
76、前端会涉及哪些安全问题?有没有了解xss
-
XSS 攻击?
- 含义:XSS攻击通常指的是给网站注入恶意脚本,使这个恶意脚本在用户浏览器远行,利用这些恶意脚本,攻击者可以获取用户的权限密钥,比如cookie,然后窃取用户隐私数据。
-
本质: 是将恶意代码与正常代码混在一起,浏览器无法分别哪些脚本是可信的还是不可信的,所以导致了恶意脚本被执行;
-
前端安全问题?
-
XSS 攻击
-
跨站请求伪造(CSRF)
-
产生原因:
- 攻击者诱导受害用户者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
-
解决方案:
- 直接禁止 不受信任的域名 对我们发起请求。
-
-
77、首屏加载时间过长?
- 因为vue是 SPA 单页面,所以初始加载时间过长、使用import按需引入(路由懒加载);
- 使用 骨架屏 或者 loading等待效果 进行页面等待加载,提高用户体验
78、promise有几种状态?
- Pending(进行中,初始状态,既不是成功,也不是失败状态。);
- Resolved(已完成,又称 Fulfilled);
- Rejected(已失败) ;
79、vuex第一次刷新导致首页空白?
-
因为页面刷新的话是所有的页面都销毁,其中vuex也会将数据进行初始化,这时候就会导致首页空白
-
解决方案:
- 将vuex的数据存储到 localStorage 或 sessionStorage ,进行数据持久化;
80、两个新页面如何进行传值?
首先有俩种思路:一种是get方式参数在url上,跳转后的页面肯定能获取到。还有一种思路,就是将参数存到一个公共的地方,页面都可以获取到。
- 1.cookie 传值。
缺点: cookie储存是需要服务器支持的,本地直接运行静态文件是实现不了的。cookie能够存储少量数据到客户端的磁盘中,特定的网页之间是可以共享cookie中的数据。
- 2.LocalStorage和SessionStorage传值。
优点:本地静态文件可支持。在HTML5中,新加入了一个localStorage特性,这个特性主要是用来作为本地存储来使用的,解决了cookie存储空间不足的问题(cookie中每条cookie的存储空间为4k),localStorage中一般浏览器支持的是5M大小,这个在不同的浏览器中localStorage会有所不同。此方法类似cookie,将数据存在一个公共的地方,实现页面之间传值。
- 3.get方式,Url传值.。
优点: 速度快. cookie 和 LocalStorage和SessionStorage 都存在速度慢,反应不过来的问题, 我在a页面写进去,在b页面读出来.有时会读到空值。post提交表单跳转的方式肯定不行了,因服务端并不处理这些请求参数,连到达客户端的机会都没有。
- 4.window.open和window.opener之间传值
window.open可以打开一个新的页面,在新的页面中可以通过window.opener获取父页面的窗口对象,从而可以获取父窗口中的参数。
如:var content = window.opener.document.getElementById("open").innerHTML; ————————————————
对于不同的解决方法,都有优缺点
- 1、url携带参数
优点:取值方便,可以跨域,利于页面分享,没有环境限制。
缺点:url携带参数值的长度有限制。
- 2、cookie方式
优点:可以在同源内的的任意网页中访问,存储数据的周期可以自由设置。
缺点:有长度限制。
- 3、设置窗口之间的父子关联关系
优点:取值方便.只要window.opener指向父窗口,就可以访问所有对象.不仅可以访问值,还可以访问父窗口的方法.值长度无限制。 缺点:两窗口要存在着关系.就是利用window.open打开的窗口。不能跨域。
- 4、h5技术,window.localStorage存储数据
优点:储存空间大,有5M存储空间。
缺点:不是所有浏览器都支持。
81、call、bind、apply 有什么区别?
-
call、bind、apply作用是?都是函数的方法
- 改变函数执行时的上下文,再具体一点就是改变函数运行时的this指向。
-
三者的区别:
-
bind方法会返回执行上下文被改变的函数而不会立即执行该函数,而是返回一个函数,可以自行选择调用方;而前两者call与apply都是直接执行该函数,直接返回结果,bind的参数和call相同;
-
他们俩之间的差别在于参数的区别,call和apply的第一个参数都是要改变上下文的对象,而call从第二个参数开始以参数列表的形式展现,apply则是把除了改变上下文对象的参数放在一个数组里面作为它的第二个参数。
- apply 第二个参数为 数组 ,call 第二个参数 为参数列表(1,2,3);
-
-
应用场景:
-
apply 可用于数组的拼接、添加
-
call 可用于判断数据类型 : console.log(Object.prototype.toString.call(arr1)); // [object Array]
-
call 和 apply 可用于 构造函数继承
子函数继承父函数的方法 子函数 { 父函数.call(this) // 继承父函数内部的属性与方法 }
-
82、cookie超出4kb,会怎么样?
- 会报400的错误;
- 解决方案:不要将数据存到cookie中,存到localstorage之中;
83、单页面与多页面的区别?使用场景?
-
单页面(SPA):
-
含义: SPA应用程序只有一个html文件,在vue中可以通过
vue-router
来局部切换组件,而非刷新整个页面,来实现无刷新切换页面的技术 ; -
原理: js会感知到url的变化,通过这一点可以用js监听url中hash值的变化,通过onhashchange事件,由于哈希值的变换并不会引发页面的刷新和跳转,当监听到hash变化,就可以动态的切换组件,就可以实现无刷新切换页面技术 ; 在vue-router中 vue不支持onhashchange事件,它希望你使用vue-router中的钩子函数解决 ;
-
优点:
- 页面切换快
- 用户体验好
-
缺点:
- 首屏加载速度慢
- 不易于SEO
-
-
多页面(MAP):
- 含义原理: 指有多个独立页面的应用(多个html页面),每个页面必须重复加载js、css等相关资源。多页应用跳转,需要整页资源刷新。
- 优点:1、首屏加载速度快;2、SEO效果好;
- 缺点:1、页面切换慢;2、用户体验欠佳;
-
两者的对比分析,如下表:
84、作用域插槽与具名插槽的区别?及使用?
-
插槽: 让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件 ===> 子组件 。
-
具名插槽:
- 有多个插槽 ,父组件为了区分不同插槽, 就要用slot标签的name属性来标识了 ;
- 父组件要决定在什么插槽里面放什么内容,就要将name的值赋值给slot属性传递给对应的插槽。如果slot没有name属性,就是匿名插槽了,而父组件中不指定slot属性的内容,就会被丢到匿名插槽中
-
作用域插槽:
- 数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)
85、BFC 块级格式化上下文
-
形成条件
- 浮动元素、绝对定位等,脱标后,父级元素高度塌陷;
-
BFC是什么?
- 形成独立渲染的区域,内部元素渲染不影响外界;
-
应用场景
- 常用于清除浮动,设置overflow:hidden;
86、Vue2和Vue3的区别?
-
数据的双向绑定的区别
-
Vue3支持多个根节点、vue2只能有一个根节点
-
vue2和vue3最大的区别就是,vue3使用了Composition API (组合api)
-
生命周期变化:
- beforeCreate -> 使用 setup()
- created -> 使用 setup()
- beforeMount -> onBeforeMount
- mounted -> onMounted
- beforeUpdate -> onBeforeUpdate
- updated -> onUpdated
- beforeDestroy -> onBeforeUnmount
- destroyed -> onUnmounted
-
vue2和vue3的diff算法
-
vue2:
- vue2 diff算法就是进行虚拟节点对比,并返回一个patch对象,用来存储两个节点不同的地方,最后用patch记录的消息去局部更新Dom。vue2 diff算法会比较每一个vnode,而对于一些不参与更新的元素,进行比较是有点消耗性能的。
-
vue3:
- vue3 diff算法在初始化的时候会给每个虚拟节点添加一个patchFlags,patchFlags就是优化的标识。 虚拟diff对比时,只会比较patchFlags发生变化的vnode,进行更新视图,对于没有变化的元素做静态标记,在渲染的时候直接复用。
-
-
v-if与v-for的优先级
- vue2是v-for优先,vue3则是v-if优先;
87、React与Vue有什么不同?
-
核心设计思想上不同
- vue是 渐进式双向绑定的MVVM框架 ;
- React的核心思想是声明式渲染和组件化、单向数据流 ;
-
组件写法的不同
- Vue的组件写法是通过template的单文件组件格式 ;
- React的组件写法是JSX+inline style,也就是吧HTML和CSS全部写进JavaScript中。
-
响应式原理不同
- React主要是通过setState()方法来更新状态,状态更新之后,组件也会重新渲染。
- vue会遍历data数据对象,使用Object.definedProperty()将每个属性都转换为getter和setter 。
88、图片懒加载的原理
- 图片懒加载的原理很简单,就是我们先设置图片的data-set属性(当然也可以是其他任意的,只要不会发送http请求就行了,作用就是为了存取值)值为其图片路径,由于不是src,所以不会发送http请求。 然后我们计算出页面scrollTop的高度和浏览器的高度之和,如果图片举例页面顶端的坐标Y(相对于整个页面,而不是浏览器窗口)小于前两者之和,就说明图片就要显示出来了(合适的时机,当然也可以是 其他情况),这时候我们再将 data-set属性替换为src属性即可。
89、浏览器兼容问题?
- 因为有时候旧的浏览器不支持ES6等的新的js新语法,可以用Babel将代码进行更低版本的转换,在babel-loader里面babel-core配置;
- 旧的浏览器对一些HTML5和css3的新语法不支持,比如:css3中flex布局,可以用float代替;html5中的语义化标签用div替代;
- 一些标签默认样式在各个浏览器显示的都不相同,解决问题需要自行封装样式;