vue2.0兼容ie那个版本以上?
不支持ie8,部分兼容ie9,完全兼容ie10,因为vue的响应式原理是基于es5的Object.defineProperty()
,这个方法不支持ie8及以下
解释MVVM模式
MVVM是Model-View_ViewModel的缩写
model:数据层,处理业务逻辑和数据 (存储数据及对数据的处理如增删改查)
View:视图层,代表UI视图,负责数据的展示
viewModel:业务逻辑层,负责监听model中数据的改变并控制视图的更新,处理用户操作
model和view无直接关联,通过viewModel进行联系,model和viewModel有着双向绑定的联系,因此当model中数据改变会触发view层的刷新,view中用户交互操作而改变的数据也会在model中同步
vue特点
- 数据驱动:自动计算属性和追踪依赖的表达式
- 组件化:可以复用,解耦的组件来构造页面 (实现了html的封装和重用)
- 轻量:代码量小,不依赖其他库 (大小只有几十kbs)
- 快速:精确有效的批量更新DOM
- (视图、数据、结构分离)
Vue的优点?vue的缺点?
-
优点:
渐进式、组件化、轻量级、虚拟dom、响应式、单页面路由、数据与视图分离
-
缺点:
单页面不利于seo,不支持ie8及以下,首屏加载时间长
vue两个核心
- 数据驱动 (数据渲染:ViewModel,保证数据和视图的一致性)
- 组件化 (模板语法:UI全部由组件数构成)
请说出vue.cli项目src目录中每个文件夹和文件的用法
assets放静态文件 components放组件 router定义路由相关的配置 app.vue是应用主组件 main.js是入口文件
assets和static的区别
相同点:两者都是存放静态资源文件的
不同点:assets存放的文件,在build(打包)的时候,会进行打包,压缩体积上传,
static不会进行打包压缩流程,而是直接进入打包好的目录,直接上传服务器,因为跳过了这个流程,在打包时会提高一定的效率,但因为这一点,没有压缩,会占用服务器更大空间
(建议:项目中style,js等文件可以放在assets,走打包压缩这一流程。而第三方库,例如iconfont.css等文件可以放在static,因为这些文件已经经过处理)
vue和jquery的区别
- jquery是直接操作dom,vue数据和视图是分离的,不直接操作dom,只需要操作数据即可
- jquery操作dom行为是频繁的,而vue利用虚拟DOM技术,大大提高了更新DOM的性能
- vue集成的一些库,大大提高开发效率,比如vuex,router等
父子组件通信
父传子:将数据绑定在子组件上,子组件props接收
子传父:子组件通过$emit方法传递,触发父组件event事件
prop验证的type类型有哪几种?
String
、 Number
、 Boolean
、 Array
、 Object
、 Date
、 Function
、 Symbol
、
为什么vue组件中,data要是一个函数?
对象为引用类型,当重用组件时,由于数据对象都指向同一个内存地址,当在一个组件中修改时,其他重用的组件中的data会同时被修改,
而使用返回对象的函数,由于每次返回的都是一个新的对象(object实例),引用地址不同,则不会出现这个问题
vue组件通讯都有什么方式?
- 父传子:将数据绑定在子组件上,子组件props接收
- props 和 $emit。(父组件向子组件传递数据是通过props传递的,子组件传递给父组件是通过$emit触发事件来做到的。)
- $parent 和 $children 获取当前组件的父组件和当前组件的子组件。
- $refs 获取组件实例。
- envetBus 兄弟组件数据传递,这种情况下可以使用事件总线的方式。
- vuex 状态管理。
组件的依赖注入作用?
解决问题:多层组件嵌套?
使用场景:如果父组件下很多个子孙组件都要用祖先组件中的一个数据,这时候就要用到组件的依赖注入。子组件可以用this.$parent访问父组件的实例,孙子组件可以用this.$parent.$parent来访问,那曾孙子组件呢,是不是要写很多个$parent。
provide 选项应该是一个对象或返回一个对象的函数。该对象包含可注入其子孙的 property。
inject 选项应该是一个字符串数组,在可用的注入内容中搜索用的 key
value是对象(引用类型)才能实现响应式,如果是基本类型,就无法实现响应式
// 父组件 <template> <div> <son></son> </div> </template> <script> export default { provide(){ return { number: 1 } } } </script> // 子组件 <template> <div></div> </template> <script> export default { inject: ['number'] } </script>
组件的name选项有什么作用?
- 递归组件时,组件调用自身使用
- 用
is
特殊特性和component
内置组件标签时使用 keep-alive
内置组件标签中,include
和exclude
属性中使用
怎么强制刷新组件?
this.$forceUpdate
- 组件加上
key
,然后变化key
的值
组件会在什么时候下被销毁?
- 没有使用
keep-alive
时的路由切换 v-if="false"
- 执行
vm.$destory
怎么访问子组件的实例或者子元素?
<child-component ref="child"></child-component>
- 访问子组件中的某个方法
this.$refs.child.setNumber()
- 访问子组件中的某个数据
this.$refs.child.value
- 访问子组件中的元素
this.$refs['child'].querySelector('div')
怎么在子组件中访问父组件的实例?
this.$parent
怎么在组件中访问到根实例?
this.$root
vue生命周期有哪些?
beforeCreate:
实例创建前:created:
实例创建后:完成数据观测,属性和方法的运算 (watch/event事件回调,模板渲染成html前,vm.$el未定义,故数据初始化最好在这阶段完成)beforeMount:
载入前:render函数在这里被调用,生成虚拟DOM,但是还没转成真实DOM并替换elmounted:
载入后:真是DOM挂载完毕 (此时vm.$el可以调用,不能保证所有的子组件都挂载,要等视图更新完毕用vm.$nextTick)beforeUpdate:
数据更新前调用:新的虚拟DOM生成,但没有根虚拟DOM对比打补丁updated
数据更新后调用:新旧虚拟DOM对比打补丁后,进行真是DOM的更新beforeDestory:
实例销毁前调用,此时实例仍然完全可用,依然可以访问数据destoryed:
实例销毁之后调用,此时实例的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁
父子组件生命周期顺序
父beforeCreate ➡ 父created ➡ 父beforeMount ➡ 子beforeCreate ➡ 子created ➡ 子beforeMount ➡ 子mounted ➡ 父mounted
vue在created和mounted,这两个生命周期中请求数据有什么区别呢?
在created中,页面视图未出现,如果请求信息过多,页面会长时间处于白屏状态,在mounted不会这样,比较好
(DOM节点没出来,无法操作DOM节点)
第一次页面加载会触发哪几个钩子?
beforeCreate
、 created
、 beforeMount
、 mounted
DOM渲染是在那个生命周期完成的?
在mounted生命周期
vm.$nextTick有什么作用?
vm.$nextTick( () => { this.handler() })
将nextTick中的回调延迟到下次DOM更新完毕 (更新循环) 之后执行,vue采用的是异步更新的策略,同一事件循环内,多次修改,会统一进行一次视图更新,所以数据一更新,视图却还没更新,所以拿到的还是上一次的旧视图数据
列举常用的指令有哪些?
v-for、v-if、v-else-if、v-else、v-show、v-bind(:)、v-on(@)、v-model、v-html、v-text、v-once
列举表单修饰符和事件修饰符
-
事件修饰符
.stop
阻止事件冒泡.prevent
阻止默认事件(例如a标签的跳转).capture
事件默认是冒泡,capture作用是捕获.self
只有绑定点击事件的本身才会触发事件.once
监听一个自定义事件,但是只触发一次,一旦触发之后,监听器就会被移除.native
加在自定义组件的事件上,保证事件能执行(解决给组件绑定自定义事件无效?).sync
父子组件传值,子组件想更新这个值,使用此修饰符可简写
-
表单修饰符
.lazy
改变输入框的值时value不会改变,当失去焦点的时候。v-model绑定的值value才会改变.trim
去掉v-model绑定的值首尾空格.number
将值转换成数字(先输入字符串和先输入数字,是两种情况,先输入数字的话,只取前面数字部分,先输入字母的话,number修饰符无效)
使用事件修饰符要注意什么?
要注意顺序很重。用@click.prevent.self
会阻止所有的点击,而@click.self.prevent
只会组织对元素自身的点击
v-if和v-show的区别
- 相同点:两者都可以控制DOM的显示隐藏
- v-show:是通过控制DOM样式中的display属性来达到显示和隐藏, 不能用于
<tempalte>
- v-if:是通过销毁和重建DOM来达到显示和隐藏,(v-if是惰性的,如果初始条件为false,则什么也不做,只有为true时,才进行局部编译) 可以用于
<tempalte>
v-if有更高的切换消耗,v-show有更高的初始渲染消耗,DOM频繁显示隐藏用v-show
v-for和v-if的优先级,应该注意什么,怎么更好的使用他们
当他们处于同一节点时, v-for
比 v-if
的优先级更高,这意味着, v-if
会重复运行于每个 v-for
循环中,当你只想渲染部分节点的时候,这种优先级的机制十分有用
如果你想有条件的跳过循环的执行,那么你可以将v-if
置于外层元素,比如template
使用v-for遍历对象时,是按什么顺序遍历的,如何保证顺序
按照Object.keys()
的顺序遍历
转成数组保证顺序
v-once的使用场景
其作用是之渲染元素和组件一次, (虽有的重新渲染,元素,组件及其所有的子节点被视为静态内容跳过), 当组件中有大量的静态内容可以使用这个命令
watch 和computed 的区别?
watch使用场景:适合一个数据影响多个数据 当需要在数据变化时执行异步操作或开销较大的操作时;(搜索)
computed使用场景:适合一个数据受多个数据影响 是基于它的响应式依赖进行缓存的,只在相关响应式依赖发生改变时它才会重新计算。(计算价格)
watch怎么使用?
watch: { price(newVal, oldVal){ // .... } }
怎么在watch监听开始之后立即被调用?
在选项参数中指定 immediate: true
将立即以表达式的当前值触发回调。
watch: { price: { handler(newVal, oldVal){ // ... }, immediate: true } }
watch怎么深度监听对象变化?
选项参数加 deep
watch: { price: { handler(newVal, oldVal){ // ... }, deep: true } }
computed怎么传参?
// html <div>{{ total(3) }}</div> // js computed: { total() { return function(n) { return n * this.num } }, }
computed中的属性名和data中的属性名称可以相同么?
不能同名,因为computed属性名、data数据名、props数据名都会被挂载在vm实例上
if (key in vm.$data) { warn(`The computed property "${key}" is already defined in data.`, vm) } else if (vm.$options.props && key in vm.$options.props) { warn(`The computed property "${key}" is already defined as a prop.`, vm) }
data的属性名可以和methods中的属性名相同么?
不能同名,会报错(看源码)
let data = vm.$options.data const keys = Object.keys(data) const methods = vm.$options.methods let i = keys.length while (i--) { const key = keys[i] if (process.env.NODE_ENV !== 'production') { if (methods && hasOwn(methods, key)) { warn( `Method "${key}" has already been defined as a data property.`, vm ) } } }
watch、methods中的属性或方法,可以用箭头函数定义么?
不可以 this
会是 undefined
所有的生命周期钩子自动绑定 this 上下文到实例中,因此可以访问数据,对属性和方法进行运算。 箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,
delete和vue.delete的区别
-
delete:
delete object.a
只是将删除对象变为
''
或undefined
-
vm.$delete(vm.object, 'a')
直接删除了对象成员,如果对象时响应式的,确保删除能触发更新视图,这个方式主要解决vue不能检测对象属性被删除的问题
怎么使css样式只在当前组件中生效?
<style scoped></style>
在style上加scoped属性需要注意哪些?
如果在公共组件中使用,修改公共组件的样式需要用 /deep/
深度选择器
-
/deep/
在vue3.0之前可使用,vue3.0之后使用就会报错
/deep/ .el-input{ 50px }
-
::v-deep
在vue3.0之后使用,替代/deep/
::v-deep .el-input{ 50px }
-
>>>
只作用于css,对于less和scss不起作用,如果less和scss的话需要使用/deep/或::v-deep
>>> .el-input{ 50px }
怎么动态绑定class 和 style?
-
对象语法
<div class="test" :class="{active: actived ,'active-click': clicked && actived}"></div>
-
数组语法(三元表达式)
<div class="test" :class="[actived? activeClass : '', clicked && actived ? activeClickClass : '']"></div>
-
对象和数组混合
<div :class="[testClass , {active: actived}, {'active-click': clicked && actived}]"></div>
- 对象和计算属性(直接绑定数据对象)
<div :class="classObject"></div>
data(){ return { classObject:{ active: this.actived, 'active-click': this.actived && this.clicked, } } } // 或 computed: { classObject: function() { return { active: this.actived, 'active-click': this.actived && this.clicked, } } }
手写过滤器
局部过滤器
filters: { addUnit(val){ return val + '元' } }
全局过滤器
Vue.filter('addUnit', function(val) { return val + '元' })
过滤器中可以用this么?
不可以(禁止使用)
style上加scoped属性的原理?
vue通过DOM结构以及css样式上加上唯一的标记‘data-v-xxxxx’,保证唯一,达到样式私有化,不污染全局的作用
这是在标记vue文件中css时使用scoped标记产生的,因为要保证各文件中的css不相互影响,给每个component都做了唯一的标记,所以每引入一个component就会出现一个新的'data-v-xxx'标记
vue中能监听到数组变化的方法有哪些?为什么这些方法能监听到呢
push()
、pop()
、shift()
、unshift()
、splice()
、sort()
、reverse()
、 这些方法在Vue中被重新定义了,故可以监听到数组变化filter()
、concat()
、slice()
、 这些方法会返回一个新数组,也可以监听到数组的变化
在Vue中那些数组变化无法监听,为什么,怎么解决?
- 利用索引直接设置一个数组项时;
this.array[index] = newValue
- 直接修改数组的长度,
this.array.length = newLength
原因:Vue没有对数组进行Object.defineProperty
的属性劫持,所以直接arr[index] = xxx是无法更新视图的
解决办法:
this.arr.splice(index, 1, item)
this.$set(this.arr, index, value)
Vue中哪些对象变化无法监听,为什么?怎么解决?
- 对象属性的添加
- 对象属性的删除
原因:因为Vue是通过 Object.definedProperty
来将对象的key转成getter/setter的形式来追踪变化,但getter/setter只能追踪一个数据是否被修改,无法追踪新增属性和删除属性,所以才会导致上面对象变化无法监听。
属性添加:this.$set(this.obj,"key","newValue")
属性删除: this.$delete(obj, key)
怎么缓存当前打开的路由组件
用 <keep-alive></keep-alive>
内置组件包裹路由组件
跟keep-alive有关的生命周期是哪些?
activated
:keep-alive组件激活时调用deactivated
:keep-alive组件停用时调用- 以上钩子服务器端渲染期间不被调用
谈谈你对keep-alive的了解
keep-alive
是一个抽象组件,它自身不会渲染成一个DOM元素,也不会出现在父子组件中
使用 keep-alive
包裹动态组件时,会缓存不活动的组件实例,而不是销毁他们
include
定义缓存白名单, 被定义的组件会被缓存exclude
定义缓存黑名单, 被定义的组件不会被缓存- 以上两个参数可以是逗号分隔字符串、正则表达式或一个数组,
include="a,b"
、:include="/a|b/"
、:include="['a','b']"
-
max
最多可以缓存多少组件实例,一旦这个数字达到了,在新实例被创建之前,已缓存组件中最久没有被访问的实例会被销毁掉
匹配首先检查组件自身的 name, 如果name选项不可用。则匹配他的局部注册名称(父组件components选项的键值)。匿名组件不能被匹配
不会在函数式组件中正常工作,因为它们没有缓存实例;
当组件在内被切换,它的activated和deactivated这两个生命周期钩子函数将会被对应执行。
在Vue事件中是如何使用event对象的?
@click="handler($event)"
事件参数中传入event对象
在Vue事件中传入$event,使用$event.target和$event.currentTarget有什么区别?
$event.currentTarget
指向事件所绑定的元素$event.target
指向事件触发的元素
vue中怎么重置data?
Object.assgin(this.$data, this.$options.data())
-
vm.$data
获取Vue实例的data选项(对象)
-
vm.$options
用于获取当前 Vue 实例的初始化选项
不需要响应式的数据应该怎么处理?
- 在data外定义赋值
data(){ this.list = [{a: 1}] return { } }
- object.freeze
data(){ return { list: Object.freeze([{a: 1}]) } }
怎样理解单向数据流?双向数据流?
-
单向数据流
数据总是从父组件传到子组件,子组件没有权利修改父组件传过来的数据,只能请求父组件对原始数据进行修改。这样会防止从子组件意外改变父组件的状态,从而导致你的应用的数据流向难以理解。
注意:在子组件直接用 v-model 绑定父组件传过来的 props 这样是不规范的写法,开发环境会报警告。
如果实在要改变父组件的 props 值可以再data里面定义一个变量,并用 prop 的值初始化它,之后用$emit 通知父组件去修改。
-
双向数据流
双向数据流是指数据从父级向子级传递数据,子级可以通过一些手段改变父级向子级传递的数据。
比如用v-model、.sync来实现双向数据流。
Object.defineProperty 和 Proxy的区别?
-
Object.defineProperty
- 不能监听到数据length属性的变化
- 不能监听对象的添加
- 只能劫持对象的属性,所以我们需要对每个对象的每个属性进行遍历
-
Proxy
- 可以监听到数组length属性的变化
- 可以监听对象的添加
- 可代理整个对象,不需要对对象进行遍历,极大提高性能
- 多达13种的拦截,远超ObjectProperty只有get和set两种拦截
Vue的模板语法用的是哪个web模板引擎的?说说你对这模板引擎的理解?
采用的是Mustache的web模板引擎mustache.js
什么是vue实例?
实例化(创建)一个对象,里面包含生命周期钩子函数,data(对象),methods(方法)等
vue响应式原理?
-
数据劫持:
实现一个监听器,Observe对数据对象进行遍历,包括子属性对象的属性,利用
Object.definedProperty
对属性加上setter
和getter
给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化。-
数据代理:
数据代理就是让每次拿data里的数据时,不用每次都写一长串,比如
vm._data.obj.b
。 可以写成vm.obj.a
-
-
Dep(发布订阅模式)
- 定义
subs
数组,用作收集订阅者Watcher - 当劫持到数据变更的时候,通过notify通知订阅者Watcher进行update操作
- 定义
-
Watcher(观察者)
它负责做的事情就是订阅Dep,当Dep发出消息传递(notify)的时候,所有订阅着Dep的Watchers会进行自己的update操作
Vue组件里写的原生addEventListeners监听事件,要手动去销毁吗?为什么?需要注意什么?
需要,不然会造成多次绑定和内存泄露
需要在beforeDestory()中销毁
注意事项
- addEventListener() 的执行函数必须使用外部函数,例:
window.addEventListener('resize',this.computeOffset);
- 执行函数不能是匿名函数,例:
window.addEventListener('resize', ()=>{this.computeOffset()})
- 执行函数不能改变this指向,例:
window.addEventListener('resize',this.computeOffset.bind(this));
- addEventListener和removeEventListener第三个参数必须一致,例:
window.addEventListener('resize',this.computeOffset,true);
window.removeEventListener('resize',this.computeOffset,true);
vue的SSR是什么?有什么好处
- SSR就是服务端渲染
- 基于nodejs serve服务环境开发,所有html代码在服务端渲染
- 数据返回给前端,然后前端进行激活,即可成为浏览器识别的html代码
- SSR首次加载更快,有更好的用户体验,有更好的seo优化,因为爬虫能看到整个页面的内容,如果是vue项目,由于数据还要经过解析,这就造成爬虫并不会等待你的数据加载完成,所以其实Vue项目的seo体验并不是很好
为什么说虚拟DOM快?
- 虚拟DOM不会进行重排与重绘操作
- 虚拟DOM进行频繁修改,会一次性比较并修改真实DOM中修改的部分,并不是只要有一个数据发生变化,就更新整个dom树.
- 真实DOM频繁重排和重绘,更新效率是相当低的
- 虚拟DOM有效降低大面积的重排和重绘,因为最终会与真实DOM比较差异,只渲染局部
-
消耗计算
- 虚拟DOM:虚拟DOM增删改 + (与diff效率有关)真实DOM差异增删改 + (较少的节点)重排和重绘
- 真实DOM:真实DOM完全增删改 + (可能较多的节点) 重排和重绘
简单概括虚拟DOM原理流程?
- 用JavaScript对象模拟DOM树,并渲染这个DOM树
- 比较新旧DOM树,得到比较的差异对象
- 把差异对象应用到渲染的DOM树
key的作用
减少不必要的元素重复渲染,保证每个元素的key在其同级元素中具有唯一性,重复的key会造成重复渲染, 在Diff算法中,会借助元素的key值来判断该元素是新创建的还是被移动的而来的元素, 1. 使同类型元素切换时不进行元素的复用 2. 使列表重新渲染时复用已有的元素,提高效率 https://juejin.cn/post/6844904111964422157
如果不使用key,默认采用的是“就地更新”的策略,如果数据项的顺序被改变,
Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。
使用key要注意什么?
- 不要使用对象或数组之类的非基本类型值作为key,请用字符串或数值类型的值
- 不要使用数组的index作为key值,因为在删除数组某一项,index也会随之变化,导致key变化,渲染会出错
渲染
[a,b,c]
用 index 作为 key,那么在删除第二项的时候,index 就会从 0 1 2 变成 0 1(而不是 0 2),vue追踪就会发生问题,错乱
插槽slot怎么使用?
- 单个插槽 | 匿名插槽
// 父组件 <template> <div> <child>啦啦啦。。。</child> </div> </template> // 子组件 <template> <div> <slot></slot> </div> </template>
- 具名插槽
通过给slot命名的方式,添加不止一个位置的内容
slot="name"(旧语法)或
v-slot:name
或 #name(新语法)
// 父组件 <template> <div> <child> <template v-slot:"one"> 这是插入到one插槽的内容 </template> <template v-slot:"two"> 这是插入到two插槽的内容 </template> <template v-slot:"three"> 这是插入到three插槽的内容 </template> </child> </div> </template> // 子组件 <template> <div> <slot name="one"></slot> <slot name="two"></slot> <slot name="three"></slot> </div> </template>
- 后备内容
slot的默认值,如果父组件没有传递内容。就会显示默认值
//子组件 <template> <div class='child'> <slot>这就是默认值</slot> </div> </template>
- 编译作用域
父级模板里的所有内容都是在父级作用域中编译的,子模板里的所有内容都是在子作用域中编译的,父组件直接传入子组件内的数据是不可以的。
- 作用域插槽
父组件不能直接使用子组件的数据,但是可以通过一下方式,改变一下
// 父组件 <template> <div> <child> // 有命名的方式 <template v-slot:one="slotone">{{slotone.num}}</template> // 无命名的方式 <template v-slot:default="slotdefault">{{slotdefault.num2}}</template> </child> </div> </template> // 子组件 <template> <div> <slot name="one" :num="num"></slot> <slot :num="num"></slot> </div> </tempalte> <script> export default{ data(){ return{ num: 1, num2: 2 } } } </script>