Vue面试题
js闭包:
能够读取其他函数内部变量的函数
-
可以延长变量的生命周期 可以创建私有的环境
-
将变量始终保存在内存中 可以封装对象的私有属性和方法
-
坏处是消耗内存、内存溢出
防抖和节流
- 防抖:触发事件后,在n秒内,事件只执行一次;n秒内再次触发,时间重新计算(下拉触底加载下一页)
- 节流:连续发生的事件在n秒内,只执行一次(搜索查询)
vue中的data为什么是一个函数
- 为了保证组件的独立性和可复用性,如果 data 是个函数的话,每复用一次组件就会返回新的 data,类似于给每个组件实例创建一个私有的数据空间,保护各自的数据互不影响
Js的数据类型
- ES5的5种:*Null,undefined,Boolean,Number,String, ES6新增:Symbol表示独一无二的值 ES10新增:BigInt 表示任意大的整数*
null和undefined的区别
- 在 if 语句中 null 和 undefined 都会转为false两者用相等运算符比较也是相等;首先 Undefined 和 Null 都是基本数据类型,这两个基本数据类型分别都只有一个值,就是 undefined 和 null。
- undefined 代表的含义是未定义;null 代表的含义是空对象
数据类型存储以及堆栈内存是什么
- 基本数据类型**:**直接存储在栈内存中,占据空间小,大小固定,属于被频繁使用的数据。
- 引用数据:类型将指针存在栈中,将值存在堆中。 当我们把对象值赋值给另外一个变量时,复制的是对象的指针,指向同一块内存地址
堆(heap)和栈(stack)有什么区别存储机制
栈: 是一种连续储存的数据结构,具有先进后出后进先出的性质。
堆: 是一种非连续的树形储存数据结构,具有队列优先,先进先出;
var let const区别
- var: 存在变量提升;存在变量覆盖,已经被定义且赋值的变量,如果再次被赋值,则以后一次值为准;没有块级作用域;
- const:定义的是常量,声明之后必须赋值;定义的值不能去修改,否则报错;有块级作用域;不存在变量提升和变量覆盖;对于数组和对象的元素修改,不算做对常量的修改,不会报错。
- let: 有块级作用域;不存在变量提升和变量覆盖;let不允许在相同的作用域中重复声明,注意是相同作用域,不同作用域重复声明不会报错
解构赋值
- let a=1;let b=2 交换变量 [a,b]=[b,a]
es6去重
- let item = […new Set(arr)]
Promise面试题
- promise的构造函数是同步执行的,当new Promise的一瞬间,1,2 就立刻被执行,而 .then方法是异步执行的,当执行完1和2之后,会执行输出4,最后执行输出3
MVC和MVVM
-
MVC:M(model数据)、V(view视图),C(controlle控制器)缺点是前后端无法独立开发,必须等后端接口做好了才可以往下走;前端没有自己的数据中心,太过依赖后台
-
MVVM:M(model数据)、V(view视图)、VM(viewModel控制数据的改变和控制视图)
html部分相当于View层,可以看到这里的View通过通过模板语法来声明式的将数据渲染进DOM元素,当ViewModel对Model进行更新时,通过数据绑定更新到View。 Vue实例中的data相当于Model层,而ViewModel层的核心是Vue中的双向数据绑定,即Model变化时VIew可以实时更新,View变化也能让Model发生变化。 -
MVVM与MVC最大的区别就是:它实现了View和Model的自动同步,也就是当Model的属性改变时,我们不用再自己手动操作Dom元素,来改变View的显示,而是改变属性后该属性对应View层显示会自动改变
v-model原理
- 是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调从而达到数据和视图同步。
vue中的data
- 实际上就是一个闭包,因为vue是单页面应用,是由很多组件构成,每一个组件中都有一个data,所以通过闭包给每一个组件创建了一个私有的作用域,这样就不会相互影响。
v-if和v-show
- v-if是通过添加和删除元素进行显示或者隐藏
- v-show是通过操作DOM修改display样式来修改元素的显示和隐藏
v-for为什么有key
-
key可以提高虚拟DOM的更新效率;key 只能是字符串或者number,其他类型不可以
-
key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,随后Vue进行【新的虚拟DOM】与【旧的虚拟DOM】差异比较
-
旧虚拟DOM找到了与新虚拟DOM相同的key:
若虚拟DOM中内容没变,直接使用之前的真实DOM
若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM
用index作为key可能会引发的问题
- 若对数据进行:逆序添加、逆序删除等破坏顺序的操作,会产生没有必要的真实DOM更新==>界面效果没问题,但效率低
- 如果结构中还包含输入类的DOM,会产生错误的DOM更新 ==> 界面有问题
打包后dist目录过大
- dist打包生成的文件中有 .map 文件,可以删除。在 vue.config.js文件中配置:productionSourceMap: false
- 组价和路由使用懒加载、按需引入等
- 对于文件和图片进行压缩。
watch和computed的区别
-
computed能完成的功能,watch都可以完成
watch能完成的小功能,computed不一定能完成。(watch可以进行异步操作)
-
所有被vue管理的函数,最好写成普通函数,这样this的指向才是vm或组件实例对象
-
所有不被vue管理的函数(定时器的回调、ajax的回调、promise的回调等),最好写成箭头函数,这样this的指向才是vm或组件实例对象
vue组件之间的数据传递
-
父→子
通过给子组件身上绑定自定义属性,然后再子组件里使用props属性来接收即可
-
子→父
通过父组件给子组件传递函数类型的props实现:子组件给父组件传递数据;通过父组件给子组件绑定一个自定义事件实现:子组件给父组件传递数据(子组件$emit);通过父组件给子组件绑定一个自定义事件实现:使用ref实现
全局事件总线
- main.js:将全局事件bus,挂载到Vue的原型上,这样所有的组件都可以使用
消息订阅与发布
- 一种组件间的通信方式,适用于任意组件间通信。
跨域
- webpack 里的proxy
- 配置多个域名在map中 只有配置过的允许跨域
- jsonp(需要后端支持)
git命令
- git init 初始化git仓库
- git add 文件列表 追踪文件
- git commit -m 提交信息 向仓库中提交代码
- git log 查看提交记录
- git status 查看文件状态
get与post区别
- get是从服务器上获取数据,post是向服务器传送数据。
- post比get安全,因为数据在地址栏上不可见。
- get使用url、cookie传参
cookie、localStorage、sessionStorage
- sessionStorage:仅在当前浏览器窗口关闭之前有效
- localStorage:始终有效,窗口或浏览器关闭也一直保存,本地存储,因此用作持久数据;
- cookie:只在设置的cookie过期时间之前有效,即使窗口关闭或浏览器关闭
- sessionStorage:不在不同的浏览器窗口中共享,即使是同一个页面;
- localstorage:在所有同源窗口中都是共享的;也就是说只要浏览器不关闭,数据仍然存在
- 也是在所有同源窗口中都是共享的.也就是说只要浏览器不关闭,数据仍然存在
async和await
- async是来定义函数的,定义异步函数,打印函数名可以得到一个promise对象,言外之意可以通过这个 函数名称.then 这个方法
- async 内部实现,有返回值 成功返回promise.resolve() ,出错返回promise.reject() 返回值用catch捕获
- await 后面跟的是任意表达式,一般使用promise的表达式
- async和await 属于es7语法
- promise es6语法,promise中包含catch,async需要自己定义catch
setTimeout时间为0以及误差的原因
- setTimeout,如果时间为0,则会立即插入队列,不是立即执行,等待前面的代码执行完毕。
js的数据类型
- 基本数据类型:Boolean、Number、String、Null、Undefined
- 复杂数据类型: Object、Array、Function、Date
js变量提升
-
在js中,变量和函数的声明会被提升到最顶部执行
函数提升高于变量的提升
函数内部如果用var声明了相同名称的外部变量,A函数将不再向上寻找
匿名函数不会提升
this指向
-
this总是指向函数的直接调用者。
如果有new关键字,this指向new出来的对象
在事件中,this指向触发这个事件的对象
普通函数和箭头函数区别
- 箭头函数没有原型,原型是undefined
- 箭头函数this指向全局对象,而函数指向引用对象
- call,apply,bind方法改变不了箭头函数的指向
slot插槽
-
slot插槽,可以理解为slot在组件模板中提前占据了位置,当复用组件时,使用相关的slot标签时,标签里的内容就会自动替换组件模板中对应slot标签的位置,作为承载分发内容的出口
-
主要作用是:复用和扩展组件,做一些定制化组件的处理
map和forEach
-
forEach方法,是最基本的方法,遍历和循环。默认有3个参数:分别是遍历的每一个元素item,遍历的索引index,遍历的数组array
map方法,和foreach一致,不同的是会返回一个新的数组,所以callback需要有return返回值,如果没有会返回undefined
箭头函数和普通函数区别
-
函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象
不可以当作构造函数,也就是说不可以使用new命令,否则会报错
不可以使用arguments对象,该对象在函数体内不存在,如果要用可以使用Rest参数代替
不可以使用yield命令,因此箭头函数不能用作Generator函数
es6新增
-
新增模版字符串
箭头函数
增加let、const来声明变量
for-of用来遍历数据-例如数组中的值
解构赋值
新增简单数据类型Symbol,独一无二的,不会与其他属性名冲突
将Promise对象纳入规范,提供了原生的Promise对象
attribute 和 property 的区别
- attribute 是 dom 元素在文档中作为 html 标签拥有的属性
- property 就是 dom 元素在 js 中作为对象拥有的属性。
- 对于 html 的标准属性来说,attribute 和 property 是同步的,是会自动更新的
- 但是对于自定义的属性来说,他们是不同步的
内存泄漏(堆内存无法释放)原因
- 全局变量
- dom 清空时,还存在引用
- 定时器未清除
- 子元素存在引起的内存泄露
script 引入方式
<script defer>
: 异步加载,元素解析完成后执行<script async>
: 异步加载,但执行时会阻塞元素渲染
JS垃圾回收机制是怎样的
-
垃圾回收机制就是不停歇的寻找这些不再使用的变量,并且释放掉它所指向的内存。
-
标记清除:大部分浏览器使用这种垃圾回收,当变量进入执行环境(声明变量)的时候,垃圾回收器将该变量进行了标记,当该变量离开环境的时候,将其再度标记,随之进行删除。
引用计数:这种方式常常会引起内存的泄露,主要存在于低版本的浏览器。它的机制就是跟踪某一个值得引用次数,当声明一个变量并且将一个引用类型赋值给变量得时候引用次数加1,当这个变量指向其他一个时引用次数减1,当为0时出发回收机制进行回收。
逐进增强和优雅降级
-
逐进增强
针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高版本浏览器进行效果、交互等改进和追加功能达到更好的用户体验。
-
优雅降级
一开始就构建完整的功能,然后再针对低版本浏览器进行兼容
让CSS只在当前组件中起作用
- scoped
<keep-alive></keep-alive>
的作用是什么
- 主要是用于需要频繁切换的组件时进行缓存,不需要重新渲染页面
如何获取dom
- 给dom元素加ref=‘refname’,然后通过this.$refs.refname进行获取dom元素
vue-loader是什么
- vue文件的一个加载器,将template/js/style转换为js模块
为什么用key
- 给每个dom元素加上key作为唯一标识 ,diff算法可以正确的识别这个节点,使页面渲染更加迅速
$nextTick的使用
- 在data()中的修改后,页面中无法获取data修改后的数据,使用$nextTick时,当data中的数据修改后,可以实时的渲染页面
单页面应用和多页面应用区别及缺点
-
单页面应用(SPA),通俗的说就是指只有一个主页面的应用,浏览器一开始就加载所有的js、html、css。所有的页面内容都包含在这个主页面中。但在写的时候,还是分开写,然后再加护的时候有路由程序动态载入,单页面的页面跳转,仅刷新局部资源。多用于pc端。
-
多页面(MPA),就是一个应用中有多个页面,页面跳转时是整页刷新
-
单页面的优点:用户体验好,快,内容的改变不需要重新加载整个页面,基于这一点spa对服务器压力较小;前后端分离,页面效果会比较酷炫
-
单页面缺点:不利于seo;导航不可用,如果一定要导航需要自行实现前进、后退。初次加载时耗时多;页面复杂度提高很多。
vue和jQuery的区别
- jQuery是使用选择器( )选取 D O M 对象,对其进行赋值、取值、事件绑定等操作,其实和原生的 H T M L 的区别只在于可以更方便的选取和操作 D O M 对象,而数据和界面是在一起的。比如需要获取 l a b e l 标签的内容: )选取DOM对象,对其进行赋值、取值、事件绑定等操作,其实和原生的HTML的区别只在于可以更方便的选取和操作DOM对象,而数据和界面是在一起的。比如需要获取label标签的内容: )选取DOM对象,对其进行赋值、取值、事件绑定等操作,其实和原生的HTML的区别只在于可以更方便的选取和操作DOM对象,而数据和界面是在一起的。比如需要获取label标签的内容:(“lable”).val();,它还是依赖DOM元素的值。
- Vue则是通过Vue对象将数据和View完全分离开来了。对数据进行操作不再需要引用相应的DOM对象,可以说数据和View是分离的,他们通过Vue对象这个vm实现相互的绑定。这就是传说中的MVVM。
vue获取数据在一般在哪个周期函数
- created
beforeMount
mounted
vue生命周期
vuex有哪几种属性
- 有五种,State、 Getter、Mutation 、Action、 Module
state: 基本数据(数据源存放地)
getters: 从基本数据派生出来的数据
mutations : 提交更改数据的方法,同步!
actions : 像一个装饰器,包裹mutations,使之可以异步。
modules : 模块化Vuex
vue路由跳转
- 声明式导航router-link
- this.$router.push():query传参;不带参数;params传参;直接path
- query类似 get,跳转之后页面 url后面会拼接参数,类似?id=1。
非重要性的可以这样传,密码之类还是用params,刷新页面id还在。
params类似 post,跳转之后页面 url后面不会拼接参数。
如何解决数据层级结构太深的问题
如以下代码: span 'v-text="a.b.c.d">
, 可以使用vm.$set
手动定义一层数据: vm.$set("demo",a.b.c.d)
Vue.js页面闪烁
Vue. js提供了一个v-cloak指令,该指令一直保持在元素上,直到关联实例结束编译。当和CSS一起使用时,这个指令可以隐藏未编译的标签,直到实例编译结束。
$route
和$router
的区别
- $route是“路由信息对象”,包括path,params,hash,query,fullPath,matched,name等路由信息参数。
- $router是“路由实例”对象包括了路由的跳转方法,钩子函数等
Vue.set 方法原理
在两种情况下修改 Vue 是不会触发视图更新的。
在实例创建之后添加新的属性到实例上(给响应式对象新增属性)
直接更改数组下标来修改数组的值。
### CSS选择器以及优先级的理解
ID选择器、类选择器、标签选择器、属性选择器、伪类选择器、后代选择器
!important > 内联样式 > ID选择器 > 类选择器 > (标签选择器、伪类选择器、属性选择器)
安全性问题
- XSS 攻击: 注入恶意代码
- cookie 设置 httpOnly
- 转义页面上的输入内容和输出内容
- CSRF : 跨站请求伪造,防护:
- get 不修改数据
- 不被第三方网站访问到用户的 cookie
- 设置白名单,不被第三方网站请求
- 请求校验
浏览器内核
HTML和HTML5有什么区别
深拷贝和浅拷贝
深拷贝和浅拷贝是针对复杂数据类型来说的,浅拷贝只拷贝一层,而深拷贝是层层拷贝。
1.浅拷贝:
将原对象或原数组的引用直接赋给新对象,新数组,新对象只是对原对象的一个引用,而不复制对象本身,新旧对象还是共享同一块内存
如果属性是一个基本数据类型,拷贝就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址,
2.深拷贝:
创建一个新的对象和数组,将原对象的各项属性的“值”(数组的所有元素)拷贝过来,是“值”而不是“引用”
深拷贝就是把一个对象,从内存中完整的拷贝出来,从堆内存中开辟了新区域,用来存新对象,并且修改新对象不会影响原对象
3、赋值:
当我们把一个对象赋值给一个新的变量时,赋的是该对象在栈中的内存地址,而不是堆中的数据。也就是两个对象
立即执行函数
立即执行函数会形成一个单独的作用域,我们可以封装一些临时变量或者局部变量,避免污染全局变量。
arguments
arguments 当我们不知道有多少个参数传进来的时候就用 arguments 来接收,是一个类似于数组的对象,他有length属性,可以arguments[ i ]来访问对象中的元素, 但是它不能用数组的一些方法。
call、apply、bind
都是来改变this指向和函数的调⽤,实际上call与apply的功能是相同的,只是两者的传参方式不一样,
call⽅法跟的是⼀个参数列表,
apply跟⼀个 数组作为参数,call⽅法和apply使⽤后就直接调⽤
bind 传参后不会立即执行,而是返回一个改变了this指向的函数,这个函数可以继续传参,且执行,需要类似于bind()()两个括号才能调⽤。