Vue3-05

侦听器

我们可以通过 watch 选项来侦听一个特定的值,当被侦听的值发生变化时,就会执行回调函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
<div>
<input v-model="name" />
<p>{{ name }}</p>
</div>
</template>

<script>
export default {
data() {
return {
name: "a",
};
},
watch: {
name(newValue, oldValue) {
console.log(newValue, oldValue);
},
},
};
</script>

上面这个例子实现了一个简单的双向绑定,当输入框的值发生变化时,会触发 watch 中的回调函数。

表单输入绑定

在 Vue3 中,表单的绑定方式与 Vue2 中有所不同。

在 Vue2 中,我们可以通过 v-model 来实现表单的双向绑定,但是在 Vue3 中,v-model 只能用于组件上,不能用于原生的表单元素上。

e.g.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<div>
<input v-model="name" />
<p>{{ name }}</p>
</div>
</template>

<script>
export default {
data() {
return {
name: "a",
};
},
};
</script>

在 Vue3 中,我们可以通过 v-model 的另一种写法来实现表单的双向绑定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<div>
<input :value="name" @input="name = $event.target.value" />
<p>{{ name }}</p>
</div>
</template>

<script>
export default {
data() {
return {
name: "a",
};
},
};
</script>

上面这个例子实现了一个简单的双向绑定,当输入框的值发生变化时,会触发 input 事件,然后将输入框的值赋值给 name

模板引用

在 Vue3 中,我们可以通过 ref 来获取模板中的元素。

也就是说,虽然 Vue 中不推荐直接操作 DOM,但是我们可以通过 ref 来获取 DOM 元素,然后操作 DOM。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<div>
<input ref="input" />
<button @click="handleClick">获取输入框的值</button>
</div>
</template>

<script>
export default {
methods: {
handleClick() {
console.log(this.$refs.input.value);
},
},
};
</script>

挂载结束后,this.$refs.input 就是一个 DOM 元素,我们可以通过 this.$refs.input.value 来获取输入框的值。

没有特殊情况,我们不推荐直接操作 DOM,但是在某些情况下,我们不得不直接操作 DOM,比如在某些第三方库中,我们需要传入一个 DOM 元素,这时候我们就可以通过 ref 来获取 DOM 元素。

组件组成

组件是 Vue 中最重要的概念之一,组件可以帮助我们将一个复杂的页面拆分成多个简单的组件,然后组合起来,这样就可以提高代码的可维护性。

当使用构建工具时,我们可以将组件拆分成多个文件(一个单独的 .vue 文件,这些被称为 SFC 单文件组件),每个文件都包含三部分内容:templatescriptstyle

组件组成结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<div>
<h1>组件组成</h1>
</div>
</template>

<script>
export default {
name: "ComponentComposition",
};
</script>

<style scoped>
h1 {
color: red;
}
</style>

用时候 <style> 标签中的 scoped 属性,可以让样式只作用于当前组件。

组件引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<div>
<h1>组件引用</h1>
<ComponentComposition />
</div>
</template>

<script>
import ComponentComposition from "./ComponentComposition.vue";

export default {
name: "ComponentComposition",
components: {
ComponentComposition,
},
};
</script>

实例

components1.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<div>
<h1>组件引用</h1>
</div>
<div class="container">{{ message }}</div>
</template>

<script>
export default {
name: "components1",
data() {
return {
message: "Hello Vue!",
};
},
};
</script>

<style>
.container {
color: red;
}
</style>

App.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<div>
<h1>组件引用</h1>
<components1 />
</div>
</template>

<script>
import components1 from ".//components/components1.vue";

export default {
name: "App",
components: {
components1,
},
};
</script>

组件嵌套关系

在 Vue 中,组件可以嵌套,也就是说,一个组件可以作为另一个组件的子组件。

创建 pages 文件夹,在 pages 文件夹中创建 Header.vueMain.vueAside.vueAeti.vueItem.vue五个组件。

通过组件间的嵌套,使得组件之间的关系更加清晰,通常形成的组件树如下:

1
2
3
4
5
6
7
8
9
App
├── Header
├── Main
│ ├── Article
│ └── Article
└── Aside
├── Item
├── Item
└── Item
代码-点击展开

Header.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<div>
<h1>Header</h1>
</div>
</template>

<style scoped>
h3 {
width: 100%;
height: 100px;
border: 5px solid #999;
text-align: center;
line-height: 100px;
box-sizing: border-box;
}
</style>

Main.vue

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
<template>
<div class="main">
<h3>Main</h3>
<Article />
<Article />
</div>
</template>

<script>
import Article from "./Article.vue";

export default {
name: "Main",
components: {
Article,
},
};
</script>

<style scoped>
.main {
width: 70%;
height: 500px;
background-color: #999;
float: left;
box-sizing: border-box;
border: 5px solid #999;
}
</style>

Aside.vue

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
<template>
<div class="aside">
<h3>Aside</h3>
<Item />
<Item />
<Item />
</div>
</template>

<script>
import Item from "./Item.vue";

export default {
name: "Aside",
components: {
Item,
},
};
</script>

<style scoped>
.aside {
width: 30%;
height: 500px;
background-color: #999;
float: left;
box-sizing: border-box;
border: 5px solid #999;
}
</style>

Article.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<div class="article">
<h3>Article</h3>
</div>
</template>

<style scoped>
.article {
width: 80%;
height: 200px;
background-color: #999;
box-sizing: border-box;
border: 5px solid #999;
margin: 0 auto;
}
</style>

Item.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<div class="item">
<h3>Item</h3>
</div>
</template>

<style scoped>
.item {
width: 80%;
height: 100px;
background-color: #999;
box-sizing: border-box;
border: 5px solid #999;
margin: 0 auto;
}
</style>

App.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
<div>
<Header />
<Main />
<Aside />
</div>
</template>

<script>
import Header from "./pages/Header.vue";
import Main from "./pages/Main.vue";
import Aside from "./pages/Aside.vue";

export default {
name: "App",
components: {
Header,
Main,
Aside,
},
};
</script>