vue组件

一、组件的定义

组件(Component)是Vue.js最强大的功能之一,组件是一个自定义元素或一个模块,包括所需的模板(HTML)、样式(CSS)、逻辑(JavaScript)

组件分为全局组件(component)、局部组件(components)

二、组件的注册及使用

注意:

  • 构造Vue实例时传入的各种选项大多数都可以在组件里使用,只有一个例外:data,必须是函数,同时这个函数要求返回一个对象

1 data: function(){
2   return {
3     msg: 'Hello World'
4   }
5 }
  • 组件模板template
    • 可以使用模板字符串
    • 必须是单个根元素
复制代码
 1 <!-- 单个根元素 -->
 2 <div>
 3   <ul>
 4     <li></li>
 5   </ul>
 6   <ul>
 7     <li></li>
 8   </ul>
 9 </div>
10 
11 <!-- 不符合单个根元素的情况 -->
12 <p></p>
13 <p></p>
复制代码
  • 组件命名方式

    • 短横线方式(推荐)

      • my-component

    • 大驼峰方式

      • MyComponent

      • 只能在其他组件模板字符串中使用,不能在HTML模板中直接使用

      • 如果需要在HTML模板中使用,需要将其进行特定的转化

        • 首字母从大写转为小写

        • 后续每遇到大写字母都要转化为小写字母并且在转化后的小写字母前加”-“,例:MyZuJian ——> my-zu-jian

三、全局组件

1 // 声明全局组件
2 Vue.component('组件名称',{
3   data: '组件数据', // data是一个function
4   template: '组件模板内容'
5 })
    • component(参数一,参数二)

      • 参数一:组件名(可以看作是标签的名称)

      • 参数二:一个对象形式的选项,里面存放组件的声明信息

    • 全局组件注册后,任何Vue实例都可以使用

例:

复制代码
1 // 声明一个全局的HelloWorld组件
2 Vue.component('HelloWorld', {
3   data: function(){
4     return {
5       msg: 'Hello World'
6     }
7   },
8   template: '<div>{{msg}}</div>'
9 });
复制代码

四、局部组件

局部组件定义后只能在当前注册它的Vue实例中使用,其是通过某个Vue实例或者组件的实例选项components注册

例:

复制代码
1 var Child = {
2   template: '<div>A custom component!</div>'
3 }
4 new Vue({
5   components: {
6     // <my-component> 将只在父组件模板中可用
7     'my-component': Child
8   }
9 })
复制代码

五、组件分离写法

抽离模板部分

1、script标签

复制代码
 1 <div id="app">
 2   <cpn></cpn>
 3   <cpn></cpn>
 4   <cpn></cpn>
 5 </div>
 6 
 7 <!-- script标签,类型必须是text/x-template -->
 8 <script type="text/x-template" id="cpn">
 9     <div>
10         <h2>我是标题</h2>
11         <p>我是内容</p>
12   </div>
13 </script>
14 
15 <script src="./js/vue.js"></script>
16 <script type="text/javascript">
17   Vue.component('cpn',{
18     template: '#cpn',
19     // data: 组件内数据参考第二节“组件的注册及使用”
20   })
21   const app = new Vue({
22     el: '#app',
23     data: {
24       message: "hello!",
25     }
26   });
27 </script>
复制代码

2、template标签

复制代码
 1 <div id="app">
 2   <cpn></cpn>
 3   <cpn></cpn>
 4   <cpn></cpn>
 5 </div>
 6 
 7 <!-- template标签 -->
 8 <template id="cpn">
 9   <div>
10     <h2>我是标题</h2>
11     <p>我是内容</p>
12   </div>
13 </template>
14 
15 <script src="./js/vue.js"></script>
16 <script type="text/javascript">
17   Vue.component('cpn',{
18     template: '#cpn',
19     // data: 组件内数据参考第二节“组件的注册及使用”
20   })
21   const app = new Vue({
22     el: '#app',
23     data: {
24       message: "hello!",
25     }
26   });
27 </script>
复制代码
复制代码
 1 <div id="app">
 2   <cpn></cpn>
 3 </div>
 4 
 5 <template id="cpn">
 6   <div>
 7     <h2>{{title}}</h2>
 8     <p>我是内容</p>
 9   </div>
10 </template>
11 
12 <script src="./js/vue.js"></script>
13 <script type="text/javascript">
14   Vue.component('cpn',{
15     template: '#cpn',
16     data(){
17       return {
18         title: '我是组件的标题'
19       }
20     }
21   })
22   const app = new Vue({
23     el: '#app',
24     data: {
25       message: "hello!",
26     }
27   });
28 </script>
复制代码

六、组件通信(重点/难点)

实际工作中使用组件有需要互相之间传递数据的需求,此时就得考虑如何进行组件间传值的问题

1、父➡子传值

  • 父组件以属性的形式绑定到子组件身上

  • 子组件通过使用属性props接收

    • props是单向绑定的(只读属性):当父组件的属性变化时,将传导给子组件,但反过来不会

    • props属性支持两种常见的写法形式

      • 数组(推荐)

        • 优点:书写简单

        • 缺点:不能设置默认值,数据类型

      • 对象

        • 优点:可以设置数据默认值、数据类型

        • 缺点:写法复杂

    • props类型验证支持的数据类型

      • String

      • Number

      • Boolean

      • Array

      • Object

      • Data

      • Function

      • Symbol

复制代码
 1 <div id="app">
 2     <!-- 如果day属性在props里是驼峰,在这里应写为“-”连接的形式,例props里day为cInfo的话应写为:c-info='dayNum' -->
 3     <child :day='dayNum'></child>
 4 </div>
 5 
 6 <script src="./js/vue.js"></script>
 7 <script type="text/javascript">
 8   var child = {
 9     // props形式一:数组形式
10     props: ['day'],
11     // props形式二:对象形式
12     props: {
13       day: {
14         type: String,
15         // 如果没有在子组件(child)中绑定day属性,会显示默认值
16         default: '',
17         // 如果有required并且为true,则该项day是必传的
18         required: true
19       }
20     },
21     template: '<p>星期{{dayNum}}</p>'
22   }
23   const vm = new Vue({
24     el: '#app',
25     data: {
26       dayNum: ''
27     },
28     components: {
29       child
30     }
31   })
32 </script>
复制代码

props里类型是对象或者数组时,默认值必须是一个函数

复制代码
 1 <div id="app">
 2     <child :day='dayNum'></child>
 3 </div>
 4 
 5 <script src="./js/vue.js"></script>
 6 <script type="text/javascript">
 7   var child = {
 8     props: {
 9       day: {
10         type: Array,
11         default(){
12           return ['']
13         }
14       }
15     },
16     template: '<p>星期{{dayNum}}</p>'
17   }
18   const vm = new Vue({
19     el: '#app',
20     data: {
21       dayNum: ''
22     },
23     components: {
24       child
25     }
26   })
27 </script>
复制代码

2、子➡父传值

  • 子组件模板内容中用$emit()定义自定义事件,$emit()方法有两个参数

    • 参数一:自定义事件名称

    • 参数二:需要传递的数据(可选)

  • 父组件模板内容中的子组件占位符上用v-on(或@)绑定子组件定义的自定义事件名,监听子组件的事件,实现通信

复制代码
 1 <div id="app">
 2   <child @anlarge-text='bigger'></child>
 3   <p :style="{fontSize: fontSize + 'px'}">{{msg}}</p>
 4 </div>
 5 
 6 <script src="./js/vue.js"></script>
 7 <script type="text/javascript">
 8   // 子组件
 9   var child = {
10     template: `<button @click="$emit('anlarge-text',2)">点我给父组件字体加2px</button>`,
11   }
12   // 根组件(父)
13   const vm = new Vue({
14     el: '#app',
15     data: {
16       msg: 'Hello Vue',
17       fontSize: 12,
18     },
19     components: {
20       child
21     },
22     methods: {
23       bigger: function (n){
24         this.fontSize += n
25       }
26     }
27   })
28 </script>
复制代码

3、父组件访问(操作)子组件

使用$refs或$children

  • $refs(推荐)

复制代码
 1 <div id="app">
 2   <cpn ref="aaa"></cpn>
 3   <button @click="btnClick">按钮</button>
 4 </div>
 5 
 6 <template id="cpn">
 7   <div>我是子组件</div>
 8 </template>
 9 
10 <script src="./js/vue.js"></script>
11 <script type="text/javascript">
12   const vm = new Vue({
13     el: '#app',
14     data: {
15       message: 'Hello Vue'
16     },
17     methods: {
18       btnClick(){
19         console.log(this.$refs.aaa.name);
20       }
21     },
22     components: {
23       cpn: {
24         template: '#cpn',
25         data(){
26           return {
27             name: '我是子组件的name'
28           }
29         },
30         methods: {
31           showMessage(){
32             console.log('我是子组件的方法')
33           }
34         }
35       },
36     },
37 
38   })
39 </script>
复制代码

$children(不推荐,如果子组件使用多次,有选取下标顺序问题)

复制代码
 1 <div id="app">
 2   <cpn></cpn>
 3   <button @click="btnClick">按钮</button>
 4 </div>
 5 
 6 <template id="cpn">
 7   <div>我是子组件</div>
 8 </template>
 9 
10 <script src="./js/vue.js"></script>
11 <script type="text/javascript">
12   const vm = new Vue({
13     el: '#app',
14     data: {
15       message: 'Hello Vue'
16     },
17     methods: {
18       btnClick(){
19         console.log('this.$children');
20         this.$children[0].showMessage()
21       }
22     },
23     components: {
24       cpn: {
25         template: '#cpn',
26         methods: {
27           showMessage(){
28             console.log('showMessage')
29           }
30         }
31       },
32     },
33 
34   })
35 </script>
复制代码

4、子组件访问父组件

使用$parent(不推荐使用,了解)

复制代码
 1 <div id="app">
 2     <cpn></cpn>
 3 </div>
 4 
 5 <template id="cpn">
 6     <div>我是子组件</div>
 7     <button @click="btnClick">按钮</button>
 8 </template>
 9 
10 <script src="./js/vue.js"></script>
11 <script type="text/javascript">
12     const vm = new Vue({
13         el: '#app',
14         data: {
15             message: 'Hello Vue'
16         },
17         components: {
18             cpn: {
19                 template: '#cpn',
20                 methods: {
21                     btnClick(){
22                         // 访问父组件$parent
23                         console.log(this.$parent.message)
24                     }
25                 }
26             },
27         },
28         
29     })
30 </script>
复制代码

5、EventBus

EventBus又称中央事件总线

在Vue中通过单独的事件中心来管理非父子关系组件(兄弟)间的通信:

核心步骤

  • 建立事件中心

const eventBus = new Vue()
  • 传递数据
eventBus.$emit('自定义事件名', 传递的数据)
  • 接收数据
eventBus.$on('自定义事件名', [callback])
  • 销毁事件中心
eventBus.$off('自定义事件名')

案例:互相伤害

复制代码
 1 <div id="app">
 2     <person_one></person_one>
 3     <hr/>
 4     <zj_two></zj_two>
 5     <hr/>
 6     <button @click='destoryBus'>炸掉事件中心</button>
 7 </div>
 8 
 9 <template id="one">
10     <div>{{hp}}</div>
11     <div>
12         <button @click='fn1'>点我让对方受到1点伤害</button>
13     </div>
14 </template>
15 
16 <template id="two">
17     <div>{{data}}</div>
18     <div>
19         <button @click='fn2'>点我让对方受到2点伤害</button>
20     </div>
21 </template>
22 
23 <script src="./js/vue.js"></script>
24 <script type="text/javascript">
25     // 定义事件中心
26     const eventBus = new Vue()
27 
28     Vue.component('person_one',{
29         data(){
30             return {
31                 hp: 100
32             }
33         },
34         template: `#one`,
35         methods:{
36             fn1(){
37                 eventBus.$emit('attack_two',1)
38             }
39         },
40         mounted(){
41             eventBus.$on('attack_one',val => this.hp -= val)
42         }
43     })
44     Vue.component('person_two',{
45         data(){
46             return {
47                 hp: 100
48             }
49         },
50         template: `#two`,
51         methods:{
52             fn2(){
53                 eventBus.$emit('attack_one',2)
54             }
55         },
56         mounted(){
57             eventBus.$on('attack_two',val => this.hp -= val)
58         }
59     })
60     new Vue({
61         el: '#app',
62         methods: {
63             destoryBus(){
64                 // 销毁双方监听的事件
65                 eventBus.$off('attack_one')
66                 eventBus.$off('attack_two')
67             }
68         }
69     })
70 </script>
复制代码

6、动态组件

通过使用保留的<component>元素,动态的绑定到它们的is属性,我们让多个组件可以使用同一个挂载点,并动态的切换

复制代码
 1 <div id="app">
 2     <keep-alive>
 3         <component :is="currentView"></component>
 4     </keep-alive>
 5 </div>
 6 
 7 <script src="./js/vue.js"></script>
 8 <script>
 9     // 多个组件
10     var home = {}
11     var posts = {}
12 
13     var vm = new Vue({
14         el: "#app",
15         data: {
16             currentView: "home",
17         },
18         components: {
19             home,
20             posts,
21         },
22     });
23 </script>
复制代码

keep-alive的作用:

  • 可以将已经切换出去的非活跃组件保留在内存中,可以保留切换出去的组件的状态,避免重新渲染

案例:使用动态组件实现tab切换效果

复制代码
 1 <div id="app">
 2     <button @click='change("step1")'>第一步</button>
 3     <button @click='change("step2")'>第二步</button>
 4     <button @click='change("step3")'>第三步</button>
 5     <keep-alive>
 6         <component :is="name"></component>
 7     </keep-alive>
 8 </div>
 9 
10 <script src="./js/vue.js"></script>
11 <script>
12     var step1 = {template: '<div>这是第一步的操作</div>'}
13     var step2 = {template: '<div>这是第二步的操作</div>'}
14     var step3 = {template: '<div>这是第三步的操作</div>'}
15 
16     var vm = new Vue({
17         el: "#app",
18         data: {
19             name: "step2",
20         },
21         components: {
22             step1,
23             step2,
24             step3
25         },
26         methods: {
27             change:function(name){
28                 this.name = name
29             }
30         }
31     })
32 </script>
复制代码

七、组件插槽

插槽的作用:父组件向子组件传递内容

插槽的优点:能大大的提高组件的重用能力

通俗的讲,插槽无非是在子组件中挖个坑,坑里放什么东西由父组件决定

插槽的类型:匿名(单个)插槽、具名插槽、作用域插槽

1、匿名插槽

复制代码
 1 <div id="app">
 2     <!-- 插槽内容 -->
 3     <alert-box>Something bad happened.</alert-box>
 4 </div>
 5 
 6 <template id="cpn">
 7     <div style="color: red">
 8         <strong>Error:</strong>
 9         <slot>我是默认值</slot>
10     </div>
11 </template>
12 
13 <script src="./js/vue.js"></script>
14 <script type="text/javascript">
15     Vue.component("alert-box", {
16         template: `#cpn`
17     });
18 
19     const vm = new Vue({
20         el: "#app",
21     });
22 </script>
复制代码

注意:子组件的slot标签中允许书写内容,当父组件不往子组件传递内容时,slot中的内容才会被显示出来

2、具名插槽

slot元素可以用name属性来进一步配置如何分发内容,多个插槽可以有多个不同的名字,具名插槽的内容将根据内容片段(父组件)中的slot进行匹配替换

复制代码
 1 <div id="app">
 2   <cpn slot="center">
 3     <span>我是修改插槽默认值的内容</span>
 4   </cpn>
 5 </div>
 6 
 7 <template id="cpn">
 8   <div>
 9     <slot name="left"><span>左边</span></slot>
10     <slot name="center"><span>中间</span></slot>
11     <slot name="right"><span>右边</span></slot>
12   </div>
13 </template>
14 
15 <script src="./js/vue.js"></script>
16 <script type="text/javascript">
17   const vm = new Vue({
18     el: "#app",
19     components: {
20       cpn: {
21         template: '#cpn'
22       }
23     },
24   });
25 </script>
复制代码

3、作用域插槽

应用场景:父组件对子组件的内容进行加工

复制代码
 1 <div id="app">
 2   <cpn>
 3     <!-- 使用slot-scope拿到子组件数据,等号右边的slot是自定义的名字 -->
 4     <!-- vue2.5版本之前使用template,之后可以使用其他标签,如div -->
 5     <template slot-scope="slot">
 6       <!-- 这里的data与子组件中自定义的data命名相同 -->
 7       <span>{{slot.data.join('--')}}</span>
 8     </template>
 9   </cpn>
10 </div>
11 
12 <template id="cpn">
13   <div>
14     <!-- 这里的data属性名不固定,可以自定义 -->
15     <slot :data="languages">
16       <ul>
17         <li v-for="item in languages">{{item}}</li>
18       </ul>
19     </slot>
20   </div>
21 </template>
22 
23 <script src="./js/vue.js"></script>
24 <script type="text/javascript">
25   const vm = new Vue({
26     el: "#app",
27     data: {
28       message: 'Hello World'
29     },
30     components: {
31       cpn: {
32         template: '#cpn',
33         data(){
34           return {
35             languages: ['JavaScript', 'Java', 'C++', 'Python', 'Go']
36           }
37         }
38       }
39     },
40   });
41 </script>
复制代码

 

 

posted @   大米饭盖饭  阅读(216)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示