Vue3学习笔记

Vue3学习笔记

Vue只关注视图层,采用自底向上增量开发的设计。
Vue的目标是通过尽可能简单的API实现响应的数据绑定和组合的视图组件。

实例:

1
2
3
<div id="hello-vue" class="demo">
{{ message }}
</div>

起步

安装

  1. 下载文件并引入(vue.global.js)
  2. cdn引入(vue.global.js)
  3. npm安装
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 安装升级cnpm
npm install -g cnpm

# 安装vue3
cnpm install vue@next

# 安装vue-cli
cnpm install -g @vue/cli

# 查看版本
vue --version

# 如果已经安装了vue2,则
cnpm install -g @vue/cli-init

创建项目

1
2
3
4
5
# 创建项目,选项可以回车
vue init webpack project_name

# 运行
cnpm run dev

还可以使用Vite构建vue项目。

1
2
3
4
5
6
7
8
9
10
11
12
# 安装vite
cnpm install -g create-vite-app

# 使用vite创建vue项目
create-vite-app project_name
cva project_name
cnpm init vite-app project_name

# 运行
cd project_name
cnpm install
cnpm run dev

构建

生成dist目录,然后放到服务器。

1
npm run build

搭建服务器

1
2
3
4
5
# python server
python -m http.server --directory [dist_dir]

# npx
npx http-server

目录结构

vue3项目目录

src/App.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- 展示模板 -->
<template>
<img alt="Vue logo" src="./assets/logo.png" />
<HelloWorld msg="Hello Vue 3.0 + Vite" />
</template>
<!-- Vue 代码 -->
<script>
/* 从 src/components/HelloWorld.vue 中引入 HelloWorld 组件 */
import HelloWorld from './components/HelloWorld.vue'

export default {
name: 'App',
components: {

HelloWorld

}
}
</script>

起步

学习Vue3阶段可以直接引入vue.global.js文件。

引入vue.global.js
1
<script crossorigin="anonymous" integrity="sha512-+i5dAv2T8IUOP7oRl2iqlAErpjtBOkNtREnW/Te+4VgQ52h4tAY5biFFQJmF03jVDWU4R7l47BwV8H6qQ+/MfA==" src="https://lib.baomitu.com/vue/3.0.7/vue.global.js"></script>

Vue3中的应用是通过使用 createApp 函数来创建的,语法格式如下:

1
2
3
4
5
6
7
8
9
10
11
// 创建vue3应用
const app = Vue.createApp({
/*
选项
选项用于配置根组件
mount()应用时,该组件被用作渲染的起点
*/
})

// 配置根组件HelloVueApp,挂载应用到#hello-vue
Vue.createApp(HelloVueApp).mount('#hello-vue')

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div id="hello-vue" class="demo">
<!-- {{ }}用于输出对象属性和函数返回值 -->
{{ message }}
</div>

<script>
const HelloVueApp = {
data() {
return {
message: 'Hello Vue!!'
}
}
}​
Vue.createApp(HelloVueApp).mount('#hello-vue')
</script>

data

data 是一个函数,返回一个对象。

vm.$data.count == vm.count

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const app = Vue.createApp({
data() {
return {
count: 4
}
}
})

const vm = app.mount('#app')

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

methods

可以在组件中添加方法,使用 methodsmethods 是一个对象,包含了多个方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const app = Vue.createApp({
data() {
return {
count: 4
}
},
methods: {
// increment方法
increment() {
// this指向该组件实例
this.count++
}
}
})

const vm = app.mount('#app')

document.write(vm.count) // => 4
document.write("<br>")
vm.increment()

document.write(vm.count) // => 5

在一个方法中调用另一个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const app = Vue.createApp({
data() {
return {

}
},
methods: {
func1() {

},
func2() {
// 在func2中调用func1
this.$options.methods.func1();
}
}
})

const vm = app.mount('#app')

模板语法

插值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
<!-- 文本插值,动态更新 -->
<div id="app">
<p>{{ message }}</p>
</div>

<!-- 一次性插值 -->
<span v-once>这个将不会改变: {{ message }}</span>

<!-- 插入html -->
<div id="example1" class="demo">
<p>使用双大括号的文本插值: {{ rawHtml }}</p>
<p>使用 v-html 指令: <span v-html="rawHtml"></span></p>
</div>
<script>
const RenderHtmlApp = {
data() {
return {
rawHtml: '<span style="color: red">这里会显示红色!</span>'
}
}
}

Vue.createApp(RenderHtmlApp).mount('#example1')
</script>

<!-- 属性 -->
<div v-bind:id="dynamicId"></div>

<button v-bind:disabled="isButtonDisabled">按钮</button>

<div id="app">
<label for="r1">修改颜色</label><input type="checkbox" v-model="use" id="r1">
<br><br>
<!-- 如果use为true则使用class1 -->
<div v-bind:class="{'class1': use}">
v-bind:class 指令
</div>
</div>
<script>
const app = {
data() {
return {
use: false
}
}
}

Vue.createApp(app).mount('#app')
</script>

<!-- 表达式,每个绑定都只能包含单个表达式 -->
<div id="app">
<!-- 算术运算 -->
{{5+5}}<br>
<!-- 三元运算符 -->
{{ ok ? 'YES' : 'NO' }}<br>
<!-- js表达式 -->
{{ message.split('').reverse().join('') }}
<!-- 字符串连接 -->
<div v-bind:id="'list-' + id"></div>
</div>

<script>
const HelloVueApp = {
data() {
return {
ok: true,
message: 'message',
id: 1
}
}
}

Vue.createApp(HelloVueApp).mount('#hello-vue')
</script>
tips:每个绑定只能包含单个表达式。

指令

指令是带有 v- 前缀的特殊属性。
指令用于在表达式的值改变时,将某些行为应用到DOM上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="app">
<!-- v-if指令将根据表达式seen的值(true或false)来决定是否插入p元素。 -->
<p v-if="seen">现在你看到我了</p>
</div>

<script>
const app = {
data() {
return {
seen: true /* 改为false,信息就无法显示 */
}
}
}

Vue.createApp(app).mount('#app')
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!-- v-for指令可以绑定数组的数据来渲染一个项目列表 -->
<div id="app">
<ol>
<li v-for="site in sites">
{{ site.text }}
</li>
</ol>
</div>
<script>
const app = {
data() {
return {
sites: [{
text: 'Google'
},
{
text: 'facebook'
},
{
text: 'Taobao'
}
]
}
}
}

Vue.createApp(app).mount('#app')
</script>

参数:参数在指令后以冒号指明。
例如: v-bind 指令被用来响应地更新HTML属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div id="app">
<p><a v-bind:href="url"></a></p>
</div>

<script>
const HelloVueApp = {
data() {
return {
url: 'https://www.baidu.com'
}
}
}

Vue.createApp(HelloVueApp).mount('#app')
</script>

v-on:

1
2
3
4
5
6
7
8
<!-- 完整语法 -->
<a v-on:click="doSomething"> ... </a>

<!-- 缩写 -->
<a @click="doSomething"> ... </a>

<!-- 动态参数的缩写 (2.6.0+) -->
<a @[event]="doSomething"> ... </a>

修饰符:

1
<form v-on:submit.prevent="onSubmit"></form>

输入

v-model 指令用来在input、select、textarea、checkbox、radio等表单控件元素上创建双向数据绑定,根据表单上的值,自动更新绑定的元素的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- 可以使用v-model指令来实现双向数据绑定 -->
<div id="app">
<p>{{ message }}</p>
<input v-model="message">
</div>

<script>
const app = {
data() {
return {
message: 'baidu'
}
}
}

Vue.createApp(app).mount('#app')
</script>

缩写

Vue.js为两个最为常用的指令提供了特别的缩写:

1
2
3
4
5
6
7
8
9
<!-- 完整语法 -->
<a v-bind:href="url"></a>
<!-- 缩写 -->
<a :href="url"></a>

<!-- 完整语法 -->
<a v-on:click="doSomething"></a>
<!-- 缩写 -->
<a @click="doSomething"></a>

条件语句

条件判断使用 v-if 指令,指令的表达式返回 true 时才会显示。

  • v-if
  • v-else
  • v-else-if
  • v-show

使用 v-show 指令来根据条件展示元素:

1
<h1 v-show="ok">Hello!</h1>
tips: 这里注意v-if和v-show的区别。

循环语句

循环使用 v-for 指令。
v-for 指令需要以 site in sites 形式的特殊语法, sites 是源数据数组并且 site 是数组元素迭代的别名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<div id="app">
<ol>
<li v-for="site in sites">
{{ site.text }}
</li>
</ol>
</div>
<script>
const app = {
data() {
return {
sites: [{
text: 'Google'
},
{
text: 'baidu'
},
{
text: 'Taobao'
}
]
}
}
}

Vue.createApp(app).mount('#app')
</script>

v-for 还支持一个可选的第二个参数,参数值为当前项的索引:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<div id="app">
<ol>
<li v-for="(site, index) in sites">
{{ index }} -{{ site.text }}
</li>
</ol>
</div>
<script>
const app = {
data() {
return {
sites: [{
text: 'Google'
},
{
text: 'baidu'
},
{
text: 'Taobao'
}
]
}
}
}

Vue.createApp(app).mount('#app')
</script>

v-for迭代对象

v-for 可以通过一个对象的属性来迭代数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div id="app">
<ul>
<li v-for="value in object">
{{ value }}
</li>
</ul>
</div>

<script>
const app = {
data() {
return {
object: {
name: '链接',
url: 'http://www.baidu.com',
slogan: '学的不仅是技术,更是梦想!'
}
}
}
}

Vue.createApp(app).mount('#app')
</script>

也可以提供第二个的参数为键名:

1
2
3
4
5
6
7
<div id="app">
<ul>
<li v-for="(value, key) in object">
{{ key }} : {{ value }}
</li>
</ul>
</div>

第三个参数为索引:

1
2
3
4
5
6
7
<div id="app">
<ul>
<li v-for="(value, key, index) in object">
{{ index }}. {{ key }} : {{ value }}
</li>
</ul>
</div>

v-for 迭代整数:

1
2
3
4
5
6
7
<div id="app">
<ul>
<li v-for="n in 10">
{{ n }}
</li>
</ul>
</div>

显示过滤/排序后的结果:可以对数组的元素进行处理后再显示出来,一般可以通过创建一个计算属性,来返回过滤或排序后的数组。

1
2
3
4
5
6
<!-- 输出数组中的偶数: -->
<div id="app">
<ul>
<li v-for="n in evenNumbers">{{ n }}</li>
</ul>
</div>

组件

注册组件

组件(Component)可以扩展HTML元素,封装可重用的代码。
可以使用多个组件构建大型应用。

注册全局组件:

1
2
3
4
5
6
7
const app = Vue.createApp({
...
})

app.component('my-component', {

})

使用该组件:

1
<my-component></my-component>

vue组件实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- vue应用挂载点 -->
<div id="app"></div>

<script>
// 创建vue应用
const app = Vue.createApp({})

// 在应用上注册组件componentA
app.component('componentA', {
template: '<h1>组件A</h1>'
})

// 将应用挂载到页面
app.mount('#app')
</script>

实例: button-counter 组件,在每次点击后,计数器会加1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script>
const app = Vue.createApp({})

app.component('button-counter', {
data() {
return {
count: 0
}
},
template: `
<button @click="count++">
点了 {{ count }} 次!
</button>
`
})

app.mount('#app')
</script>

组件复用

组件可以多次复用:

1
2
3
4
5
<div id="components-demo">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>

全局组件

注册全局组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
<div id="app">
<baidu></baidu>
</div>

<script>
const app = Vue.createApp({})

app.component('globalComponent', {
template: '<h1>全局组件</h1>'
})

app.mount('#app')
</script>

局部组件

可以通过一个js对象来定义组件,然后在 components 字段中定义要使用的组件:

1
2
3
4
5
6
7
8
9
10
// 定义组件
const componentA = {

}
const componentB = {

}
const componentC = {

}
1
2
3
4
5
6
7
8
// 使用组件
const app = Vue.createApp({
// 创建应用,通过components字段配置所使用到的组件
components: {
'component-a': componentA,
'component-b': componentB
}
})

实例中的局部组件,只能在该实例中使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div id="app">
<part-component></part-component>
</div>

<script>
var partComponent = {
template: '<h1>局部组件</h1>'
}

const app = Vue.createApp({
components: {
'part-component': partComponent
}
})

app.mount('#app')
</script>

prop

prop 是子组件用来接受父组件传递的数据的属性。

父组件通过 props 将数据传给子组件,子组件需要用 props 声明 prop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div id="appo">
<site-name title="Google">
</site-namet>
<site-name title="baidu">
</site-namet>
<site-name title="Taobao"></site-name>
</div>

<script>
const app = Vue.createApp({})

app.component('site-name', {
// 将title属性暴露给子组件
props: ['title'],
template: `<h4>{{ title }}</h4>`
})

app.mount('#app')
</script>

动态prop:

可以使用 v-bind 动态绑定props的值到父组件的数据中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<div id="app">
<site-info v-for="site in sites" :id="site.id" :title="site.title"></site-info>
</div>

<script>
const Site = {
data() {
return {
sites: [{
id: 1,
title: 'Google'
},
{
id: 2,
title: 'baidu'
},
{
id: 3,
title: 'Taobao'
}
]
}
}
}

const app = Vue.createApp(Site)

app.component('site-info', {
props: ['id', 'title'],
template: `<h4>{{ id }} - {{ title }}</h4>`
})

app.mount('#app')
</script>

prop验证:

组件可以给 props 指定验证条件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Vue.component('my-component', {
props: {
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function() {
return {
message: 'hello'
}
}
},
// 自定义验证函数
propF: {
validator: function(value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
})

type 可以是下面原生构造器:

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol

type 也可以是一个自定义构造器,使用 instanceof 检测。

$emit

$emit 用于子组件向父组件传递数据。

具体方法是:子组件使用 $emit 触发自定义事件,父组件使用 v-on 指令监听子组件的自定义事件。

1
2
// 子组件触发事件,args为传递给父组件的参数
vm.$emit(eventName, [...args]);

.sync

父组件调用子组件时使用sync修饰符可以做到父子组件的属性的双向数据绑定。

1
2
<!-- val是父组件的数据 -->
<child :value.sync="val"></child>

计算

计算: computed

实例:反转字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div id="app">
<p>原始字符串: {{ message }}</p>
<p>计算后反转字符串: {{ reversedMessage }}</p>
</div>

<script>
const app = {
data() {
return {
message: 'baidu!!'
}
},
computed: {
// 计算属性的 getter
reversedMessage: function() {
// this指向 vm 实例
return this.message.split('').reverse().join('')
}
}
}

Vue.createApp(app).mount('#app')
</script>

computed vs methods

可以使用 methods 代替 computed
但是computed依赖缓存,只有依赖关系发生变化才会重新计算。
而methods在重新渲染时总是会重新执行。

1
2
3
4
5
methods: {
reversedMessage2: function() {
return this.message.split('').reverse().join('')
}
}

computed setter

computed属性默认只有 getter ,也可以设置 setter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var vm = new Vue({
el: '#app',
data: {
name: 'Google',
url: 'http://www.google.com'
},
computed: {
site: {
// getter
get: function() {
return this.name + ' ' + this.url
},
// setter
set: function(newValue) {
var names = newValue.split(' ')
this.name = names[0]
this.url = names[names.length - 1]
}
}
}
})
// 调用 setter, vm.name 和 vm.url 也会被对应更新
vm.site = '百度 http://www.baidu.com';
document.write('name: ' + vm.name);
document.write('<br>');
document.write('url: ' + vm.url);

监听

可以使用监听属性 watch 来响应数据变化。

使用watch实现计数器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div id="app">
<p style="font-size:25px;">计数器: {{ counter }}</p>
<button @click="counter++" style="font-size:25px;">点我</button>
</div>

<script>
const app = {
data() {
return {
counter: 1
}
}
}
vm = Vue.createApp(app).mount('#app')
vm.$watch('counter', function(nval, oval) {
alert('计数器值的变化 :' + oval + ' 变为 ' + nval + '!');
});
</script>

样式绑定

可以使用 v-bind 绑定属性,可以绑定字符串、对象、数组。

v-bind:class === :class

绑定class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- 根据isActive是true还是false来是否添加active这个class -->
<div :class="{'active': isActive }"></div>

<!-- isActive为true -->
<div class="active"></div>

<!-- isActive为false -->
<div class=""></div>

<!-- :class可以与class属性共存 -->
<div class="static" :class="{ 'active' : isActive, 'text-danger' : hasError }"></div>

<!-- 直接绑定一个data中的对象 -->
<div class="static" :class="classObject"></div>

绑定数组

1
2
3
4
5
<!-- 绑定数组 -->
<div class="static" :class="[activeClass, errorClass]"></div>

<!-- 三元表达式 -->
<div class="static" :class="[isActive ? activeClass : '', errorClass]"></div>

style

可以 v-bind:style 设置内联样式。

1
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>

可以直接绑定到一个样式对象:

1
<div :style="styleObject"></div>

使用数组将多个样式对象绑定到一个元素上:

1
<div :style="[baseStyles, overridingStyles]"></div>

可以给css样式同一个属性提供不同的值:

1
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>

组件中使用class

下面四个class都会有:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div id="app">
<baidu class="classC classD"></baidu>
</div>

<script>
// 创建一个Vue 应用
const app = Vue.createApp({})

// 定义一个名为 baidu的新全局组件
app.component('baidu', {
template: '<h1 class="classA classB">I like baidu!</h1>'
})

app.mount('#app')
</script>

如果组件有多个根元素,通过 $attrs 属性定义那些部分接受这个class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="app">
<baidu class="classA"></baidu>
</div>

<script>
const app = Vue.createApp({})

app.component('baidu', {
template: `
<p :class="$attrs.class">I like baidu!</p>
<span>这是一个子组件</span>
`
})

app.mount('#app')
</script>

事件处理

可以使用 v-on 监听DOM事件。

v-on 可以缩写为 @

1
2
3
v-on:click="func"

@click="func"

v-on :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="app">
<button @click="counter += 1">增加 1</button>
<p>这个按钮被点击了 {{ counter }} 次。</p>
</div>

<script>
const app = {
data() {
return {
counter: 0
}
}
}

Vue.createApp(app).mount('#app')
</script>

实例2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<div id="app">
<!-- `greet` 是在下面定义的方法名 -->
<button @click="greet">点我</button>
</div>

<script>
const app = {
data() {
return {
name: 'baidu'
}
},
methods: {
greet(event) {
// `methods` 内部的 `this` 指向当前活动实例
alert('Hello ' + this.name + '!')
// `event` 是原生 DOM event
if (event) {
alert(event.target.tagName)
}
}
}
}

Vue.createApp(app).mount('#app')
</script>

实例3:可以绑定多个方法,逗号隔开

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div id="app">
<!-- 这两个 one() 和 two() 将执行按钮点击事件 -->
<button @click="one($event), two($event)">
点我
</button>
</div>

<script>
const app = {
data() {},
methods: {
one(event) {
alert("第一个事件处理器逻辑...")
},
two(event) {
alert("第二个事件处理器逻辑...")
}
}
}

Vue.createApp(app).mount('#app')
</script>

事件修饰符

Vue通过由点 . 表示的指令后缀来调用修饰符。

  • .stop - 阻止冒泡
  • .prevent - 阻止默认事件
  • .capture - 阻止捕获
  • .self - 只监听触发该元素的事件
  • .once - 只触发一次
  • .left - 左键事件
  • .right - 右键事件
  • .middle - 中间滚轮事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 阻止单击事件冒泡 -->
<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>

按键修饰符

Vue允许为 v-on 在监听键盘事件时添加按键修饰符:

1
2
<!-- 只有在 keyCode 是 13 时调用 vm.submit() -->
<input v-on:keyup.13="submit">

记住所有的 keyCode 比较困难,所以 Vue 为最常用的按键提供了别名:

1
2
3
4
<!-- 同上 -->
<input v-on:keyup.enter="submit">
<!-- 缩写语法 -->
<input @keyup.enter="submit">

全部的按键别名:

  • .enter
  • .tab
  • .delete (捕获 “删除” 和 “退格” 键)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right

系统修饰键:

  • .ctrl
  • .alt
  • .shift
  • .meta

鼠标按钮修饰符:

  • .left
  • .right
  • .middle
1
2
3
4
5
<p>
<!-- Alt + C -->
<input @keyup.alt.67="clear">
<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>

.exact修饰符

.exact 允许精准控制系统修饰符组合触发的事件。

1
2
3
4
5
6
7
8
<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
<button @click.ctrl="onClick">A</button>

<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button @click.ctrl.exact="onCtrlClick">A</button>

<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button @click.exact="onClick">A</button>

表单

可以使用 v-modelinputtextareaselect 等元素上创建双向数据绑定。

v-model 会忽略所有表单元素的value、checked、selected属性的初始值,使用的是data选项中声明初始值。

v-model在内部为不同的输入元素使用不同的属性并抛出不同的事件:

  • text和textarea元素使用value属性和input事件;
  • checkbox和radio使用checked属性和change事件;
  • select字段将value作为属性并将change作为事件。

使用v-model实现双向数据绑定:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<div id="app">
<p>input 元素:</p>
<input v-model="message" placeholder="编辑我……">
<p>input 表单消息是: {{ message }}</p>

<p>textarea 元素:</p>
<textarea v-model="message2" placeholder="多行文本输入……"></textarea>
<p>textarea 表单消息是:</p>
<p style="white-space: pre">{{ message2 }}</p>

</div>

<script>
const app = {
data() {
return {
message: '',
message2: '百度\r\nhttps://www.baidu.com'
}
}
}

Vue.createApp(app).mount('#app')
</script>

在文本区域 textarea 插值是不起作用,需要使用 v-model 来代替:

1
2
3
4
5
<!-- 错误 -->
<textarea>{{ text }}</textarea>

<!-- 正确 -->
<textarea v-model="text"></textarea>

复选框双向数据绑定:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<div id="app">
<p>单个复选框:</p>
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>

<p>多个复选框:</p>
<input type="checkbox" id="baidu" value="baidu" v-model="checkedNames">
<label for="baidu">baidu</label>
<input type="checkbox" id="google" value="Google" v-model="checkedNames">
<label for="google">Google</label>
<input type="checkbox" id="taobao" value="Taobao" v-model="checkedNames">
<label for="taobao">taobao</label>
<br>
<span>选择的值为: {{ checkedNames }}</span>
</div>

<script>
const app = {
data() {
return {
checked: false,
checkedNames: []
}
}
}

Vue.createApp(app).mount('#app')
</script>

单选按钮:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<div id="app">
<input type="radio" id="baidu" value="baidu" v-model="picked">
<label for="baidu">baidu</label>
<br>
<input type="radio" id="google" value="Google" v-model="picked">
<label for="google">Google</label>
<br>
<span>选中值为: {{ picked }}</span>
</div>

<script>
const app = {
data() {
return {
picked: 'baidu'
}
}
}

Vue.createApp(app).mount('#app')
</script>

select:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<div id="app">
<select v-model="selected" name="fruit">
<option value="">选择一个网站</option>
<option value="www.baidu.com">baidu</option>
<option value="www.google.com">Google</option>
</select>

<div id="output">
选择的网站是: {{selected}}
</div>
</div>

<script>
new Vue({
el: '#app',
data: {
selected: ''
}
})
</script>

v-for:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<div id="app" class="demo">
<select v-model="selected">
<option v-for="option in options" :value="option.value">
{{ option.text }}
</option>
</select>
<span>选择的是: {{ selected }}</span>
</div>

<script>
const app = {
data() {
return {
selected: 'www.baidu.com',
options: [{
text: 'baidu',
value: 'www.baidu.com'
},
{
text: 'Google',
value: 'www.google.com'
},
{
text: 'Taobao',
value: 'www.taobao.com'
}
]
}
}
}

Vue.createApp(app).mount('#app')
</script>

值绑定:

修饰符:
.lazy :在change事件中更新而不是在input事件中更新。

1
<input v-model.lazy="msg">

.number :转化为Number类型。

.trim :过滤输入的首尾空格。

1
<input v-model.trim="msg">

自定义指令

Vue允许注册自定义指令。

注册一个自定义的全局指令 v-focus ,功能是页面加载时元素获得焦点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<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注册局部指令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<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')

钩子函数

指令定义函数提供了几个钩子函数(可选):

  • created : 在绑定元素的属性或事件监听器被应用之前调用。
  • beforeMount : 指令第一次绑定到元素并且在挂载父组件之前调用。。
  • mounted : 在绑定元素的父组件被挂载后调用。。
  • beforeUpdate: 在更新包含组件的 VNode 之前调用。。
  • updated: 在包含组件的 VNode 及其子组件的 VNode 更新后调用。
  • beforeUnmount: 当指令与元素解除绑定且父组件已卸载时,只调用一次。
  • unmounted: 当指令与元素解除绑定且父组件已卸载时,只调用一次。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import {
createApp
} from 'vue'
const app = createApp({})

// 注册
app.directive('my-directive', {
// 指令是具有一组生命周期的钩子:
// 在绑定元素的 attribute 或事件监听器被应用之前调用
created() {},
// 在绑定元素的父组件挂载之前调用
beforeMount() {},
// 绑定元素的父组件被挂载时调用
mounted() {},
// 在包含组件的 VNode 更新之前调用
beforeUpdate() {},
// 在包含组件的 VNode 及其子组件的 VNode 更新之后调用
updated() {},
// 在绑定元素的父组件卸载之前调用
beforeUnmount() {},
// 卸载绑定元素的父组件时调用
unmounted() {}
})

// 注册 (功能指令)
app.directive('my-directive', () => {
// 这将被作为 `mounted` 和 `updated` 调用
})

// getter, 如果已注册,则返回指令定义
const myDirective = app.directive('my-directive')

钩子函数参数

  • el: 绑定到的元素
  • binding: 一个对象,包含instance, value, oldValue, arg, modifiers, dir
  • vnode
  • prevNode

路由

路由允许我们通过不同的url访问不同的内容。
可以实现单页面多视图应用。

需要引入vue-router。

引入

  1. 引入文件: https://unpkg.com/vue-router@4.0.5/dist/vue-router.global.js
  2. npm安装:cnpm install vue-router@4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script src="https://unpkg.com/vue@3"></script>
<script src="https://unpkg.com/vue-router@4"></script>

<div id="app">
<h1>Hello App!</h1>
<p>
<!--使用 router-link 组件进行导航 -->
<!--通过传递 `to` 来指定链接 -->
<!--`<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签-->
<router-link to="/">Go to Home</router-link>
<router-link to="/about">Go to About</router-link>
</p>
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>
</div>

router-view将显示与url对应的组件。你可以把它放在任何地方,以适应你的布局。

混入

混入(mixins)定义了一部分可复用的方法或者计算属性。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 定义混入对象
const myMixin = {
created() {
this.hello()
},
methods: {
hello() {
console.log('欢迎来到混入实例-baidu!')
}
}
}

// 定义一个应用,使用混入
const app = Vue.createApp({
mixins: [myMixin]
})

app.mount('#app') // => "欢迎来到混入实例-baidu!"

axios

axios是一个基于 Promise 的http库。
可用于浏览器和node。

安装:

  1. 直接引入文件。
  2. npm:cnpm install axios -S

使用:

1
2
3
4
5
6
7
8
9
10
11
Vue.axios.get(api).then((response) => {
console.log(response.data);
})

this.axios.get(api).then((response) => {
console.log(response.data);
})

this.$http.get(api).then((response) => {
console.log(response.data);
})

GET

读取JSON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const app = {
data() {

return {
info: 'Ajax 测试!!'
}

},
mounted () {

axios
.get('https://www.baidu.com/try/ajax/json_demo.json')
.then(response => (this.info = response))
.catch(function (error) { // 请求失败处理
console.log(error);
});

}
}

Vue.createApp(app).mount('#app')

GET传参:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 直接在 URL 上添加参数 ID=12345
axios.get('/user?ID=12345')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});

// 也可以通过 params 设置参数:
axios.get('/user', {
params: {
ID: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});

POST

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
new Vue({
el: '#app',
data() {
return {
info: null
}
},
mounted() {
axios
.post('https://www.baidu.com/try/ajax/demo_axios_post.php')
.then(response => (this.info = response))
.catch(function(error) { // 请求失败处理
console.log(error);
});
}
})

POST传参:

1
2
3
4
5
6
7
8
9
10
axios.post('/user', {
firstName: 'Fred', // 参数 firstName
lastName: 'Flintstone' // 参数 lastName
})
.then(function(response) {
console.log(response);
})
.catch(function(error) {
console.log(error);
});

执行多个并发请求

1
2
3
4
5
6
7
8
9
10
11
function getUserAccount() {
return axios.get('/user/12345');
}

function getUserPermissions() {
return axios.get('/user/12345/permissions');
}
axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function(acct, perms) {
// 两个请求现在都执行完成
}));

axios API

可以通过向axios传递配置来创建请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
axios(config)
// 发送 POST 请求
axios({
method: 'post',
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
});
// GET 请求远程图片
axios({
method: 'get',
url: 'http://bit.ly/2mTM3nY',
responseType: 'stream'
})
.then(function(response) {
response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'))
});
axios(url[, config])
// 发送 GET 请求(默认的方法)
axios('/user/12345');

请求方法的别名:

1
2
3
4
5
6
7
axios.request(config)
axios.get(url[, config])
axios.delete(url[, config])
axios.head(url[, config])
axios.post(url[, data[, config]])
axios.put(url[, data[, config]])
axios.patch(url[, data[, config]])
在使用别名方法时, url、method、data 这些属性都不必在配置中指定。

并发:

1
2
axios.all(iterable)
axios.spread(callback)

创建实例:

1
2
3
4
5
6
7
8
axios.create([config])
const instance = axios.create({
baseURL: 'https://some-domain.com/api/',
timeout: 1000,
headers: {
'X-Custom-Header': 'foobar'
}
});

实例方法:

1
2
3
4
5
6
7
axios #request(config)
axios #get(url[, config])
axios #delete(url[, config])
axios #head(url[, config])
axios #post(url[, data[, config]])
axios #put(url[, data[, config]])
axios #patch(url[, data[, config]])

拦截器

在请求被 then 或者 catch 处理之前拦截。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 添加请求拦截器
axios.interceptors.request.use(function(config) {
// 在发送请求之前做些什么
return config;
}, function(error) {
// 对请求错误做些什么
return Promise.reject(error);
});

// 添加响应拦截器
axios.interceptors.response.use(function(response) {
// 对响应数据做点什么
return response;
}, function(error) {
// 对响应错误做点什么
return Promise.reject(error);
});

移除拦截器:

1
2
3
4
var myInterceptor = axios.interceptors.request.use(function() {
/*...*/
});
axios.interceptors.request.eject(myInterceptor);

调试

使用浏览器调试vue。

  1. 浏览器安装vue.js devtools插件

  2. vue开启配置

main.js 添加:

main.js
1
Vue.config.devtools = true

ref

  1. https://v3.cn.vuejs.org/guide/introduction.html
  2. 《Vue 3.0从入门到精通》

评论