三. 模块引用
3.1. $refs
某些情况下,我们在组件中想要直接获取到元素对象或者子组件实例:
- 在Vue开发中我们是不推荐进行DOM操作的;
- 这个时候,我们可以给元素或者组件绑定一个ref的attribute属性;
组件实例有一个$refs属性:
- 它一个对象Object,持有注册过
ref
attribute 的所有 DOM 元素和组件实例。
App.vue的实现:
<template> <div> <h2 ref="title">哈哈哈</h2> <hello-world ref="helloCpn"></hello-world> <button @click="visitElement">访问元素或者组件</button> </div> </template> <script> import HelloWorld from './HelloWorld.vue'; export default { components: { HelloWorld }, methods: { visitElement() { // 访问元素 console.log(this.$refs.title); // 访问组件实例 this.$refs.helloCpn.showMessage(); } } } </script>
HelloWorld.vue实现:
<template> <div> </div> </template> <script> export default { methods: { showMessage() { console.log("我是HelloWorld组件的showMessage方法"); } } } </script>
3.2. $parent
我们可以通过$parent来访问父元素。
HelloWorld.vue的实现:
- 这里我们也可以通过$root来实现,因为App是我们的根组件;
<template> <div> <button @click="visitParent">访问父组件</button> </div> </template> <script> export default { methods: { showMessage() { console.log("我是HelloWorld组件的showMessage方法"); }, visitParent() { console.log(this.$parent.message); } } } </script>
注意:在Vue3中已经移除了$children的属性,所以不可以使用了。
四. 生命周期
4.1. 生命周期图片
什么是生命周期呢?
- 每个组件都会经历从创建、挂载、更新、卸载等一系列的过程;
- 在这个过程中的某一个阶段,用于可能会想要添加一些属于自己的代码逻辑(比如组件创建完后就请求一些服务器数据);
- 但是我们如何可以知道目前组件正在哪一个过程呢?Vue给我们提供了组件的生命周期函数;
生命周期函数:
- 生命周期函数是一些钩子函数,在某个时间会被Vue源码内部进行回调;
- 通过对生命周期函数的回调,我们可以知道目前组件正在经历什么阶段;
- 那么我们就可以在该生命周期中编写属于自己的逻辑代码了;
实例的生命周期
4.2. 生命周期演练
我们通过一个App和Home来演练所有的生命周期函数。
App.vue组件对象:
<template> <div> <button @click="toggle">切换</button> <div v-if="isShow"> <home></home> </div> </div> </template> <script> import Home from './Home.vue'; export default { components: { Home }, data() { return { isShow: true } }, methods: { toggle() { this.isShow = !this.isShow; console.log(this.isShow); } } } </script>
Home.vue组件对象:
<template> <div> <button @click="changeMessage">修改message</button> <h2 ref="titleRef">{{message}}</h2> </div> </template> <script> export default { data() { return { message: "Hello World" } }, methods: { changeMessage() { this.message = "你好啊,李银河"; } }, beforeUpdate() { console.log("beforeUpdate"); console.log(this.$refs.titleRef.innerHTML); }, updated() { console.log("updated"); console.log(this.$refs.titleRef.innerHTML); }, beforeCreate() { console.log("beforeCreate"); }, created() { console.log("created"); }, beforeMount() { console.log("beforeMount"); }, mounted() { console.log("mounted"); }, beforeUnmount() { console.log("beforeUnmount"); }, unmounted() { console.log("unmounted"); } } </script>
五. 组件的v-model
5.1. 组件的v-model
前面我们在input中可以使用v-model来完成双向绑定:
- 这个时候往往会非常方便,因为v-model默认帮助我们完成了两件事;
v-bind:value
的数据绑定和@input
的事件监听;
如果我们现在封装了一个组件,其他地方在使用这个组件时,是否也可以使用v-model来同时完成这两个功能呢?
- 也是可以的,vue也支持在组件上使用v-model;
当我们在组件上使用的时候,等价于如下的操作:
- 我们会发现和input元素不同的只是属性的名称和事件触发的名称而已;
那么,为了我们的MyInput组件可以正常的工作,这个组件内的 <input>
必须:
- 将其
value
attribute 绑定到一个名叫modelValue
的 prop 上; - 在其
input
事件被触发时,将新的值通过自定义的update:modelValue
事件抛出;
MyInput.vue的组件代码如下:
<template> <div> <input :value="modelValue" @input="inputChange"> </div> </template> <script> export default { props: ["modelValue"], emits: ["update:modelValue"], methods: { inputChange(event) { this.$emit("update:modelValue", event.target.value); } } } </script>
在App.vue中,我们在使用MyInput可以直接使用v-model:
<template> <div> <my-input v-model="message"/> <button @click="changeMessage">修改message</button> </div> </template> <script> import MyInput from './MyInput.vue'; export default { components: { MyInput }, data() { return { message: "" } }, methods: { changeMessage() { this.message = "Hello World" } } } </script>
5.2. computed实现
在上面的案例中,我们可能会想到一种实现方法:直接将Props中的属性双向绑定到input上
<template> <div> <input v-model="modelValue"> </div> </template> <script> export default { props: ["modelValue"] } </script>
上面这种方式可以实现组件的双向绑定吗?答案是不可以
- 因为我们在内部修改了props之后,外界并不知道我们对props的修改,所以并不会将事件传递出去;
- 另外,在开发中直接修改props中的属性不是一个好的习惯,不要这样去做;
那么,我们依然希望在组件内部按照双向绑定的做法去完成,应该如何操作呢?我们可以使用计算属性的setter
和getter
来完成。
<template> <div> <input v-model="value"> </div> </template> <script> export default { props: ["modelValue"], emits: ["update:modelValue"], computed: { value: { get() { return this.modelValue; }, set(value) { this.$emit("update:modelValue", value) } } } } </script>
5.3. 绑定多个属性
我们现在通过v-model是直接绑定了一个属性,如果我们希望绑定多个属性呢?也就是我们希望在一个组件上使用多个v-model是否可以实现呢?
- 我们知道,默认情况下的v-model其实是绑定了
modelValue
属性和@update:modelValue
的事件; - 如果我们希望绑定更多,可以给v-model传入一个参数,那么这个参数的名称就是我们绑定属性的名称;
我们先看一下在App.vue中我是如何使用的:
<template> <div> <my-input v-model="message" v-model:title="title"/> <h2>{{message}}</h2> <button @click="changeMessage">修改message</button> <hr> <h2>{{title}}</h2> <button @click="changeTitle">修改title</button> </div> </template> <script> import MyInput from './MyInput.vue'; export default { components: { MyInput }, data() { return { message: "", title: "" } }, methods: { changeMessage() { this.message = "Hello World" }, changeTitle() { this.title = "Hello Title" } } } </script>
注意:这里我是绑定了两个属性的
<my-input v-model="message" v-model:title="title"/>
v-model:title
相当于做了两件事:
- 绑定了
title
属性; - 监听了
@update:title
的事件;
所以,我们MyInput中的实现如下:
<template> <div> <input :value="modelValue" @input="input1Change"> <input :value="title" @input="input2Change"> </div> </template> <script> export default { props: ["modelValue", "title"], emits: ["update:modelValue", "update:title"], methods: { input1Change(event) { this.$emit("update:modelValue", event.target.value); }, input2Change(event) { this.$emit("update:title", event.target.value); } } } </script>