Object.defineProperty
Object.defineProperty()的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性
Object.defineProperty(obj, prop, desc)
- obj 需要定义属性的当前对象
- prop 当前需要定义的属性名
- desc 属性描述符
一般通过为对象的属性赋值的情况下,对象的属性可以修改也可以删除,但是通过Object.defineProperty()定义属性,通过描述符的设置可以进行更精准的控制对象属性。
Object.defineProperty缺点:
- 无法监听es6 的Set、Map变化
- 无法监听Class类型的数据
- 属性的新加或者删除也无法监听
- 数组元素的增加和删除也无法监听
属性的新加或者删除无法监听
- vue2的解决方案
Vue.set(对象, 属性, true)
- vue3支持动态增加属性的拦截
数组元素的增加和删除无法监听
- vue2的解决方案,vue2重新写了push
- vue3支持动态增加的拦截
v-show和v-if的区别
(1)、v-if和v-show用于视图层进行条件判断视图展示
(2)、v-if的原理是根据判断条件来动态的进行增删DOM元素,v-show是根据判断条件来动态的进行显示和隐藏元素,频繁的进行增删DOM操作会影响页面加载速度和性能,由此我们可以得出结论:
当您的项目程序不是很大的时候,v-if和v-show都可以用来进行判断展示和隐藏(这种场景使用v-if只是影响不大,并不是没有影响);
当您的项目程序比较大的时候,不推荐使用v-if来进行判断展示和隐藏,推荐使用v-show;
(3)、只有v-if能和v-else连用进行分支判断,v-show是不能和v-else连用的,如果出现多种条件场景的情况下,可以使用v-if来进行判断
Vue中的v-if和v-for不建议一起用
vue源码:
源码位置:\vue-dev\src\compiler\codegen\index.js
export function genElement (el: ASTElement, state: CodegenState): string {
if (el.parent) {
el.pre = el.pre || el.parent.pre
}
if (el.staticRoot && !el.staticProcessed) {
return genStatic(el, state)
} else if (el.once && !el.onceProcessed) {
return genOnce(el, state)
} else if (el.for && !el.forProcessed) {
return genFor(el, state)
} else if (el.if && !el.ifProcessed) {
return genIf(el, state)
} else if (el.tag === 'template' && !el.slotTarget && !state.pre) {
return genChildren(el, state) || 'void 0'
} else if (el.tag === 'slot') {
return genSlot(el, state)
} else {
// component or element
...
}
在进行if判断的时候,v-for是比v-if先进行判断
结论:v-for优先级比v-if高:
- 永远不要把
v-if和v-for同时用在同一个元素上,带来性能方面的浪费(每次渲染都会先循环再进行条件判断) - 如果避免出现这种情况,则在外层嵌套
template(页面渲染不生成dom节点),在这一层进行v-if判断,然后在内部进行v-for循环
动态 class
<div :class="classObj">动态切换class 对象方法</div>
<div :class="classArr">动态切换class 数组方法</div>
...
data: {
classObj: {
aa: true,
bb: true,
cc: false,
}
classArr: ["aa", "bb"]
}
动态 style
<div :style="styleObj">动态切换class 对象方法</div>
<div :style="styleArr">动态切换class 数组方法</div>
...
data: {
styleObj: {
backgrounColor: "red",
fontSize: "12px"
}
classArr: [
{ backgrounColor: "red" },
{ fontSize: "12px" }
]
}
Vue3
- 初始化写法变了
- data写法改了,函数式写法
<div id="box">
{{10+20}}
</div>
//vue2
new Vue({
el: "#box",
data: {
},
methods: {
}
})
//vue3 初始化
Vue.createApp({
data() {
return {
myname: "abc"
}
},
methods: {
}
}).mount("#box")
v-for中的key
vue渲染过程是,是先生成虚拟DOM,然后再根据虚拟DOM生成真实DOM, 数据更新的时候,不是完全地将整颗Virtual DOM进行渲染,而是去渲染改变的部分, 这时候就需要一个计算Virtual DOM树改变部分的算法了,这个算法就是Diff算法。
Vue为了高效地渲染元素,默认会复用已有元素而不是重新渲染。
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username" key="username" />
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address" key="email" />
</template>
在上面的代码中,如果没有为input元素设置key,在切换loginType之后,Vue会使用原来的input元素,而其中的内容也不会清空。
为了使切换loginType之后input元素的内容清空,需要为input标签添加key,使切换过后用新的input元素进行渲染且清空内容。
- diff算法默认执行起来是这样的:
- 引入key之后,diff算法:
跟踪每个节点的身份,从而重用和重新排序现有元素 理想的key值是每项都有的唯一标记
key 是 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速:
- 1、更准确:因为带
key就不是原地复用了,在比较是否是同一个节点的sameNode函数a.key === b.key对比中可以避免就地复用的情况,所以会更加准确。同时避免频繁更新不同元素,从而使得整个patch过程更加高效,减少DOM操作量,提高性能。 - 2、更快速:利用
key的唯一性生成map对象来获取对应节点,比遍历方式更快。
数据更新检测(Vue2)
Vue3没有这个问题
- 使用以下方法操作数组,可以检测变动
push() pop() shift() unshift() splice() sort() reverse()
- 新数组替换旧的数组,不会被拦截到
filter() concat() slice() map()
- 不能检测以下变动的数组 vm.item[indexOfItem] = newValue
解决:
- Vue.set(vm.dataList, 0. "hello")
- splice
修饰符
事件修饰符
<ul @click.self="handleUlClick">
//stop: 阻止冒泡
<li @click.stop="handleLiClick">1111</li>
<li>2222</li>
</ul>
| 修饰符 | 作用 | JS原生 |
|---|---|---|
| stop | 阻止冒泡 | event.stopPropagation() |
| self | 将事件绑定到自身,只有自身才能触发,通常用于避免冒泡事件的影响 | |
| prevent | 阻止默认事件的发生 | event.preventDefault() |
| capture | 捕获冒泡,即有冒泡发生时,有该修饰符的dom元素会先执行,如果有多个,从外到内依次执行,然后再按自然顺序执行触发的事件。 | |
| once | 设置事件只能触发一次,比如按钮的点击等。 | |
| passive | 该修饰符大概意思用于对DOM的默认事件进行性能优化,根据官网的例子比如超出最大范围的滚动条滚动的。 | |
| native | 在父组件中给子组件绑定一个原生的事件,就将子组件变成了普通的HTML标签,不加'. native'事件是无法触 发的。 |
按键修饰符
//enter键 + ctrl键 时,触发
<input type="text" @keyup.enter.ctrl="handleKeyup"/>
<input type="text" @keyup.65="handleKeyup"/>
<!-- Alt + C -->
<input @keyup.alt.67="clear">
<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">做一些操作</div>
methods: {
handleKeyup(evt) {
//if(evt.keyCode===13) {
//}
}
}
| 修饰符 | 作用 |
|---|---|
| enter | enter键 |
| delete | (捕获“删除”和“退格”按键) => // 删除键 |
| tab | tab键 |
| esc | 取消键 |
| space | 空格键 |
| up | 上 |
| down | 下 |
| left | 左 |
| right | 右 |
| ctrl | |
| shift | |
每一个键盘按键都对应一个数字,一般也会将这个数字称之为键盘码值,使用[keyCode大全]
表单修饰符
lazy修饰符
- 默认情况下,v-model默认是在input事件中同步输入框的数据。也就是说,一旦有数据发生改变对应的data中的数据就会自动发生改变
- lazy修饰符可以让数据在失去焦点或者回车时才会更新
number修饰符
- 默认情况下,在输入框无论我们输入的是字母还是数字,都会被当做字符串类型进行处理
- number修饰符可以让在输入框中输入的内容自动转成数字类型
trim修饰符
- 如果输入的内容首尾有很多空格,通常我们希望将其去掉
- trim修饰符可以过滤内容左右两边的空格
<!-- 没有加lazy之前,实时绑定,加上之后,回车绑定-->
<input type="text" v-model.lazy="message"><br>
<input type="number" v-model.number="age"><br>
<input type="text" v-model.trim="name">
const app = new Vue({
el:"#app",
data:{
message:'',
age:'',
name:''
}
})
计算属性和侦听器
computed
- 计算属性(防止模板过重,难易维护),负责逻辑放在计算属性中来写。
- 计算属性有缓存,基于依赖的缓存。
watch
- 当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
区别:
- methods: 事件绑定,逻辑计算。可以不用return,没有缓存
- computed: (重视结果)解决模板过重问题,必须有return,只求结果,有缓存,同步。
- watch: (重视过程),监听一个值的改变。不用返回值,异步同步。