计算属性
html模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护,此时可以考虑使用computed计算属性
<div id="computed-basics">
<p>Has published books:</p>
<span>{{ publishedBooksMessage }}</span>
</div>
Vue.createApp({
data() {
return {
author: {
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
}
}
},
computed: {
// 计算属性的 getter
publishedBooksMessage() {
// `this` points to the vm instance
return this.author.books.length > 0 ? 'Yes' : 'No'
}
}
}).mount('#computed-basics')
计算属性缓存:上面的计算属性也可以写成一个函数来调用,两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的反应依赖关系缓存的。计算属性只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 author.books 还没有发生改变,多次访问 publishedBookMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。而写成函数式,每当触发重新渲染时,调用方法将总会再次执行函数。
计算属性的setter:计算属性默认只有 getter,不过在需要时你也可以提供一个 setter
computed: {
fullName: {
// getter
get() {
return this.firstName + ' ' + this.lastName
},
// setter
set(newValue) {
const names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
watch侦听器
当需要在数据变化时执行异步或开销较大的操作时,使用此方式最好。watch会限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态
const watchExampleVM = Vue.createApp({
data() {
return {
question: '',
answer: 'Questions usually contain a question mark. ;-)'
}
},
watch: {
// whenever question changes, this function will run
question(newQuestion, oldQuestion) {
if (newQuestion.indexOf('?') > -1) {
this.getAnswer()
}
}
},
methods: {
getAnswer() {
this.answer = 'Thinking...'
axios
.get('https://yesno.wtf/api')
.then(response => {
this.answer = response.data.answer
})
.catch(error => {
this.answer = 'Error! Could not reach the API. ' + error
})
}
}
}).mount('#watch-example')
class与style绑定
对象语法
// 方式一
<div
class="static"
:class="{ active: isActive, 'text-danger': hasError }"
></div>
data() {
return {
isActive: true,
hasError: false
}
}
// 方式二
<div :class="classObject"></div>
data() {
return {
classObject: {
active: true,
'text-danger': false
}
}
}
数组语法
// 方式一
<div :class="[activeClass, errorClass]"></div>
data() {
return {
activeClass: 'active',
errorClass: 'text-danger'
}
}
// 方式二
<div :class="[isActive ? activeClass : '', errorClass]"></div>
在组件上使用动态class
<div id="app">
<my-component class="baz"></my-component>
</div>
const app = Vue.createApp({})
app.component('my-component', {
template: `
<p :class="$attrs.class">Hi!</p>
<span>This is a child component</span>
`
})
可以为 style 绑定中的 property 提供一个包含多个值的数组,常用于提供多个带前缀的值,只会渲染数组中最后一个被浏览器支持的值
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
条件渲染
可以把一个 <template> 元素当做不可见的包裹元素,并在上面使用 v-if。最终的渲染结果将不包含 <template> 元素。(在template元素上不能使用v-else、v-show)
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS property display。
v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销
不推荐同时使用 v-if 和 v-for。当 v-if 与 v-for 一起使用时,v-if 具有比 v-for 更高的优先级。
列表渲染
v-for遍历数组,不要使用对象或数组之类的非基本类型值作为 v-for 的 key。请用字符串或数值类型的值。
<ul id="array-with-index">
<li v-for="(item, index) in items" :key="index">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
</ul>
v-for遍历对象
<li v-for="(value, name, index) in myObject">
{{ index }}. {{ name }}: {{ value }}
</li>
可以利用带有 v-for 的 <template> 来循环渲染一段包含多个元素的内容
事件处理
多事件处理
<!-- 这两个 one() 和 two() 将执行按钮点击事件 -->
<button @click="one($event), two($event)">
Submit
</button>
methods: {
one(event) {
// first handler logic...
},
two(event) {
// second handler logic...
}
}
事件修饰符,使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生
stop:阻止冒泡(通俗讲就是阻止事件向上级DOM元素传递)
prevent:阻止默认事件的发生
capture:捕获冒泡,即有冒泡发生时,有该修饰符的dom元素会先执行,如果有多个,从外到内依次执行,然后再按自然顺序执行触发的事件。
self:将事件绑定到自身,只有自身才能触发,通常用于避免冒泡事件的影响
once:设置事件只能触发一次,比如按钮的点击等
passive:该修饰符大概意思用于对DOM的默认事件进行性能优化
<!-- 阻止单击事件继续传播 -->
<a @click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form @submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a @click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form @submit.prevent></form>
<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div @click.capture="doThis">...</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div @click.self="doThat">...</div>
不要把 .passive 和 .prevent 一起使用,因为 .prevent 将会被忽略,同时浏览器可能会向你展示一个警告。
按键修饰符
.enter.tab.delete(捕获“删除”和“退格”键).esc.space.up.down.left.right
<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->
<input @keyup.enter="submit" />
系统修饰符
在不同操作系统中,系统修饰符可能代表的按键不一样
.ctrl.alt.shift.meta
<!-- Alt + Enter -->
<input @keyup.alt.enter="clear" />
<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>
表单输入绑定
v-model 会忽略所有表单元素的 value、checked、selected attribute 的初始值而总是将当前活动实例的数据作为数据来源。你应该通过 JavaScript 在组件的 data 选项中声明初始值。
v-model 在内部为不同的输入元素使用不同的 property 并抛出不同的事件:
- text 和 textarea 元素使用
valueproperty 和input事件; - checkbox 和 radio 使用
checkedproperty 和change事件; - select 字段将
value作为 prop 并将change作为事件。
// 复选框
<input type="checkbox" id="checkbox" v-model="checked" />
<label for="checkbox">{{ checked }}</label>
// 单选框
<input type="radio" id="one" value="One" v-model="picked" />
<label for="one">One</label>
select中如果 v-model 表达式的初始值未能匹配任何选项,<select> 元素将被渲染为“未选中”状态。在 iOS 中,这会使用户无法选择第一个选项。因为这样的情况下,iOS 不会触发 change 事件。
lazy修饰符
<!-- 在“change”时而非“input”时更新 -->
<input v-model.lazy="msg" />
响应式原理(Proxy)
利用Proxy代理,new Proxy创建一个代理对象
let obj = {
name: '1111',
age: 8
}
// new Proxy(需要代理的对象, {get(), set()})
let obj2 = new Proxy(obj,{
// 当访问对象中的某个属性是执行get方法,target参数表示被劫持的对象,property表示访问的属性
get(target, property){
return target[property]
},
// 修改对象的某个属性时会触发set事件
set(target, property, newVal) {
target[property] = newVal
},
})