【Vue.js】017-Vue组件:基本使用、组件注册、父子组件、父子组件通信

布满荆棘的人生 2023-10-05 18:21 115阅读 0赞

目录

一、组件化的实现和使用步骤

1、什么是组件化

2、Vue的组件化思想

3、组件的基本步骤

步骤:

图示:

二、组件化的基本使用过程

1、代码演示

2、运行结果

三、全局组件和局部组件

1、全局组件

代码演示:

运行结果:

2、局部组件

代码演示:

运行结果(只能用在特定实例之内了):

四、父组件和子组件

1、简单使用

代码演示:

运行结果:

五、注册组件语法糖

1、概述

2、演示

六、组件模板的分离写法

1、概述

2、两种写法

代码演示:

运行结果:

七、组件数据的存放

1、概述

2、代码演示

3、运行结果

八、父子组件的通信

1、概述

2、props基本用法

概述:

props的值有两种方式:

图解:

代码演示:

运行结果:

3、props数据验证

概述:

自定义类型:

演示:

4、子级向父级传递

概述:

什么时候需要自定义事件呢?

自定义事件的流程:

图解:

自定义事件代码演示:

运行结果:

九、父子组件的访问方式

1、概述

2、$children

概述:

图解(截图大多来自老师的教学PPT):

代码演示:

运行结果:

3、$refs

$children的缺陷:

$refs的使用:

图解:

4、$parent

概述:

图解:

十、非父子组件通信

1、概述


一、组件化的实现和使用步骤

1、什么是组件化

人的大脑处理问题的能力是有限的,当人们面对复杂问题的时候,很难一次性解决,但人的大脑也天生具有将问题拆解的能力,化繁为简,逐个解决,这种复杂问题拆解成多个小问题在程序中的表现就是组件化。在程序开发中,如果我们将一个复杂页面的所有逻辑都放在一起,势必导致逻辑混乱,难以阅读,不利于后续的管理和扩展。但如果我们将页面拆解成一个个小的功能块,每个功能块完成自己独立的功能,且这些功能块能够被应用到其他页面(可复用),那么程序的开发就会非常易于管理和扩展。

类似此图:

20210124153016636.png

2、Vue的组件化思想

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI5Njg5MzQz_size_16_color_FFFFFF_t_70

3、组件的基本步骤

步骤:

创建组件构造器——注册组件——使用组件;

图示:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI5Njg5MzQz_size_16_color_FFFFFF_t_70 1

二、组件化的基本使用过程

1、代码演示

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title></title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <!-- 3、使用组件 -->
  10. <my-cpn></my-cpn>
  11. </div>
  12. <script src="../../js/vue.js"></script>
  13. <script>
  14. //1、创建组件构造器对象
  15. const cpnC = Vue.extend({
  16. template:`
  17. <div>
  18. <h2>我是二级标题</h2>
  19. <p>我是P标签</p>
  20. </div>
  21. `
  22. });
  23. //2、注册组件,传入组件名和组件构造器对象
  24. Vue.component('my-cpn',cpnC);
  25. const app = new Vue({
  26. el:'#app',
  27. data:{
  28. }
  29. })
  30. </script>
  31. </body>
  32. </html>

2、运行结果

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI5Njg5MzQz_size_16_color_FFFFFF_t_70 2

三、全局组件和局部组件

1、全局组件

在上面《组件化的基本使用过程》中我们所使用的就是全局组件,全局组件意味着可以在多个vue的实例下使用

代码演示:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title></title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <!-- 3、使用组件 -->
  10. <my-cpn></my-cpn>
  11. </div>
  12. <div id="app2">
  13. <my-cpn></my-cpn>
  14. </div>
  15. <script src="../../js/vue.js"></script>
  16. <script>
  17. //1、创建组件构造器对象
  18. const cpnC = Vue.extend({
  19. template:`
  20. <div>
  21. <h2>我是二级标题</h2>
  22. <p>我是P标签</p>
  23. </div>
  24. `
  25. });
  26. //2、注册组件,传入组件名和组件构造器对象
  27. Vue.component('my-cpn',cpnC);
  28. const app = new Vue({
  29. el:'#app',
  30. data:{
  31. }
  32. })
  33. const app2 = new Vue({
  34. el:'#app2',
  35. data:{
  36. }
  37. })
  38. </script>
  39. </body>
  40. </html>

运行结果:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI5Njg5MzQz_size_16_color_FFFFFF_t_70 3

2、局部组件

那么怎么样才能注册局部组件呢?

答案是:在特定的vue实例下注册

代码演示:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title></title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <!-- 3、使用组件 -->
  10. <my-cpn></my-cpn>
  11. </div>
  12. <div id="app2">
  13. <my-cpn></my-cpn>
  14. </div>
  15. <script src="../../js/vue.js"></script>
  16. <script>
  17. //1、创建组件构造器对象
  18. const cpnC = Vue.extend({
  19. template:`
  20. <div>
  21. <h2>我是二级标题</h2>
  22. <p>我是P标签</p>
  23. </div>
  24. `
  25. });
  26. //2、注册组件,传入组件名和组件构造器对象
  27. // Vue.component('my-cpn',cpnC);
  28. const app = new Vue({
  29. el:'#app',
  30. data:{
  31. },
  32. components:{
  33. // 注意这里的my-cpn要用引号引起
  34. 'my-cpn': cpnC
  35. }
  36. })
  37. const app2 = new Vue({
  38. el:'#app2',
  39. data:{
  40. }
  41. })
  42. </script>
  43. </body>
  44. </html>

运行结果(只能用在特定实例之内了):

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI5Njg5MzQz_size_16_color_FFFFFF_t_70 4

四、父组件和子组件

1、简单使用

代码演示:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title></title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <!-- 5、使用 -->
  10. <cpn2></cpn2>
  11. </div>
  12. <script src="../../js/vue.js"></script>
  13. <script>
  14. // 1、创建第一个组件的内容构造器
  15. const cpnC1 = Vue.extend({
  16. template:`
  17. <div>
  18. <h1>我是标题1</h1>
  19. <p>我是内容1</p>
  20. </div>
  21. `
  22. })
  23. // 2、创建第二个组件的内容构造器
  24. const cpnC2 = Vue.extend({
  25. template:`
  26. <div>
  27. <h1>我是标题2</h1>
  28. <p>我是内容2</p>
  29. <!-- 注意:这里的cpn1必须放在div之内 -->
  30. <cpn1></cpn1>
  31. </div>
  32. `,
  33. // 3、在组件的内容构造器中注册组件
  34. components:{
  35. 'cpn1': cpnC1
  36. }
  37. })
  38. const app = new Vue({
  39. el:'#app',
  40. data:{
  41. },
  42. // 4、局部注册
  43. components:{
  44. 'cpn2': cpnC2
  45. }
  46. })
  47. </script>
  48. </body>
  49. </html>

运行结果:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI5Njg5MzQz_size_16_color_FFFFFF_t_70 5

五、注册组件语法糖

1、概述

上面我们演示的基本使用注册组件的方式,在新版本的vue中已经不常见了,现在都使用语法糖进行注册;

2、演示

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title></title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <!-- 3、使用组件 -->
  10. <my-cpn></my-cpn>
  11. </div>
  12. <script src="../../js/vue.js"></script>
  13. <script>
  14. // 之前的写法
  15. //1、创建组件构造器对象
  16. // const cpnC = Vue.extend({
  17. // template:`
  18. // <div>
  19. // <h2>我是二级标题</h2>
  20. // <p>我是P标签</p>
  21. // </div>
  22. // `
  23. // });
  24. //2、注册组件,传入组件名和组件构造器对象
  25. // Vue.component('my-cpn',cpnC);
  26. // 语法糖写法:等于是二者的合并
  27. // 这是全局注册的写法
  28. Vue.component('my-cpn',{
  29. template:`
  30. <div>
  31. <h2>我是二级标题</h2>
  32. <p>我是P标签</p>
  33. </div>
  34. `
  35. })
  36. const app = new Vue({
  37. el:'#app',
  38. data:{
  39. },
  40. components:{
  41. // 这是局部组件的语法糖写法
  42. 'my-cpn': {
  43. template:`
  44. <div>
  45. <h2>我是二级标题</h2>
  46. <p>我是P标签</p>
  47. </div>
  48. `
  49. }
  50. }
  51. })
  52. </script>
  53. </body>
  54. </html>

六、组件模板的分离写法

1、概述

我们之前在注册组件的时候,将组件的模板html代码写在vue实例中,如果html代码比较复杂的话会因为文件内容过多而导致混乱;

2、两种写法

代码演示:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title></title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <my-cpn></my-cpn>
  10. </div>
  11. <!-- 分离写法方式一 -->
  12. <!-- <script type="text/x-temolate" id="myCpn">
  13. <div>
  14. <h2>我是二级标题</h2>
  15. <p>我是P标签</p>
  16. </div>
  17. </script> -->
  18. <!-- 分离写法方式二 -->
  19. <template id="myCpn">
  20. <div>
  21. <h2>我是二级标题</h2>
  22. <p>我是P标签</p>
  23. </div>
  24. </template>
  25. <script src="../../js/vue.js"></script>
  26. <script>
  27. Vue.component('my-cpn',{
  28. template: '#myCpn'
  29. });
  30. const app = new Vue({
  31. el:'#app',
  32. data:{
  33. }
  34. })
  35. </script>
  36. </body>
  37. </html>

运行结果:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI5Njg5MzQz_size_16_color_FFFFFF_t_70 6

七、组件数据的存放

1、概述

组件对象也有一个data属性,只是这个data属性必须是一个函数,而且这个函数返回一个对象,对象内部保存着数据;

之所以data属性必须是一个函数,是因为当返回的是同一个对象的时,组件多次使用的话会相互影响;

2、代码演示

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title></title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. {
  10. {message}}
  11. <my-cpn></my-cpn>
  12. </div>
  13. <template id="myCpn">
  14. <div>
  15. <h1>{
  16. {title}}</h1>
  17. <p>{
  18. {content}}</p>
  19. </div>
  20. </template>
  21. <script src="../../js/vue.js"></script>
  22. <script>
  23. const app = new Vue({
  24. el: '#app',
  25. data: {
  26. message: '你好!'
  27. },
  28. components:{
  29. 'my-cpn': {
  30. template: '#myCpn',
  31. data(){
  32. return{
  33. title: "我是标题",
  34. content: "我是p标签"
  35. }
  36. }
  37. }
  38. }
  39. })
  40. </script>
  41. </body>
  42. </html>

3、运行结果

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI5Njg5MzQz_size_16_color_FFFFFF_t_70 7

八、父子组件的通信

1、概述

在开发中,往往一些数据确实需要从上层传递到下层:比如在一个页面中,我们从服务器请求到了很多的数据。其中一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示。这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件)将数据传递给小组件(子组件);

如何进行父子组件间的通信呢?Vue官方提到通过props向子组件传递数据,通过事件向父组件发送消息;

真实的开发中,Vue实例和子组件的通信和父组件和子组件的通信过程是一样的;

2、props基本用法

概述:

在组件中,使用选项props来声明需要从父级接收到的数据;

props的值有两种方式:

方式一:字符串数组,数组中的字符串就是传递时的名称;

方式二:对象,对象可以设置传递时的类型,也可以设置默认值等;

图解:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI5Njg5MzQz_size_16_color_FFFFFF_t_70 8

代码演示:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title></title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. {
  10. {message}}
  11. <my-cpn :title="title" :content="content"></my-cpn>
  12. </div>
  13. <template id="myCpn">
  14. <div>
  15. <h1>{
  16. {title}}</h1>
  17. <p>{
  18. {content}}</p>
  19. </div>
  20. </template>
  21. <script src="../../js/vue.js"></script>
  22. <script>
  23. const app = new Vue({
  24. el: '#app',
  25. data: {
  26. message: '你好!',
  27. title: "我是标题啊",
  28. content: "我是内容啊"
  29. },
  30. components:{
  31. 'my-cpn': {
  32. template: '#myCpn',
  33. props:['title','content']
  34. }
  35. }
  36. })
  37. </script>
  38. </body>
  39. </html>

运行结果:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI5Njg5MzQz_size_16_color_FFFFFF_t_70 9

3、props数据验证

概述:

在前面,我们的props选项是使用一个数组;

我们说过,除了数组之外,我们也可以使用对象,当需要对props进行类型等验证时,就需要对象写法了。

验证都支持哪些数据类型呢?

String、Number、Boolean、Array、Object、Date、Function、Symbol;

当我们有自定义构造函数时,验证也支持自定义的类型;

自定义类型:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI5Njg5MzQz_size_16_color_FFFFFF_t_70 10

演示:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI5Njg5MzQz_size_16_color_FFFFFF_t_70 11

4、子级向父级传递

概述:

props用于父组件向子组件传递数据,还有一种比较常见的是子组件传递数据或事件到父组件中;

我们应该如何处理呢?这个时候,我们需要使用自定义事件来完成;

什么时候需要自定义事件呢?

当子组件需要向父组件传递数据时,就要用到自定义事件了;

我们之前学习的v-on不仅仅可以用于监听DOM事件,也可以用于组件间的自定义事件;

自定义事件的流程:

在子组件中,通过$emit()来触发事件;

在父组件中,通过v-on来监听子组件事件;

图解:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI5Njg5MzQz_size_16_color_FFFFFF_t_70 12

自定义事件代码演示:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title></title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <!-- 注意:这里的方法不可带括号 -->
  10. <child-cpn @increment='changeTotal' @decrement='changeTotal'></child-cpn>
  11. 当前值:{
  12. {total}}
  13. </div>
  14. <template id="myCpn">
  15. <div>
  16. <button @click="increment">+1</button>
  17. <button @click="decrement">-1</button>
  18. </div>
  19. </template>
  20. <script src="../../js/vue.js"></script>
  21. <script>
  22. const app = new Vue({
  23. el: '#app',
  24. data:{
  25. total: 0
  26. },
  27. methods:{
  28. changeTotal(counter){
  29. this.total = counter;
  30. }
  31. },
  32. components:{
  33. 'child-cpn':{
  34. template: '#myCpn',
  35. data(){
  36. return{
  37. counter: 0
  38. }
  39. },
  40. methods:{
  41. increment(){
  42. this.counter++;
  43. this.$emit('increment',this.counter);
  44. },
  45. decrement(){
  46. this.counter--;
  47. this.$emit('decrement',this.counter);
  48. }
  49. }
  50. }
  51. }
  52. })
  53. </script>
  54. </body>
  55. </html>

运行结果:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI5Njg5MzQz_size_16_color_FFFFFF_t_70 13

九、父子组件的访问方式

1、概述

有时候我们需要父组件直接访问子组件,子组件直接访问父组件,或者是子组件访问跟组件;

父组件访问子组件:使用$children或$refs;

子组件访问父组件:使用$parent;

2、$children

概述:

我们先来看下$children的访问:

this.$children是一个数组类型,它包含所有子组件对象;

我们这里通过一个遍历,取出所有子组件的message状态,不常用!

图解(截图大多来自老师的教学PPT):

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI5Njg5MzQz_size_16_color_FFFFFF_t_70 14

代码演示:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title></title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <parent-cpn></parent-cpn>
  10. </div>
  11. <template id="p1">
  12. <div>
  13. <child-cpn1></child-cpn1>
  14. <child-cpn2></child-cpn2>
  15. <button @click="showChildCpnMessage">显示所有子组件信息</button>
  16. </div>
  17. </template>
  18. <template id="c1">
  19. <h1>我是子组件1</h1>
  20. </template>
  21. <template id="c2">
  22. <h1>我是子组件2</h1>
  23. </template>
  24. <script src="../../js/vue.js"></script>
  25. <script>
  26. const app = new Vue({
  27. el: "#app",
  28. components:{
  29. 'parent-cpn':{
  30. template: '#p1',
  31. components:{
  32. 'child-cpn1': {
  33. template: '#c1'
  34. },
  35. 'child-cpn2': {
  36. template: '#c2'
  37. }
  38. },
  39. methods:{
  40. showChildCpnMessage(){
  41. for(let i=0;i<this.$children.length;i++){
  42. // 通过下标拿取
  43. console.log(this.$children[i]);
  44. }
  45. }
  46. }
  47. },
  48. }
  49. })
  50. </script>
  51. </body>
  52. </html>

运行结果:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI5Njg5MzQz_size_16_color_FFFFFF_t_70 15

3、$refs

$children的缺陷:

通过$children访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值;

但是当子组件过多,我们需要拿到其中一个时,往往不能确定它的索引值,甚至还可能会发生变化;

有时候,我们想明确获取其中一个特定的组件,这个时候就可以使用$refs;

$refs的使用:

$refs和ref指令通常是一起使用的:

首先,我们通过ref给某一个子组件绑定一个特定的ID;

其次,通过this.$refs.ID就可以访问到该组件了;

图解:

2021012415441778.png

4、$parent

概述:

如果我们想在子组件中直接访问父组件,可以通过$parent;

注意事项:

尽管在Vue开发中,我们允许通过$parent来访问父组件,但是在真实开发中尽量不要这样做;

子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了;

如果我们将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题;

另外,更不要做的是通过$parent直接修改父组件的状态,那么父组件中的状态将变得飘忽不定,很不利于我的调试和维护;

图解:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI5Njg5MzQz_size_16_color_FFFFFF_t_70 16

十、非父子组件通信

1、概述

刚才我们讨论的都是父子组件间的通信,那如果是非父子关系呢?

非父子组件关系包括多个层级的组件,也包括兄弟组件的关系;

在Vue1.x的时候,可以通过$dispatch和$broadcast完成:

$dispatch用于向上级派发事件;

$broadcast用于向下级广播事件;

但是在Vue2.x都被取消了:

在Vue2.x中,有一种方案是通过中央事件总线,也就是一个中介来完成;

但是这种方案和直接使用Vuex的状态管理方案还是逊色很多;

并且Vuex提供了更多好用的功能,所以这里我们暂且不讨论这种方案,后续我们专门学习Vuex的状态管理;

发表评论

表情:
评论列表 (有 0 条评论,115人围观)

还没有评论,来说两句吧...

相关阅读