1、做接口前后端联调如何分析是前端还是后端问题
答:
- 抓包请求参数,页面上输入参数,后端接口文档需要的参数是否一致,如果不一致就是前端bug
- 如果一致没有返回预期的效果,就是后端bug;
- 渲染中,参数一致,也返回预期效果,但是页面数据显示不正确就是前端bug;
也可以用http状态说明,一般4XX,5XX是后端问题,其它是前端问题
2、cookies,sessionStorage和localStorage都有什么区别
答:
- cookies在请求数据传输的时候会携带至后台,sessionStorage,localStorage是纯客户端缓存
- cookies有过期机制,sessionStorage是会话级缓存,窗口关闭后会销毁,localStorage是本地持久缓存手动才会清除
- cookies只能存储4KB并且不推荐前端用js直接操作,sessionStorage,localStorage存储有5MB
3、箭头函数和function普通函数之间有什么区别?
答:
- 箭头函数中的this指向是固定不变的,只会从自己的作用域的上一层继承,而function定义的函数,this指向会随着调用环境变化而变化。
- 箭头函数没有prototype,不可用new,不能用call和apply绑定,因为没有this.
4、常见的SPA首屏优化方式有哪些?
答:
大家基本都会采用服务器ssr渲染,路由懒加载。
首屏优化的核心是加载和解析的性能。
加载优化:
-
减少http请求,合理使用缓存,再快的网速,再大的并发数都不如直接跳过请求。
-
启用CDN加速:让不同地区的人都可以享受稳定可靠的下载速度
-
静态资源分域,将图片,音视频等静态资源放到不同的域名,防止加载时受浏览器并发数限制
-
DNS预解析,浏览器通过查询系统hosts或者进一步询问本地的DNS服务器就可以获得结果,但是也可能要经历完整的寻址过程:览器缓存-系统缓存-路由器缓存-ISP DNS缓存-递归搜索。DNS解析所需的时间差异非常大,延迟范围可以从1ms(本地缓存结果)到普遍的几秒钟时间。所以利用DNS预解析是有意义的。最明显的例子,DNS预解析在某个页面中包含非常多的域名非常有效,如搜索结果页。如何使用X-DNS_prefetch-Control: on|off,on:启用DNS预解析。
-
尽量启用http2
解析优化 -
资源体验优化:雪碧图,压缩图片,字体,音视频等文件的体积
-
按需加载:
-
使用骨架屏
5、VueX的重要核心属性有哪些?
答:五大核心属性:
- state,用来存放数据,相当于vue里面的data
- mutaions,同步方法,可以修改state的data
- actions,异步方法,可以发送请求,不能直接修改state中的数据,要通过调用mutations里面的方法来修改;
- getters:相当于vue里面的computed,是state中数据的派生数据
- modules,用于上述四个数据的分模块注册
6、什么是跨域?一般怎么解决跨域问题?
答:浏览器的同源策略的三大要素:协议相同、域名相同、端口 相同;同源三要素有一项不符合就是跨域。解决跨域的方法有:通过jsonp,或者通用代理服务器的方法通过本地代理转发。
7、Vue中v-if和v-show的共同点和不同点有哪些?
答:相同点:都能控制元素的显示隐藏.不同点:v-show本质是通过控制display:none;v-if是动态的向DOM树中添加或删除DOM元素,v-if更适合带有权限的操作,v-show更适合日常使用。可以减少数据渲染。
8、vue有哪些优点?
答:
- 简单易学,文档资料集全,UI库很多,社区完善
- 轻量级,MVVM框架
- 双向数据绑定,页面渲染更加高效
- 组件化开发,组件复用
- Virtaual DOM,操作DOM非常耗性别,而虚拟DOM操作属于预处理,极大解放dom操作
9、vue2.0与3.0的区别
答:
- virtual DOM 完全重写,mounting & patching 提速 100%;
- 默认进行懒观察(lazy observation):在 2.x 版本里,不管数据多大,都会在一开始就为其创建观察者。当数据很大时,这可能会在页面载入时造成明显的性能压力。3.x 版本,只会对「被用于渲染初始可见部分的数据」创建观察者,而且 3.x 的观察者更高效。
- 部分命令发生了变化:比如启动项目 npm run serve
- 默认项目目录结构也发生了变化:如移除了配置文件目录,config 和 build 文件夹
- 数据双向绑定:放弃 Object.defineProperty ,使用更快的原生 Proxy;
- 3.0 新加入了 TypeScript 以及 PWA 的支持
- vue2和vue3生命周期钩子函数的不同
Vue2--------------vue3 beforeCreate -> setup() created -> setup() beforeMount -> onBeforeMount mounted -> onMounted beforeUpdate -> onBeforeUpdate updated -> onUpdated beforeDestroy -> onBeforeUnmount destroyed -> onUnmounted activated -> onActivated deactivated -> onDeactivated errorCaptured -> onErrorCaptured
- vue2和vue3定义数据变量和方法的改变:在vue2中定义数据变量是data(){},创建的方法要在methods:{}中。而在vue3中直接在setup(){}中,在这里面定义的变量和方法因为最终要在模板中使用,所以最后都得 return。
10、为什么最好把link放在head之间?
答:第一:规范要求;第二:从页面渲染原理来说,CSS的解析和html的解析需要同时进行,CSS的解析不会阻塞页面渲染。html解析得到的dom树需要css解析的样式规则,然后通过加流重绘计算后交给GPU负责合并图层生成页面。所以在不阻塞的情况下,你是选择串行还是并行呢,有什么更好的理由不让放head呢?第三:如果不放head会出现:一些浏览器会阻止渲染导致页面不能逐步呈现,没有样式或者空白不利用户体验。
11、说一下CSS选择器有哪些?
答:元素,id,类,分组,兄弟元素,伪类,属性,通配,复合,子元素,后代元素选择器
12、ES5数据结构中Set和Map有什么区别?
答:set可以用于数组去重,因为set值唯一,而map不唯一,map存储的是键值对,可以通过get方法取值而set不行,set存储的是值
13、为什么要封装axios?
答:封装之后,相当于给axios给出规范,使用起来更加简单,调用 方式统一,利于代码的管理和维护
14、js中bind,call,apply之间有什么区别?
答:三者都是重定义对象中的this:call跟apply绑定完this会立即调用当前的函数,apply传入的参数必须是数组,bind通常绑定完this返回不会立即调用当前函数,而是将函数返回。
- obj.method.bind(“obj中this的指向对象“,”参数“,”参数”)();//bind返回新函数,需要调用
- obj.method.call(“obj中的this的指向对象”,“参数”,“参数”);
- obj.method.apply(“obj中的this的指向对象”,[‘参数’,‘参数’]);
15、什么是回流和什么是重绘?什么情况下使用?
回流:也有称重排,当渲染树节点发生改变,影响了节点的几何属性(如宽、高、内边距、外边距、或是float、position、display:none;等等),导致节点位置发生变化,此时触发浏览器重排(reflow),需要重新生成渲染树。
重绘:渲染树节点发生改变,但不影响该节点在页面当中的空间位置及大小。譬如某个div标签节点的背景颜色、字体颜色等等发生改变,但是该div标签节点的宽、高、内外边距并不发生变化,此时触发浏览器重绘(repaint)
注意:回流必将引起重绘,而重绘不一定会引起回流。
何时回引起回流?
1、添加或者删除可见的DOM元素;2、元素位置改变——display、float、position、overflow等等;3、元素尺寸改变——边距、填充、边框、宽度和高度4、内容改变——比如文本改变或者图片大小改变而引起的计算值宽度和高度改变;5、页面渲染初始化;6、浏览器窗口尺寸改变——resize事件发生时;
如何避免回流?
1、将动画效果应用到position属性为absolute或fixed的元素上,否则会引起父元素及后续元素频繁回流。
2、避免频繁操作DOM树,可以先为元素设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘。
3、避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。
16、Vue组件间是如何通信的?
1、父子组件间通信;父向子传递数据是通过 props,子向父是通过 events(
e
m
i
t
)
;
通
过
父
链
/
子
链
也
可
以
通
信
(
emit);通过父链 / 子链也可以通信(
emit);通过父链/子链也可以通信(parent /
c
h
i
l
d
r
e
n
)
;
r
e
f
也
可
以
访
问
组
件
实
例
;
p
r
o
v
i
d
e
/
i
n
j
e
c
t
A
P
I
;
children);ref 也可以访问组件实例;provide / inject API;
children);ref也可以访问组件实例;provide/injectAPI;attrs/
l
i
s
t
e
n
e
r
s
(
listeners(
listeners(attrs与
l
i
s
t
e
n
e
r
s
是
两
个
对
象
,
listeners 是两个对象,
listeners是两个对象,attrs 里存放的是父组件中绑定的非 Props 属性,
l
i
s
t
e
n
e
r
s
里
存
放
的
是
父
组
件
中
绑
定
的
非
原
生
事
件
。
)
2
、
兄
弟
通
信
B
u
s
;
V
u
e
x
3
、
跨
级
通
信
B
u
s
;
V
u
e
x
;
p
r
o
v
i
d
e
/
i
n
j
e
c
t
A
P
I
、
listeners里存放的是父组件中绑定的非原生事件。) 2、兄弟通信Bus;Vuex 3、跨级通信Bus;Vuex;provide / inject API、
listeners里存放的是父组件中绑定的非原生事件。)2、兄弟通信Bus;Vuex3、跨级通信Bus;Vuex;provide/injectAPI、attrs/$listeners
17、vuex有哪些辅助函数,都有什么作用
vuex中提供了一些非常方便的辅助函数,比如mapState、mapGetter、mapMutation、mapAction等。
mapGetters和mapState用法一样,通过简单的…mapState([‘userName’])来代替比较长的计算属性函数在computed中调用即可。
mutations和actions返回的是函数,所以应该放在组件的methods属性中。
18、document.load与document.ready的区别?
load是页面加载完成后(包括所有的资源DOM文档树,css文件,js文件,图片资源)执行的一个函数;
ready是页面DOM树加载完成(不包括CSS,图片)执行的一个函数;
19、Vue的常见修饰符有哪些,各有什么作用?
Vue的常见修饰符:
事件修饰符:
.stop 阻止事件继续传播
.once 事件将只会触发一次
.prevent 阻止标签默认行为
.native可以理解为该修饰符的作用就是把一个 Vue 组件转化为一个普通的 HTML 标签(<My-component @click.native=“shout(3)”>)
.passive当我们在监听元素滚动事件的时候,会一直触发 onscroll 事件,在 PC 端是没啥问题的,但是在移动端,会让我们的网页变卡,因此我们使用这个修饰符的时候,相当于给 onscroll 事件整了一个 .lazy 修饰符。
表单修饰符:
v-model.lazy 转化为change事件再同步
v-model.number转化为数字内型
v-bind修饰符
.sync能对props进行一个双向绑定
.prop通过自定义属性存储变量,避免暴露数据,防止污染 HTML 结构;
20、javascript如何检测一个字符串类型?
JS数据类型主要分为两大类:基本数据类型和引用数据类型
基本数据类型:number、string、boolean、null、undefined、symbol(es6)
引用数据类型:object(array、function、date…)
1)typeof str == “string”:typeof 一般用来判断基本数据类型,除了判断null会输出"object",其它都是正确的,typeof 判断引用数据类型时,除了判断函数会输出"function",其它都是输出"object"
2)instanceof 可以准确的判断引用数据类型,它的原理是检测构造函数的prototype属性是否在某个实例对象的原型链上
console.log([] instanceof Array); // true
console.log(function(){} instanceof Function); // true
console.log({} instanceof Object);
3)constructor(构造函数):null和undefined是无效的对象,所以他们不会有constructor属性!
4) Object.prototype.toString.call()
Object.prototype.toString.call(‘’) ; // [object String]
Object.prototype.toString.call(1) ; // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(Symbol()); // [object Symbol]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
Object.prototype.toString.call(new Error()) ; // [object Error]
Object.prototype.toString.call(document) ; // [object HTMLDocument]
Object.prototype.toString.call(window) ; // [object global] window 是全局对象 global 的引用
21、js中new操作符做了什么事情
new操作符的主要作用是产生对象。通过new创建空对象,为创建对象打基底,开辟了内存,将this 指针指向这个对象。
22、javascript如何实现继承,有什么优缺点?
JavaScript常见的继承方式:
1)原型链继承
function Parent() {
this.name = 'parent1';
this.play = [1, 2, 3]
}
function Child() {
this.type = 'child2';
}
Child.prototype = new Parent();
let child1 = new Child()
console.log(child1) // Parent { type: 'child2' }
console.log(child1.name) // parent1
优点:不仅能继承父类的实例属性和方法,还能继承原型属性或者方法
缺点:父类的引用属性被共享了,改动一处其他的也会被改
2)构造函数继承(借助 call)
function Parent2(){
this.name = 'parent1';
this.age = '18';
}
Parent2.prototype.getName = function () {
return this.name;
}
function Child(){
Parent2.call(this);
this.type = 'child'
}
let child = new Child();
console.log(child); // 没问题, Child { name: 'parent1', age: '18', type: 'child' }
console.log(child.getName()); // 会报错 child.getName is not a function
优点:父类的引用属性不会被共享,优化了第一种继承方式的弊端
缺点:但是只能继承父类的实例属性和方法,不能继承原型属性或者方法
3)组合继承
前面我们讲到两种继承方式,各有优缺点。组合继承则将前两种方式继承起来
function Parent3 () {
this.name = 'parent3';
this.play = [1, 2, 3];
}
Parent3.prototype.getName = function () {
return this.name;
}
function Child3() {
// 第二次调用 Parent3()
Parent3.call(this);
this.type = 'child3';
}
// 第一次调用 Parent3(), 原型链继承
Child3.prototype = new Parent3();
// 手动挂上构造器,指向自己的构造函数
Child3.prototype.constructor = Child3;
var s3 = new Child3();
var s4 = new Child3();
s3.play.push(4);
console.log(s3.play, s4.play); // 不互相影响,[ 1, 2, 3, 4 ] [ 1, 2, 3 ]
console.log(s3.getName()); // 正常输出'parent3'
console.log(s4.getName()); // 正常输出'parent3'
这种方式看起来就没什么问题,方式一和方式二的问题都解决了,但是从上面代码我们也可以看到Parent3 执行了两次,造成了多构造一次的性能开销
4)原型式继承:Object.create方法实现普通对象的继承
let parent4 = {
name: “parent4”,
friends: [“p1”, “p2”, “p3”],
getName: function() {
return this.name;
}
};
let person4 = Object.create(parent4)
5)寄生组合式继承
class Person {
constructor(name) {
this.name = name
}
// 原型方法
// 即 Person.prototype.getName = function() { }
// 下面可以简写为 getName() {...}
getName = function () {
console.log('Person:', this.name)
}
}
class Gamer extends Person {
constructor(name, age) {
// 子类中存在构造函数,则需要在使用“this”之前首先调用 super()。
super(name)
this.age = age
}
}
const asuna = new Gamer('Asuna', 20)
asuna.getName() // 成功访问到父类的方法,Person: Asuna
寄生组合式继承,借助解决普通对象的继承问题的Object.create 方法,在前面几种继承方式的优缺点基础上进行改造,这也是所有继承方式里面相对最优的继承方式
23、改变this指针的方向
1)call、apply、bind三者为改变this指向的方法
call
function fn(a,b,c){
console.log(this,a+b+c); // this指向window
}
fn();
fn.call(document,1,2,3);//call改变之后this指向document
//输出 #document 6 1,2,3是实参 结果相加为6
fn.apply(document,[1,2,3]); //apply
let ff = fn.bind('小明',1,2,3); //手动调用一下
call、apply与bind区别:前两个可以自动执行,bind不会自动执行,需要手动调用
call、bind与apply区别:前两个都有无数个参数,apply只有两个参数,而且第二个参数为数组
24、JS中同步和异步执行顺序
JavaScript语言是单线程,就意味着所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。同步任务指的是:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。异步任务指的是,不进入主线程、而进入任务队列(task queue)的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
25、keep-alive属性及生命周期
生命周期 用的是激活(activated)/停用(deactivated)。如何解决keep-alive缓存:第一种:使用会将数据保留在内存中;第二种:beforeRouteLeave(to, from, next) {
// 设置下一个路由的 meta
to.meta.keepAlive = false;
// C 跳转到 A 时让 A 不缓存,即刷新
next();
},第三种每次组件渲染的时候,都会执行beforeRouteEnter
beforeRouteEnter(to, from, next){
next(vm=>{
console.log(vm)
// 每次进入路由执行
vm.getData() // 获取数据
})
},
26、webpack代码分隔的实现
1)多入口打包2)动态引入
27、遍历数组的方法?
1) indexOf() 遍历数组,返回元素在数组中第一次出现的下标,两个参数,第一个是要查找的元素,第二个是开始查找的下标
2) forEach() 遍历数组获取每一个元素,没有返回值;
3)map() 遍历数组,返回一个新数组,数组由参数里的返回值组成.必须使用return.
4) filter() 遍历数组,返回一个新数组:新数组由参数里,条件为true的元素组成
5)some() 遍历数组,返回参数函数里符合条件的元素,只要检测到有一个元素符合条件就return.
6)reduce() 遍历数组, 返回参数函数里的返回值.一般作为累加器计算总价
7) every() 遍历数组,判断数组元素是否全部符合函数参数里的条件,全部满足返回true,否则false.
8)for of 遍历数组/字符串
前端学习群,需要的要我jjljzh