之前的文章我们介绍了一下 vue 中的作用域插槽,本章我们来看一下动态组件与 v-once 指令。
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>vue</title> 6 <script src="https://cdn.jsdelivr.net/npm/vue"></script> 7 </head> 8 <body> 9 <div id="app"> 10 <child-one v-if="type === 'child-one'"></child-one> 11 <child-two v-if="type === 'child-two'"></child-two> 12 <p @click="handleClick">clickMe</p> 13 </div> 14 <script> 15 Vue.component("child-one", { 16 template: `<p>我是第一个子组件</p>` 17 }); 18 Vue.component("child-two", { 19 template: `<p>我是第二个子组件</p>` 20 }); 21 var app = new Vue({ 22 el: '#app', 23 data: { 24 type: "child-one" 25 }, 26 methods: { 27 handleClick() { 28 console.log("type 数据更改前", this.type); 29 this.type = this.type === "child-one" ? "child-two" : "child-one"; 30 console.log("type 数据更改后", this.type) 31 } 32 } 33 }) 34 </script> 35 </body> 36 </html>
上面的代码中,我们定义了两个子组件 child-one 和 child-two,并且我们在父组件中定义了一个 handleClick() 方法,当点击时我们通过父组件中 type 的值来使两个子组件进行显隐,结果如下:
当点击 clickMe 时显示结果符合我们的预期,其实上面的代码可以改成如下:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>vue</title> 6 <script src="https://cdn.jsdelivr.net/npm/vue"></script> 7 </head> 8 <body> 9 <div id="app"> 10 <component :is="type"></component> 11 <p @click="handleClick">clickMe</p> 12 </div> 13 <script> 14 Vue.component("child-one", { 15 template: `<p>我是第一个子组件</p>` 16 }); 17 Vue.component("child-two", { 18 template: `<p>我是第二个子组件</p>` 19 }); 20 var app = new Vue({ 21 el: '#app', 22 data: { 23 type: "child-one" 24 }, 25 methods: { 26 handleClick() { 27 console.log("type 数据更改前", this.type); 28 this.type = this.type === "child-one" ? "child-two" : "child-one"; 29 console.log("type 数据更改后", this.type) 30 } 31 } 32 }) 33 </script> 34 </body> 35 </html>
我们将 <child-one> <child-two> 标签删除掉,改为 <component> 标签内,并在标签内写上 :is="type",<component> 标签是一个动态标签的意思,我们也可以将它写成 <p> 或者 <h1> 都行,:is="type" ,根据父组件 type 数据的值和 handleClick() 方法对 type 数据改变来确定 :is="type" 内 type 的值是 child-one 还是 child-two,这样就能动态地改变时显示 child-one 组件还是 child-two 组件,结果如下:
运行结果和上面是一样的。
接下来我们来看一下 v-once 指令。
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>vue</title> 6 <script src="https://cdn.jsdelivr.net/npm/vue"></script> 7 </head> 8 <body> 9 <div id="app"> 10 <child-one v-if="type === 'child-one'"></child-one> 11 <child-two v-if="type === 'child-two'"></child-two> 12 <p @click="handleClick">clickMe</p> 13 </div> 14 <script> 15 Vue.component("child-one", { 16 template: `<p v-once>我是第一个子组件</p>` 17 }); 18 Vue.component("child-two", { 19 template: `<p v-once>我是第二个子组件</p>` 20 }); 21 var app = new Vue({ 22 el: '#app', 23 data: { 24 type: "child-one" 25 }, 26 methods: { 27 handleClick() { 28 console.log("type 数据更改前", this.type); 29 this.type = this.type === "child-one" ? "child-two" : "child-one"; 30 console.log("type 数据更改后", this.type) 31 } 32 } 33 }) 34 </script> 35 </body> 36 </html>
上面的代码中我们在每一个子组件的 template 模板中的 <p> 表内都加了一个 v-once 属性,如果不加这个指令的话我们每次通过 handleClick() 方法来判断 type 时,子组件就会进行一个销毁,另一个重新挂载,就会消耗不必要的内存。加上 v-once 时,当第一次挂载时需要渲染一次,当销毁时并不是彻底删除掉,而是留在了内存中,当需要重新挂载时就不需要重新渲染,而是直接去内存中拿,这样就能减少不必要的内存消耗。