前言
本篇博客主要讲述了vue的相关API、Mixin混入、数据整合以及开发中的注意事项。
面题回答
1.
$set
的实现:首先判断set的目标是否是值类型,如果是值类型就报错。如果不是,就会判断了目标是否是数组,如果是数组,那么就会判断key是不是合法,如果条件都成立就对目标数组进行操作,而这些操作诸如push、splice、pop等都是经过重写的,以达到响应式的操作。如果是对象,那么就需要通过vue.observable或者用object.defineProperty设置get/set去实现。2.
$nextTick
的作用:$.nextTick
可以当作一个Promise使用,它在下一次DOM更新循环结束之后执行延迟回调。这里涉及到事件循环以及微任务的概念。因为UI渲染是在微任务队列执行完毕之后进行的,我们要做的是在UI渲染之前完成数据的变更,那么就需要添加一个微任务,这样这个微任务会被push到任务队列的末尾。常见的场景一般有在create生命周期中,DOM节点未更新,此时拿不到DOM节点,那么可以$.nextTick方法包裹,在下一个DOM节点出现的时候,拿到对应的节点。3.v-if和v-show的区别:v-show的本质是把所在元素的display样式设置为none,dom元素其实还在,切换时不会触发组件的生命周期。而v-if则是真正的销毁所在的元素,因此会产生局部编译, 开销更大,且切换过程中会触发生命周期钩子函数。而决定使用哪个取决于切换频率,切换频率低使用v-if。
4.v-for索引:v-for默认使用就地复用策略。一般我们常用v-for来渲染列表数据,比如动态表头,有可能出现表头内容改变但是index不变的情况,这时候会出现渲染问题。或者是在表格数据量可能较大,且伴随一些增删改的操作,如果没有key整个列表都得重新渲染一遍,如果直接使用index,在增删数据后,index会产生变化,比如从4条数据的第二条数据中插入一条新的数据,就会导致后续的数据都会重新渲染,从而导致大量不必要的渲染,消耗性能。而使用唯一id,则只会对新增的数据进行渲染处理。
知识点
常用方法、Mixin混入、数据整合、注意事项理解
1.常用方法
$set
Vue有一些数组的变动无法被检测到,比如通过索引改变数组的值(this.arr[5] = 'zxp')、修改数组的长度(this.arr.length = 5 ) 等,此时需要用到$set用来添加对象属性(确保该属性为响应式,并且能触发视图更新)。
<div v-for="(item,index) in list" :key="index">
{{item.name}}
</div>
<el-button @click="changeValue">change</el-button>
//js部分
data(){
return {
list:[
{name:'111',id:'1'},
{name:'222',id:'2'}
]
}
},
methods:{
changeValue(){
this.$set(this.list,0,{name:'333',id:'3'})
//如果直接将list第一个值改为{name:'333',id:‘3’},会出现数据改变了,视图不更新的异常
}
}
$delete
用来删除对象数学(确保该属性为响应式,并且能触发视图更新)。一般我们常用delete关键字进行对象内部值得删除,但在Vue2里面不能直接使用对象删除的方法,会出现数据已经被删除,而视图没有更新的情况,即不能实现双向数据绑定。
let obj ={
a:'1',
b:'2',
c:'3'
}
delete obj.b
console.log(obj) //{a:'1',c:'3'}
data(){
return{
testData:{
name:'zxp'
},
test:'abc'
}
},
methods:{
del(){
//如果是数组,则将name改为index(数组下标)
this.$delete(this.testData,'name')
//下面这行是直接删除data中的test属性
delete test
}
}
PS:Vue2底层使用的Object.defineProperty可以实现修改和查看,但是监听不到数据的添加和删除,Vue3使用了proxy方式实现双向数据绑定,可以直接添加、删除。
$nextTick
$.nextTick
可以当作一个Promise使用,它在下一次DOM更新循环结束之后执行延迟回调。这里涉及到事件循环以及微任务的概念。因为UI渲染是在微任务队列执行完毕之后进行的,我们要做的是在UI渲染之前完成数据的变更,那么就需要添加一个微任务,这样这个微任务会被push到任务队列的末尾。
showPwd(){
this.pwdType = !this.pwdType
this.$nextTick(()=>{
this.$refs.password.focus()
})
}
<template>
<div @click="handleClick" ref="test">{{message}}</div>
</template>
<script>
export default{
data(){
return {
message:'zxp'
}
},
methods:{
handleClick(){
this.message="Hello"
console.log(this.$refs.test.innerText)//zxp
console.log(this.message)//Hello
this.$nextTick(()=>{
console.log(this.$refs.test.innerText)//Hello
console.log(this.message)//Hello
})
}
}
}
2.事件监听
事件监听
@load:img上的src有值时,图片便开始加载,但是加载完毕需要一定的时间,而@load事件是在完成加载后触发的。也可以应用于iframe、webView、滚动事件等完成后触发。
<img :src="url" @load="imgLoad"></img> export default{ data(){ return { url:'xxxxx' } }, methods:{ imgLoad(){ console.log('加载完成') } } }
@click
<div id="app"> <button @click="click">click me</button> </div> ... var app = new Vue({ el: '#app', methods: { click(event) { console.log(typeof event); // object //使用不带圆括号的形式,event对象将被自动当做实参传入; } } });@change
@change
<div id="app"> <button v-on:click="click($event, 'zxp')">click me</button> </div> ... var app = new Vue({ el: '#app', methods: { click(event, val) { console.log(typeof event); // object console.log(val); // zxp //使用带圆括号的形式,我们需要使用 $event 变量显式传入 event 对象。 } } });
- @focus:获取input焦点事件
- @input:input输入值时的事件
- @keyup:当指定的按键松开会触发的事件
- @keydown:当指定的按键按下会触发的事件
- @mousedown:按下鼠标键时触发
- @mouseup:释放按下的鼠标键时触发
- @mousemove:鼠标移动事件,
- @mouseover:移入事件,鼠标指针穿过被选元素或其子元素、孙元素,会冒泡
- @mouseout:移出事件,鼠标指针穿过被选元素或其子元素、孙元素,会冒泡
- @mouseenter:移入事件,只触发一次,不冒泡
- @mouseleave:移出事件,只触发一次,不冒泡
事件修饰符
.stop
- 阻止冒泡.prevent
- 阻止默认事件,比如提交.capture
- 阻止捕获.self
- 只监听触发该元素的事件.once
- 只触发一次.left
- 左键事件.right
- 右键事件.middle
- 中间滚轮事件
<!-- 阻止单击事件冒泡 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件侦听器时使用事件捕获模式 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当事件在该元素本身(而不是子元素)触发时触发回调 -->
<div v-on:click.self="doThat">...</div>
<!-- click 事件只能点击一次,2.1.4版本新增 -->
<a v-on:click.once="doThis"></a>
按键修饰符
.enter
.tab
.delete
(捕获 "删除" 和 "退格" 键).esc
.space
.up
.down
.left
.right
.ctrl
.alt
.shift
.meta
<p><!-- Alt + C -->
<input @keyup.alt.67="clear">
<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>
3.面试题
v-if和v-show有什么区别
共同点:v-show与v-if的作用效果是相同的,都是控制元素在页面显示。
区别:v-show的本质是把v-show所在元素的样式设置为display:none,dom元素其实还在,切换时不会触发组件的生命周期,初始渲染成本较高;而v-if则是真正的销毁所在的元素,因此v-if会产生局部编译, 开销更大,且切换过程中会触发生命周期钩子函数。而决定使用哪个取决于切换频率,切换频率低使用v-if。
v-if和v-for不能共用的原因
在对template解析时,当v-for和v-if 处于同一个节点时,v-for的执行级别比v-if要高,同时使用会遍历数组的每一项,那么每一项都会进行v-if的判断,这样会产生不必要的性能开销)。
以上内容针对vue2版本,vue3是可以一起用的,vue3更新了优先级,v-if高于v-for,但仍旧不建议两者一起使用。
v-for中key的作用
key的作用:key给每一个vnode节点一个唯一id,虚拟dom可以依靠key,更准确、更快的拿到对应的vnode节点。(PS:对比的是拿到vnode的速度而不是实际所消耗的速度。)
不带key的diff速度确实会更快,因为虚拟dom在默认情况下会采取就地复用的方式(dom节点位置不变),即没有key时,状态默认绑定的是位置,有key时,状态根据key的属性值绑定到了响应的数组元素。但是这种方式会产生如过渡效果异常等依赖于组件状态的问题(就地复用),即不够准确。
常见场景:表格动态表头,表头由动态数据v-for遍历渲染,有可能出现表头内容改变但是index不变的情况,这时候会出现渲染问题。或者是在表格数据量可能较大,且伴随一些增删改的操作,如果没有key整个列表都得重新渲染一遍,如果直接使用index,在增删数据后,index会产生变化,比如从4条数据的第二条数据中插入一条新的数据,就会导致后续的数据都会重新渲染,从而导致大量不必要的渲染,消耗性能。而使用唯一id,则只会对新增的数据进行渲染处理。
组件中的data为函数的原因
<script>
export default{
data(){
return {
a:'1',
b:'2'
}
}
}
</script>
一个组件被复用多次的话,也就会创建多个实例,从本质上来说,这些实例用的都是同一个构造函数,当我们将组件中的data写成一个函数,数据是以函数返回值形式定义的,这样每复用一次data,都会返回一份新的data,拥有自己的作用域,也是vue中闭包的一种应用。 如果组件中data是对象的话,对象是引用数据类型,它就会共用一个内存地址,造成数据污染。
最后
走过路过,不要错过,点赞、收藏、评论三连~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。