侦听器
我们可以通过 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 单文件组件),每个文件都包含三部分内容:template、script 和 style。
组件组成结构如下:
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.vue、Main.vue、Aside.vue、Aeti.vue、Item.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>
|