Ⅰ、通过 CDN 使用 Vue💥
你可以借助 script 标签直接通过 CDN 来使用 Vue:
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
这里我们使用了 unpkg,但你也可以使用任何提供 npm 包服务的 CDN,例如 jsdelivr 或 cdnjs。当然,你也可以下载此文件并自行提供服务。
第一个实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>VUE 3.0</title>
<script src="js/vue.global.min.js"></script>
</head>
<body>
<div id="hello-vue" class="demo">
{{ message }}
</div>
</body>
<script>
const HelloVueApp = {
data() {
return {
message: 'Hello Vue!!'
}
}
}
Vue.createApp(HelloVueApp).mount('#hello-vue')
</script>
</html>
Ⅱ、Vue3 起步 💥
Vue3 中的应用是通过使用 createApp 函数来创建的,语法格式如下:
const app = Vue.createApp({ /* 选项 */ })
传递给 createApp 的选项用于配置根组件。
在使用 mount() 挂载应用时,该组件被用作渲染的起点。
Vue.createApp(HelloVueApp).mount('#hello-vue')
createApp 的参数是根组件(HelloVueApp),在挂载应用时,该组件是渲染的起点。
一个应用需要被挂载到一个 DOM 元素中,以上代码使用 mount(’#hello-vue’) 将 Vue 应用 HelloVueApp 挂载到 <"div id=“hello-vue”> 中 。
HTML 页面中有一个 div 元素:
<div id="hello-vue" class="demo">
{{ message }}
</div>
-
mount(’#hello-vue’) 将 Vue 应用 HelloVueApp 挂载到 <"div id=“hello-vue”> 中。
-
{{ }} 用于输出 对象属性 和 函数返回值。
-
{{ message }} 对应应用中 message 的值。
data 选项
data 选项是一个函数。
Vue 在创建新组件实例的过程中调用此函数。它应该返回一个对象,然后 Vue 会通过响应性系统将其包裹起来,并以 $data 的形式存储在组件实例中。
<div id="hello-vue" class="demo">
</div>
const app = Vue.createApp({
data() {
return { count: 4 }
}
})
const vm = app.mount('#hello-vue')
document.write(vm.$data.count) // => 4
document.write("<br>")
document.write(vm.count) // => 4
document.write("<br>")
// 修改 vm.count 的值也会更新 $data.count
vm.count = 5
document.write(vm.$data.count) // => 5
document.write("<br>")
// 反之亦然
vm.$data.count = 6
document.write(vm.count) // => 6
以上实例属性仅在实例首次创建时被添加,所以你需要确保它们都在 data 函数返回的对象中。
方法
我们可以在组件中添加方法,使用 methods 选项,该选项包含了所需方法的对象。
以下实例我们添加了 methods 选项,选项中包含了 increment() 方法:
<div id="hello-vue" class="demo">
</div>
const app=Vue.createApp({
data(){
return{count:4}
},
methods:{
increment(){
this.count++;
}
}
})
const vm=app.mount("#hello-vue");
document.write(vm.count); //=>4
document.write("<br>");
vm.increment();
document.write(vm.count); //=>5
Ⅲ、模板语法 💥
Vue 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。
插值 --文本
数据绑定最常见的形式就是使用 {{…}}(双大括号)的文本插值:
<div id="app">
<p>{{ message }}</p>
</div>
- {{…}} 标签的内容将会被替代为对应组件实例中 message 属性的值,如果 message 属性的值发生了改变,{{…}} 标签内容也会更新。
使用 v-once 指令执行一次性地插值,当数据改变时,插值处的内容不会更新。
<span v-once>这个将不会改变: {{ message }}</span>
Html
使用 v-html 指令用于输出 html 代码:
<div id="example1" class="demo">
<p>使用双括号文本插值:{{rawHtml}}</p>
<p>使用v-html指令:<span v-html="rawHtml"></span></p>
</div>
const app=Vue.createApp({
data(){
return{
rawHtml:'<span style="color: red">我是红色字体!</span>'
}
}
})
const vm=app.mount("#example1");
属性 V-bind
HTML 属性中的值应使用 v-bind 指令。
<div v-bind:id="dynamicId"></div>
对于布尔属性,常规值为 true 或 false,如果属性值为 null 或 undefined,则该属性不会显示出来。
<button v-bind:disabled="isButtonDisabled">按钮</button>
以下实例判断 use 的值,如果为 true 使用 class1 类的样式,否则不使用该类:
<script src="js/vue.global.min.js"></script>
<style>
.class1{
background-color: black;
color: white;
}
</style>
<div id="app" class="demo">
<label for="r1">修改颜色为黑底白字</label>
<input type="checkbox" v-model="use" id="r1">
<br><br>
<div v-bind:class="{'class1':use}">
v-bind:class 指令
</div>
</div>
<script>
const app=Vue.createApp({
data(){
return{
use:false
}
}
})
const vm=app.mount("#app");
</script>
动态绑定多个值
如果你有像这样的一个包含多个 attribute 的 JavaScript 对象:
data() {
return {
objectOfAttrs: {
id: 'container',
class: 'wrapper'
}
}
}
通过不带参数的 v-bind
,你可以将它们绑定到单个元素上:
<div v-bind="objectOfAttrs"></div>
表达式
Vue.js 都提供了完全的 JavaScript 表达式支持。
<style>
#list-1{
background-color: red;
}
</style>
<div id="app" class="demo">
{{5+5}} <br>
{{ok?'YES':'NO'}}<br>
{{message.split('').reverse().join('')}}<br>
<div v-bind:id="'list-'+id">Maodi教程</div>
</div>
</body>
<script>
const app=Vue.createApp({
data(){
return{
ok:true,
message:'RUNOOB!!',
id:1
}
}
})
const vm=app.mount("#app");
</script>
指令 V-
指令是带有 v- 前缀的特殊属性。
指令用于在表达式的值改变时,将某些行为应用到 DOM 上。如下例子:
<div id="app" class="demo">
<span v-if="seen">现在你能看的到我</span> <br>
<button @click="btn">显示隐藏</button>
</div>
const app=Vue.createApp({
data(){
return{
seen:true
}
},
methods:{
btn(){
this.seen=!this.seen
}
}
})
const vm=app.mount("#app");
这里, v-if 指令将根据表达式 seen 的值( true 或 false )来决定是否插入 p 元素。
参数
参数在指令后以冒号指明。例如, v-bind 指令被用来响应地更新 HTML 属性:
<div id="app" class="demo">
<p><a v-bind:href="url">{{message}}</a></p>
</div>
<script>
const app=Vue.createApp({
data(){
return{
url:"https://www.baidu.com",
message:'百度一下'
}
}
})
const vm=app.mount("#app");
</script>
在这里 href 是参数,告知 v-bind 指令将该元素的 href 属性与表达式 url 的值绑定。
另一个例子是 v-on 指令,它用于监听 DOM 事件:
<!-- 完整语法 -->
<a v-on:click="doSomething"> ... </a>
<!-- 缩写 -->
<a @click="doSomething"> ... </a>
<!-- 动态参数的缩写 (2.6.0+) -->
<a @[event]="doSomething"> ... </a>
在这里参数是监听的事件名。
修饰符
修饰符是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。例如,.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault():
<form v-on:submit.prevent="onSubmit"></form>
用户输入 v-model
在 input 输入框中我们可以使用 v-model 指令来实现双向数据绑定:
<div id="app" class="demo">
<p>{{message}}</p>
<input v-model="message">
</div>
<script>
const app=Vue.createApp({
data(){
return{
message:'百度一下'
}
}
})
const vm=app.mount("#app");
</script>
v-model 指令用来在 input、select、textarea、checkbox、radio 等表单控件元素上创建双向数据绑定,根据表单上的值,自动更新绑定的元素的值。
- 按钮的事件我们可以使用 v-on 监听事件,并对用户的输入进行响应。
以下实例在用户点击按钮后对字符串进行反转操作:
<div id="app" class="demo">
<p>{{message}}</p>
<input v-model="message">
<button @click="btn">反转字符串</button>
</div>
<script>
const app=Vue.createApp({
data(){
return{
message:''
}
},
methods:{
btn(){
this.message=this.message.split('').reverse().join('')
}
}
})
const vm=app.mount("#app");
</script>
Ⅳ、计算属性👀
计算属性关键词: computed
。
计算属性在处理一些复杂逻辑时是很有用的。
✅可以看下以下反转字符串的例子:
<div id="app">
{{ message.split('').reverse().join('') }}
</div>
如果变的很复杂起来,也不容易看懂理解。
✅使用了计算属性的实例:
<div id="app">
原始字符串:{{message}} <br>
反转后字符串:{{reversedMessage}}
</div>
<script>
const app=Vue.createApp({
data(){
return{
message:"MAODIAINAO!!"
}
},
computed:{
reversedMessage:function (){
return this.message.split('').reverse().join('')
}
}
});
const mount = app.mount("#app");
</script>
声明了一个计算属性
reversedMessage
。
提供的函数将用作属性vm.reversedMessage
的 getter 。
vm.reversedMessage 依赖于 vm.message,在 vm.message 发生改变时,vm.reversedMessage 也会更新。
computed vs methods
- 我们可以使用
methods
来替代computed
,效果上两个都是一样的 - 但是
computed
是基于它的依赖缓存,只有相关依赖发生改变时才会重新取值 - 而使用
methods
,在重新渲染的时候,函数总会重新调用执行。
<div id="app">
<p>原始字符串: {{ message }}</p>
<p>使用 computed 计算后反转字符串: {{ reversedMessage }}</p>
<p>使用 methods 计算后反转字符串: {{ reversedMessage2() }}</p>
</div>
<script>
const app = {
data() {
return {
message: 'RUNOOB!!'
}
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
},
methods: {
reversedMessage2: function () {
return this.message.split('').reverse().join('')
}
}
}
Vue.createApp(app).mount('#app')
</script>
可以说使用 computed
性能会更好,但是如果你不希望缓存,你可以使用 methods
属性。
可写计算属性 computed setter 👌
计算属性默认是只读的。当你尝试修改一个计算属性时,你会收到一个运行时警告。只在某些特殊场景中你可能才需要用到“可写”的属性,你可以通过同时提供 getter 和 setter
来创建:
<script>
const app = Vue.createApp({
data() {
return {
firstName:'John',
lastName:'Doe'
}
},
computed: {
fullName:{
get(){
return this.firstName + ' ' + this.lastName
},
set(newValue){
// 注意:我们这里使用的是解构赋值语法
[this.firstName,this.lastName] = newValue.split(' ');
}
}
}
});
const mount = app.mount("#app");
document.write("原来"+mount.fullName);// 原来John Doe
document.write("<br>");
mount.fullName="阮棠 RT";
document.write("姓:"+mount.lastName); //RT
document.write("<br>");
document.write("名:"+mount.firstName);//阮棠
document.write("<br>");
document.write("更改后:"+mount.fullName)//更改后:阮棠 RT
</script>
现在当你再运行 this.fullName = '肢窝 胳 '
时,setter 会被调用而 this.firstName 和 this.lastName
会随之更新。
Ⅴ、Class 与 Style 绑定
绑定 HTML class
<div :class="{ active: isActive }"></div>
data() {
return {
isActive: true,
hasError: false
}
}
配合以下模板:
<div
class="static"
:class="{ active: isActive, 'text-danger': hasError }"
></div>
渲染的结果会是:
<div class="static active"></div>
✅绑定的对象并不一定需要写成内联字面量的形式,也可以直接绑定一个对象:
data() {
return {
classObject: {
active: true,
'text-danger': false
}
}
}
<div :class="classObject"></div>
这也会渲染出相同的结果。我们也可以绑定一个返回对象的计算属性
。这是一个常见且很有用的技巧:
data() {
return {
isActive: true,
error: null,
}
},
computed:{
classObject(){
return{
active:this.isActive && !this.error,
'danger':!this.error
}
}
}
<div :class="classObject"></div>
.active{
border-radius: 10px;
background-color: burlywood;
width: 100px;
height: 100px;
}
.danger{
color: white;
}
运行结果:
# 绑定数组
我们可以给 :class
绑定一个数组来渲染多个 CSS class:
data() {
return {
activeClass: 'active',
errorClass: 'danger'
}
}
<div :class="[activeClass, errorClass]"></div>
渲染的结果是:
<div class="active danger"></div>
✅如果你也想在数组中有条件地渲染某个 class,你可以使用三元表达式:
<div :class="[isActive ? activeClass : '', errorClass]"></div>
errorClass
会一直存在,但 activeClass
只会在 isActive
为真时才存在。
✅然而,这可能在有多个依赖条件的 class 时会有些冗长。因此也可以在数组中嵌套对象:
<div :class="[{ active: isActive }, errorClass]"></div>
绑定内联样式
绑定对象
:style
支持绑定 JavaScript 对象值,对应的是 HTML 元素的 style 属性:
data() {
return {
activeColor: 'red',
fontSize: 30
}
}
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
尽管推荐使用 camelCase,但 :style
也支持 kebab-cased 形式的 CSS 属性 key (对应其 CSS 中的实际名称),例如:
<div :style="{ 'font-size': fontSize + 'px' }"></div>
✅直接绑定一个样式对象通常是一个好主意,这样可以使模板更加简洁:
data() {
return {
styleObject: {
color: 'red',
fontSize: '13px'
}
}
}
<div :style="styleObject"></div>
同样的,如果样式对象需要更复杂的逻辑,也可以使用返回样式对象的计算属性。
绑定数组
我们还可以给 :style
绑定一个包含多个样式对象的数组。这些对象会被合并后渲染到同一元素上:
<div :style="[baseStyles, overridingStyles]"></div>
自动前缀
当你在 :style
中使用了需要浏览器特殊前缀的 CSS 属性时,Vue 会自动为他们加上相应的前缀。Vue 是在运行时检查该属性是否支持在当前浏览器中使用。如果浏览器不支持某个属性,那么将尝试加上各个浏览器特殊前缀,以找到哪一个是被支持的。
样式多值
你可以对一个样式属性提供多个 (不同前缀的) 值,举例来说:
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
数组仅会渲染浏览器支持的最后一个值。在这个示例中,在支持不需要特别前缀的浏览器中都会渲染为 display: flex
。
Ⅵ、条件渲染 💦
- v-if
- v-else
- v-else-if
- v-show
Ⅶ、列表渲染
# v-for
我们可以使用 v-for
指令基于一个数组来渲染一个列表。v-for
指令的值需要使用 item in items
形式的特殊语法,其中 items
是源数据的数组,而 item
是迭代项的别名:
data() {
return {
items: [{ message: 'Foo' }, { message: 'Bar' }]
}
}
<li v-for="item in items">
{{ item.message }}
</li>
✅在 v-for
块中可以完整地访问父作用域内的属性和变量。v-for
也支持使用可选的第二个参数表示当前项的位置索引。
data() {
return {
parentMessage: 'Parent',
items: [{ message: 'Foo' }, { message: 'Bar' }]
}
}
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
v-for
变量的作用域和下面的 JavaScript 代码很类似:
const parentMessage = 'Parent'
const items = [
/* ... */
]
items.forEach((item, index) => {
// 可以访问外层的 `parentMessage`
// 而 `item` 和 `index` 只在这个作用域可用
console.log(parentMessage, item.message, index)
})
注意 v-for
是如何对应 forEach
回调的函数签名的。实际上,你也可以在定义 v-for
的变量别名时使用解构,和解构函数参数类似:
<li v-for="{ message } in items">
{{ message }}
</li>
<!-- 有 index 索引时 -->
<li v-for="({ message }, index) in items">
{{ message }} {{ index }}
</li>
✅你也可以使用 of
作为分隔符来替代 in
,这更接近 JavaScript 的迭代器语法:
<div v-for="item of items"></div>
# v-for
与对象
你也可以使用 v-for
来遍历一个对象的所有属性。遍历的顺序会基于对该对象调用 Object.keys()
的返回值来决定。
data() {
return {
myObject: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
}
}
}
<ul>
<li v-for="value of myObject">
{{ value }}
</li>
</ul>
✅ 可以通过提供第二个参数表示属性名 (例如 key):
<li v-for="(value, key) in myObject">
{{ key }}: {{ value }}
</li>
✅ 第三个参数表示位置索引:
<li v-for="(value, key, index) in myObject">
{{ index }}. {{ key }}: {{ value }}
</li>
以上结果:
# 在 v-for
里使用范围值
v-for
可以直接接受一个整数值。在这种用例中,会将该模板基于 1…n 的取值范围重复多次。
<span v-for="n in 10">{{ n }}</span>
注意此处 n
的初值是从 1
开始而非 0
。
# v-for
与 v-if
当它们同时存在于一个节点上时,v-if
比 v-for
的优先级更高。这意味着 v-if
的条件将无法访问到 v-for
作用域内定义的变量别名:
<!--
这会抛出一个错误,因为属性 todo 此时
没有在该实例上定义
-->
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo.name }}
</li>
在外新包装一层 <template>
再在其上使用 v-for
可以解决这个问题 (这也更加明显易读):
<template v-for="todo in todos">
<li v-if="!todo.isComplete">
{{ todo.name }}
</li>
</template>
# 通过 key 管理状态
Vue
默认按照“就地更新”的策略来更新通过 v-for
渲染的元素列表。当数据项的顺序改变时,Vue 不会随之移动 DOM 元素的顺序,而是就地更新每个元素,确保它们在原本指定的索引位置上渲染。
默认模式是高效的,但只适用于列表渲染输出的结果不依赖子组件状态或者临时 DOM 状态 (例如表单输入值) 的情况。
为了给 Vue 一个提示,以便它可以跟踪每个节点的标识,从而重用和重新排序现有的元素,你需要为每个元素对应的块提供一个唯一的 key
attribute:
<div v-for="item in items" :key="item.id">
<!-- 内容 -->
</div>
当你使用 <template v-for>
时,key
应该被放置在这个 <template>
容器上:
<template v-for="todo in todos" :key="todo.name">
<li>{{ todo.name }}</li>
</template>
# 组件上使用 v-for
我们可以直接在组件上使用 v-for
,和在一般的元素上使用没有区别 (别忘记提供一个 key
):
<MyComponent v-for="item in items" :key="item.id" />
但是,这不会自动将任何数据传递给组件,因为组件有自己独立的作用域。为了将迭代后的数据传递到组件中,我们还需要传递 props
:
<MyComponent
v-for="(item, index) in items"
:item="item"
:index="index"
:key="item.id"
/>
不自动将 item
注入组件的原因是,这会使组件与 v-for
的工作方式紧密耦合。明确其数据的来源可以使组件在其他情况下重用。
这里是一个简单的 Todo List 的例子,展示了如何通过 v-for
来渲染一个组件列表,并向每个实例中传入不同的数据。
数组变化侦测
展示过滤或排序后的结果
有时,我们希望显示数组经过过滤或排序后的内容,而不实际变更或重置原始数据。在这种情况下,你可以创建返回已过滤或已排序数组的计算属性。
✅举例来说:
data() {
return {
numbers:[1,2,2,3,4,5,6,7,8,9,10,10]
}
},
computed:{
eventNUmber(){
return this.numbers.filter((item,index,arr)=>{
return arr.indexOf(item,0)===index; //去重
}).filter(n=>n%2==0)
}
}
<li v-for="n in evenNumbers">{{ n }}</li>
✅ 在计算属性不可行的情况下 (例如在多层嵌套的 v-for
循环中),你可以使用以下方法:
data() {
return {
sets: [[ 1, 2, 3, 4, 5 ], [6, 7, 8, 9, 10]]
}
},
methods: {
even(numbers) {
return numbers.filter(number => number % 2 === 0)
}
}
<ul v-for="numbers in sets">
<li v-for="n in even(numbers)">{{ n }}</li>
</ul>
在计算属性中使用 reverse()
和 sort()
的时候务必小心!这两个方法将变更原始数组,计算函数中不应该这么做。请在调用这些方法之前创建一个原数组的副本:
- return numbers.reverse()
+ return [...numbers].reverse()
Ⅶ、事件处理 💥
# 内联事件处理器
✅ 内联事件处理器通常用于简单场景,例如:
data() {
return {
count: 0
}
}
<button @click="count++">Add 1</button>
<p>Count is: {{ count }}</p>
# 方法事件处理器
随着事件处理器的逻辑变得愈发复杂,内联代码方式变得不够灵活。因此 v-on
也可以接受一个方法名或对某个方法的调用。
✅ 举例来说:
data() {
return {
name: 'Vue.js'
}
},
methods: {
greet(event) {
// 方法中的 `this` 指向当前活跃的组件实例
alert(`Hello ${this.name}!`)
// `event` 是 DOM 原生事件
if (event) {
alert(event.target.tagName)
}
}
}
<!-- `greet` 是上面定义过的方法名 -->
<button @click="greet">Greet</button>
方法事件处理器会自动接收原生 DOM 事件并触发执行。在上面的例子中,我们能够通过被触发事件的 event.target.tagName
访问到该 DOM 元素。
# 在内联处理器中调用方法
除了直接绑定方法名,你还可以在内联事件处理器中调用方法。这允许我们向方法传入自定义参数以代替原生事件:
methods: {
say(message) {
alert(message)
}
}
<button @click="say('hello')">Say hello</button>
<button @click="say('bye')">Say bye</button>
# 在内联事件处理器中访问事件参数
有时我们需要在内联事件处理器中访问原生 DOM 事件。你可以向该处理器方法传入一个特殊的 $event
变量,或者使用内联箭头函数:
<!-- 使用特殊的 $event 变量 -->
<button @click="warn('Form cannot be submitted yet.', $event)">
Submit
</button>
<!-- 使用内联箭头函数 -->
<button @click="(event) => warn('Form cannot be submitted yet.', event)">
Submit
</button>
methods: {
warn(message, event) {
// 这里可以访问 DOM 原生事件
if (event) {
event.preventDefault()
}
alert(message)
}
}
# 事件修饰符
在处理事件时调用 event.preventDefault()
或 event.stopPropagation()
是很常见的。尽管我们可以直接在方法内调用,但如果方法能更专注于数据逻辑而不用去处理 DOM 事件的细节会更好。
为解决这一问题,Vue 为 v-on
提供了事件修饰符。修饰符是用 .
表示的指令后缀,包含以下这些:
<!-- 单击事件将停止传递 -->
<a @click.stop="doThis"></a>
<!-- 提交事件将不再重新加载页面 -->
<form @submit.prevent="onSubmit"></form>
<!-- 修饰语可以使用链式书写 -->
<a @click.stop.prevent="doThat"></a>
<!-- 也可以只有修饰符 -->
<form @submit.prevent></form>
<!-- 仅当 event.target 是元素本身时才会触发事件处理器 -->
<!-- 例如:事件处理器不来自子元素 -->
<div @click.self="doThat">...</div>
.capture
、.once
和.passive
修饰符与原生 addEventListener 事件相对应:
<!-- 添加事件监听器时,使用 `capture` 捕获模式 -->
<!-- 例如:指向内部元素的事件,在被内部元素处理前,先被外部处理 -->
<div @click.capture="doThis">...</div>
<!-- 点击事件最多被触发一次 -->
<a @click.once="doThis"></a>
<!-- 滚动事件的默认行为 (scrolling) 将立即发生而非等待 `onScroll` 完成 -->
<!-- 以防其中包含 `event.preventDefault()` -->
<div @scroll.passive="onScroll">...</div>
.passive
修饰符一般用于触摸事件的监听器,可以用来改善移动端设备的滚屏性能。
# 按键修饰符
在监听键盘事件时,我们经常需要检查特定的按键。Vue 允许在 v-on
或 @
监听按键事件时添加按键修饰符。
<!-- 仅在 `key` 为 `Enter` 时调用 `submit` -->
<input @keyup.enter="submit" />
你可以直接使用 KeyboardEvent.key
暴露的按键名称作为修饰符,但需要转为 kebab-case 形式。
<input @keyup.page-down="onPageDown" />
在上面的例子中,仅会在 $event.key
为 'PageDown'
时调用事件处理。
按键别名
Vue 为一些常用的按键提供了别名:
系统按键修饰符
✅ 举例来说:
<!-- Alt + Enter -->
<input @keyup.alt.enter="clear" />
<!-- Ctrl + 点击 -->
<div @click.ctrl="doSomething">Do something</div>
.exact
修饰符
.exact
修饰符允许控制触发一个事件所需的确定组合的系统按键修饰符。
<!-- 当按下 Ctrl 时,即使同时按下 Alt 或 Shift 也会触发 -->
<button @click.ctrl="onClick">A</button>
<!-- 仅当按下 Ctrl 且未按任何其他键时才会触发 -->
<button @click.ctrl.exact="onCtrlClick">A</button>
<!-- 仅当没有按下任何系统按键时触发 -->
<button @click.exact="onClick">A</button>
鼠标按键修饰符
# 事件修饰符 测试✅✅✅✅✅✅
✅prevent:阻止默认事件(常用)
<a href="https://www.baidu.com" @click.prevent="showInfo">百度一下你就知道</a>
methods:{
showInfo(){
alert("点我提示信息 不做任何操作")
}
}
a
标签的默认有跳转到href
的行为,我们把默认行为禁用后,就不会跳转页面
补充:
如果不使用Vue
指令,那么怎么屏蔽事件的默认行为呢?
✅stop:阻止事件冒泡(常用)
<div class="demo" @click="showInfo">
<button @click.stop="btnInfo">nihao1</button>
<!-- 修饰符可以连续写 -->
<!-- <a href="http://www.atguigu.com" @click.prevent.stop="showInfo">点我提示信息</a> -->
</div>
methods:{
showInfo(){
alert("点我提示信息 不做任何操作")
},
btnInfo(){
alert("点击了Button")
}
}
运行结果:
🚫 如果不阻止事件冒泡:
<div class="demo" @click="showInfo">
<button @click="btnInfo">nihao1</button>
</div>
✅ 如果不使用Vue指令,那么怎么阻止事件冒泡呢?
✅once :事件只触发一次(常用)
<button @click.once="showInfo">点我提示信息</button>
methods:{
showInfo(){
alert("点我提示信息 不做任何操作")
}
}
运行结果:
✅capture:使用事件的捕获模式
<!-- 使用事件的捕获模式 -->
<div class="box1" @click.capture="showMsg(1)">
div1
<div class="box2" @click="showMsg(2)">
div2
</div>
</div>
- js:
运行结果:
🚫 关闭事件捕获:
运行结果:
分析原因: ❤✔💦
✅self:只有event.target是当前操作的元素时才触发事件
<!-- 只有event.target是当前操作的元素时才触发事件; -->
<div class="demo1" @click.self="showInfo">
<button @click="showInfo">点我提示信息</button>
</div>
js 💦
methods:{
showInfo(){
alert("点我提示信息 不做任何操作")
}
}
因为event.target
不是当前操作的元素,所有没有触发事件,这个在一定程度上面也可以阻止冒泡
注意 :
- 只会弹出一次
alert()
- 如果不加
.self
则会执行两次
#按键修饰符
一般用来 input
来形容
Ⅷ、表单输入绑定
在前端处理表单时,我们常常需要将表单输入框的内容同步给 JavaScript
中相应的变量。手动连接值绑定和更改事件监听器可能会很麻烦:
<input
:value="text"
@input="event => text = event.target.value">
{{text}}
v-model
指令帮我们简化了这一步骤:
<input v-model="text">
另外,v-model
还可以用于各种不同类型的输入,<textarea>、<select>
元素。它会根据所使用的元素自动使用对应的 DOM 属性和事件组合:
- 文本类型的
<input>
和<textarea>
元素会绑定value
property 并侦听input
事件; <input type="checkbox">
和<input type="radio">
会绑定checked
property 并侦听 change 事件;<select>
会绑定value
property 并侦听change
事件。
基本用法
文本
<p>Message is: {{ message }}</p>
<input v-model="message" placeholder="edit me" />
多行文本
<span>Multiline message is:</span>
<p style="">{{message}}</p>
<textarea v-model="message" placeholder="add multiple lines"></textarea>
❗ 注意 在 <textarea>
中是不支持插值表达式的。请使用 v-model
来替代:
<!-- 错误 -->
<textarea>{{ text }}</textarea>
<!-- 正确 -->
<textarea v-model="text"></textarea>
复选框
单一的复选框,绑定布尔类型值:
<input type="checkbox" id="checkbox" v-model="checked" />
<label for="checkbox">{{ checked }}</label>
✅ 我们也可以将多个复选框绑定到同一个数组或集合的值:
data() {
return {
checkedNames:[]
}
}
<div>Checked names: {{ checkedNames }}</div>
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
单选按钮
<div>Picked:{{ picked }}</div>
<input type="radio" id="one" value="One" v-model="picked" checked> <!--默认被选中-->
<label for="one">One</label>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="Two">Two</label>
data() {
return {
picked:'One'
}
},
选择器
✅ 单个选择器的示例如下:
<div>Selected:{{ Selected }}</div>
<select v-model="Selected">
<option disabled value="" >Please select one</option>
<option>A</option>
<option>B</option>
<option>C</option>
<option>D</option>
</select>
✅ 多选 (值绑定到一个数组):
<div>Selected:{{ Selected }}</div>
<select v-model="Selected" multiple> <!--multiple 倍数 n.数量多的-->
<option>A</option>
<option>B</option>
<option>C</option>
<option>D</option>
</select>
✅ 选择器的选项可以使用 v-for
动态渲染:
data() {
return {
selected:'A', //初始 选择器 为 A
options:[
{text:'One',value:'A'},
{text:'Two',value:'B'},
{text:'Three',value:'C'}
]
}
},
<select v-model="selected">
<option v-for="option of options" :value="option.value"> <!--value 值 为 A-->
{{option.text}} <!--信息为 One-->
</option>
</select>
<div>Selected: {{ selected }}</div> <!--已设置 初始值 A-->
值绑定 💦
对于单选按钮,复选框和选择器选项,v-model
绑定的值通常是静态的字符串 (或者对复选框是布尔值):
<!-- `picked` 在被选择时是字符串 "a" -->
<input type="radio" v-model="picked" value="a" />
<!-- `toggle` 只会为 true 或 false -->
<input type="checkbox" v-model="toggle" />
<!-- `selected` 在第一项被选中时为字符串 "abc" -->
<select v-model="selected">
<option value="abc">ABC</option>
</select>
但有时我们可能希望将该值绑定到当前组件实例上的动态数据。这可以通过使用 v-bind
来实现。此外,使用 v-bind
还使我们可以将选项值绑定为非字符串的数据类型。
复选框
<input
type="checkbox"
v-model="toggle"
true-value="yes"
false-value="no" />
{{toggle}}
true-value
和 false-value
是 Vue 特有的 attributes,仅支持和 v-model
配套使用。这里 toggle
属性的值会在选中时被设为 'yes'
,取消选择时设为 'no'
。你同样可以通过 v-bind
将其绑定为其他动态值:
<input
type="checkbox"
v-model="toggle"
:true-value="dynamicTrueValue"
:false-value="dynamicFalseValue" checked/>
data() {
return {
toggle:'已被选中',
dynamicTrueValue:'已被选中', //动态的设置 选中和未选中的值
dynamicFalseValue:'取消选中'
}
},
单选按钮
<input type="radio" v-model="pick" :value="first" checked/>
<input type="radio" v-model="pick" :value="second" />
return {
pick:"男",
first:"男",
last:"女"
}
pick
会在第一个按钮选中时被设为 first
,在第二个按钮选中时被设为 second
。
选择器选项
<select v-model="selected">
<!-- 内联对象字面量 -->
<option :value="{ number: 123 }">123</option>
</select>
{{selected.number}} <!--获取当前 option.number 的值-->
v-model
同样也支持非字符串类型的值绑定!在上面这个例子中,当某个选项被选中,selected
会被设为该对象字面量值 { number: 123 }
。
修饰符 💦
# .lazy
- 默认情况下,
v-model
会在每次input
事件后更新数据 - 加
.lazy
会转变为在change
事件中同步。 - 也就是在失去焦点 或者 按下回车键时才 更新
<!-- 在 "change" 失去焦点事件后 更新 -->
<input v-model.lazy="msg" />
# .number
如果你想让用户输入自动转换为数字,你可以在 v-model
后添加 .number
修饰符来管理输入:
<input v-model.number="age" />
如果该值无法被 parseFloat()
处理,那么将返回原始值。
number
修饰符会在输入框有 type="number"
时自动启用。
# .trim
如果你想要默认自动去除用户输入内容中两端的空格 ,你可以在 v-model
后添加 .trim
修饰符:
<input v-model.trim="msg" />
❤🧡💛
过滤器
<!--日期格式化-->
<script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.11.7/dayjs.min.js"></script>
自定义指令 💨💦
1. 介绍
除了 Vue 内置的一系列指令 (比如 v-model
或 v-show
) 之外,Vue 还允许你注册自定义的指令
下面我们注册一个全局指令 v-focus
, 该指令的功能是在页面加载时也就是 当一个 input 元素被 Vue 插入到 DOM 中后 ,元素获得焦点:
<div id="app">
<p>页面载入时,input 元素自动获取焦点:</p>
<input v-focus>
</div>
<script>
const app = Vue.createApp({})
// 注册一个全局自定义指令 `v-focus`
app.directive('focus', {
// 当被绑定的元素挂载到 DOM 中时……
mounted(el) {
// 聚焦元素
el.focus()
}
})
app.mount('#app')
</script>
✅ 我们也可以在实例使用 directives
选项来注册局部指令,这样指令只能在这个实例中使用:
<div id="app">
<p>页面载入时,input 元素自动获取焦点:</p>
<input v-focus>
</div>
<script>
const app = {
data() {
return {
}
},
directives: {
focus: {
// 指令的定义
mounted(el) {
el.focus()
}
}
}
}
Vue.createApp(app).mount('#app')
2. 指令钩子
指令定义函数提供了几个钩子函数(可选):
-
created
: 在绑定元素的属性或事件监听器被应用之前调用。 -
beforeMount
: 指令第一次绑定到元素并且在挂载父组件之前调用。。 -
mounted
: 在绑定元素的父组件被挂载后调用。。 -
beforeUpdate
: 在更新包含组件的 VNode 之前调用。。 -
updated
: 在包含组件的 VNode 及其子组件的 VNode 更新后调用。 -
beforeUnmount
: 当指令与在绑定元素父组件卸载之前时,只调用一次。 -
unmounted
: 当指令与元素解除绑定且父组件已卸载时,只调用一次。
const myDirective = {
// 在绑定元素的 attribute 前
// 或事件监听器应用前调用
created(el, binding, vnode, prevVnode) {
// 下面会介绍各个参数的细节
},
// 在元素被插入到 DOM 前调用
beforeMount(el, binding, vnode, prevVnode) {},
// 在绑定元素的父组件
// 及他自己的所有子节点都挂载完成后调用
mounted(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件更新前调用
beforeUpdate(el, binding, vnode, prevVnode) {},
// 在绑定元素的父组件
// 及他自己的所有子节点都更新后调用
updated(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件卸载前调用
beforeUnmount(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件卸载后调用
unmounted(el, binding, vnode, prevVnode) {}
全局指令:
// 注册 (功能指令)
app.directive('my-directive', () => {
// 这将被作为 `mounted` 和 `updated` 调用
})
// 配置对象
app.directive('指令名',配置对象)
3. 钩子参数
4. 简化形式
对于自定义指令来说,一个很常见的情况是仅仅需要在 mounted
和 updated
上实现相同的行为,除此之外并不需要其他钩子。这种情况下我们可以直接用一个函数来定义指令,如下所示:
<div v-color="color"></div>
app.directive('color', (el, binding) => {
// 这会在 `mounted` 和 `updated` 时都调用
el.style.color = binding.value
})
5. 对象字面量
如果你的指令需要多个值,你可以向它传递一个 JavaScript 对象字面量。别忘了,指令也可以接收任何合法的 JavaScript 表达式。
<div v-demo="{ color: 'white', text: 'hello!' }"></div>
app.directive('demo', (el, binding) => {
console.log(binding.value.color) // => "white"
console.log(binding.value.text) // => "hello!"
})
Ⅹ、生命周期
ⅩⅠ、侦听器
计算属性允许我们声明性地计算衍生值。然而在有些情况下,我们需要在状态变化时执行一些“副作用”:例如更改 DOM,或是根据异步操作的结果去修改另一处的状态。
在选项式 API 中,我们可以使用 watch 选项在每次响应式属性发生变化时触发一个函数。
测试:
✅ 你问我答
<p>
Ask a yes/no question:
<input v-model="question" />
</p>
<p>{{ answer }}</p>
data(){
return{
question:'',
answer: 'Questions usually contain a question mark. ;-)'
}
},
watch:{
question(newQuestion, oldQuestion) {
if (newQuestion.indexOf('?') > -1) {
if(oldQuestion=="你是谁"){
this.answer="我是阮棠...."
}else if(oldQuestion=="你多大了"){
this.answer="21岁"
}
}
}
}
深度监听
深度监视:
- (1). Vue中的watch默认不监测对象内部值的改变(一层)
- (2). 配置
deep:true
可以监测对象内部值改变(多层)。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>3天气案例_深度监视</title>
<script src="/js/vue.global.min.js"></script>
<style>
</style>
</head>
<body>
<!--
深度监视:
(1).Vue中的watch默认不监测对象内部值的改变(一层)
(2).配置deep:true可以监测对象内部值改变(多层)。
备注:
(1).vue自身可以监测对象内部值的改变,但vue提供的watch默认不可以!
(2).使用watch时根据数期的具体结构,决定是否采用深度监视。
-->
<div id="app">
<h2>今天天气很{{info}}</h2>
<button @click="isHot =!isHot">切换</button>
<hr>
<h3>a的值是:{{numbers.a}}</h3>
<button @click="numbers.a++">点我让a++</button>
<hr>
<h3>b的值是:{{numbers.b}}</h3>
<button @click="numbers.b++">点我让b++</button>
</div>
</body>
<script>
const app = Vue.createApp({
data() {
return {
isHot:true,
numbers:{
a:1,
b:1
}
}
},
computed:{
info(){
return this.isHot?'炎热':'凉爽'
}
},
watch:{
isHot:{
handler(newVal,oldVal){
console.log("isHost被修改了",newVal,oldVal)
}
},
//监听多级结构中某个属性的变化
/*'numbers.a':{
handler() {
console.log("a被改变了")
}
}*/
//监听多级结构中所有属性的变化
numbers:{
deep:true,
handler(){
console.log("number被改变了")
}
}
}
});
const vm = app.mount("#app");
</script>
</html>
侦听器写法
return {
isHot:true,
}
watch:{
//正常的写法
isHot:{
handler(newVal,oldVal){
console.log("isHost被修改了",newVal,oldVal)
}
},
//简写
isHot(newVal,oldVal){
console.log("isHost被修改了",newVal,oldVal)
}
}
//-----------------------------------------------------
//正常的写法
vm.$watch('isHot',{
handler(newVal,oldVal){
console.log("isHost被修改了",newVal,oldVal)
}
})
//简写
vm.$watch('isHot',function (newVal,oldVal){
console.log("isHost被修改了",newVal,oldVal)
})
即时回调的侦听器
watch
默认是懒执行的:
- 仅当数据源变化时,才会执行回调。
- 但在某些场景中,我们希望在创建侦听器时,立即执行一遍回调。
- 举例来说,我们想请求一些初始数据,然后在相关状态更改时重新请求数据。
我们可以用一个对象来声明侦听器,这个对象有 handler
方法和 immediate: true
选项,这样便能强制回调函数立即执行:
export default {
// ...
watch: {
question: {
handler(newQuestion) {
// 在组件实例创建时会立即调用
},
// 强制立即执行回调
immediate: true
}
}
// ...
}
总结
-
正常的写法 可以监听多级结构中所有属性的变化
deep:true
immediate:true
computed
和watch
之何的区别:
computed
能完成的功能,watch
都可以完成watch
能完成的功能,computed
不一定能完成,例如:watch
可以进行异步操作。
两个重要的小原则:
- 所被 Vue 管理的函数,最好写成普通函数,这样
this
的指向才是vm
或 组件实例对象。 - 所有不被 Vue 所管理的函数( 定时器的回调函数、ajax的回调函数等, Promise的回调函数),最好写成箭头函数,
- 这
this
的指向才是vm 或 组件实例对象。
ⅩⅡ、组件
组件(Component)是 Vue.js
最强大的功能之一。
组件允许我们将 UI
划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。在实际应用中,组件常常被组织成层层嵌套的树状结构:
这和我们嵌套 HTML
元素的方式类似,Vue 实现了自己的组件模型,使我们可以在每个组件内封装自定义内容与逻辑。Vue 同样也能很好地配合原生 Web Component
。如果你想知道 Vue 组件与原生 Web Components 之间的关系,可以阅读此章节。
每个 Vue 应用都是通过用 createApp
函数创建的,传递给 createApp
的选项用于配置根组件。当我们挂载应用时,该组件被用作渲染的起点。
一个应用需要被挂载到一个 DOM 元素中。
✅ 以下实例我们将 Vue 应用挂载到 <div id="app"></div>
,应该传入 #app
:
const RootComponent = { /* 选项 */ }
const app = Vue.createApp(RootComponent)
const vm = app.mount('#app')
全局组件
✅ 注册一个全局组件语法格式如下:
const app = Vue.createApp({...})
app.component('my-component-name', {
/* ... */
})
my-component-name
为组件名,/* ... */
部分为配置选项。注册后,我们可以使用以下方式来调用组件:
<my-component-name></my-component-name>
✅ 全局组件实例
- 注册一个简单的全局组件 runoob,并使用它:
<div id="app">
<runoob></runoob>
</div>
<script>
// 创建一个Vue 应用
const app = Vue.createApp({})
// 定义一个名为 runoob 的新全局组件
app.component('runoob', {
template: '<h1>自定义组件!</h1>'
})
app.mount('#app')
</script>
局部组件
- 全局注册往往是不够理想的。
比如,如果你使用一个像 webpack 这样的构建系统,全局注册所有的组件意味着即便你已经不再使用一个组件了,它仍然会被包含在你最终的构建结果中。这造成了用户下载的 JavaScript 的无谓的增加。
在这些情况下,你可以通过一个普通的 JavaScript
对象来定义组件:
const ComponentA = {
/* ... */
}
const ComponentB = {
/* ... */
}
const ComponentC = {
/* ... */
}
✅ 然后在 components
选项中定义你想要使用的组件:
const app = Vue.createApp({
components: {
'component-a': ComponentA,
'component-b': ComponentB
}
})
对于 components
对象中的每个属性来说,其属性名就是自定义元素的名字(component-a、component-b)
,其属性值就是这个组件的选项对象(ComponentA、ComponentB
)。
✅ 局部组件实例
var com1={
data(){
return{
message:'Vue-Component'
}
},
template:'<h1>我是自定义组件{{message}}</h1>'
}
const app = Vue.createApp({
components:{
'compon': com1
}
})
组件的复用
你可以将组件进行任意次数的复用:
<div id="app">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>
Prop
prop
是 子组件 用来接受父组件传递过来的数据的一个自定义属性。
父组件的数据需要通过 props
把数据传给子组件,子组件需要显式地用 props
选项声明 “prop”:
<div id="app">
<site-name title="Google"></site-name>
<site-name title="Runoob"></site-name>
<site-name title="Taobao"></site-name>
</div>
<script>
const app = Vue.createApp({})
app.component('site-name', {
props: ['title'],
template: `<h4>{{ title }}</h4>`
})
app.mount('#app')
</script>
一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop。
动态 Prop
类似于用 v-bind
绑定 HTML 特性到一个表达式,也可以用 v-bind
动态绑定 props
的值到父组件的数据中。每当父组件的数据变化时,该变化也会传导给子组件:
<div id="app">
<site-info v-for="site in sites" :id="site.id" :title="site.title"></site-info>
</div>
const Site = {
data() {
return {
sites: [
{id: 1, title: "Goole"},
{id: 2, title: "Runoob"},
{id: 3, title: "Taobao"}
]
}
}
}
const app = Vue.createApp(Site)
app.component('site-info', {
props: ['id', 'title'],
template: `<h4>{{ id }} - {{ title }}</h4>`
})
const vm = app.mount("#app");