vue组件
一、组件的定义
组件分为全局组件(component)、局部组件(components)
二、组件的注册及使用
注意:
-
构造Vue实例时传入的各种选项大多数都可以在组件里使用,只有一个例外:data,必须是函数,同时这个函数要求返回一个对象
1 data: function(){ 2 return { 3 msg: 'Hello World' 4 } 5 }
- 可以使用模板字符串
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模板中使用,需要将其进行特定的转化
-
首字母从大写转为小写
-
-
-
-
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>
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>
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>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具