VUE 组件

一、什么是组件:

  • 在 Vue 里,一个组件本质上是一个拥有预定义选项的一个 Vue 实例

  • 在Vue中,组件是可复用的 Vue 实例,且带有一个名字。

  • 组件是构成页面中独立结构单元,组件主要以页面结构形式存在,不同组件也具有基本交互功能。

  • 如下图所示,每个图书表项就是一个Vue组件:
    在这里插入图片描述

二、组件树概念:

通常一个页面会以一棵嵌套的组件树的形式来组织。这个思想很重要。
在这里插入图片描述
例如:上图左侧是一个页面,右侧是对应的组件树。当前的页面是一个Vue实例(上图组件树的第一层)。页面中有三个组件:页头、侧边栏、内容区(上图组件树的第二层),这三个组件构成了当前页面。其中侧边栏又由两个小侧边栏组成,内容区由三个小内容框组成(上图组件树第三层)。

三、组件的创建方式:

使用组件前首先需要创建组件。

备注:这里我们留个印象,下面的创建方式都是以局部组件为例,我主要想讲的是使用template模板和不使用template模板的区别。(你要是觉得乱就忽略这句话,等到看完所有内容再回来看这句话)

1.直接创建组件:

<script>
	//创建组件com1
	var com1 = {
		//template中是组件内容,可以是多个html标签潜逃而成,但是根标签只能有一个
	    template: "
	    			<div>
						<p>我是组件com1</p>
						<p>哈哈哈</p>
					</div>
	    		  "
	}
</script>

效果大概就是这样的:
在这里插入图片描述

可以看出这种创建组件的方式非常乱,所以vue提供了下面这种方式。

2.使用template模板创建组件:

Vue提供了template模板来定义组件内容,可以在该标签中书写HTML代码,然后通过id值绑定到组件内的template属性上,这样就有利于在编辑器中显示代码提示和高亮显示,不仅改善了开发体验,也提高了开发效率。

<script>
	//创建组件com1
	var com1 = {
		//使用id与template模板绑定
	    template: '#com1template'
	}
</script>
<html>
	<!--模板必须有id-->
	<template id="com1template">
		<!--根元素必须唯一-->
		<div>
			<p>我是组件com1</p>
			<p>哈哈哈</p>
		</div>
	</template>
</html>

四、组件的注册方式及使用:

为了能使用这些组件,组件必须先注册以便 Vue 能够识别。

1.局部注册:

通过Vue实例的components属性来实现局部注册,注册后只能在该Vue实例中使用,其他Vue实例想使用该组件必须也使用components属性来注册该组件。

<script>
	//创建组件com1
	var com1 = {
	    template: '<p>我是组件</p>'
	}
	
	//创建Vue实例vm1
	var vm1 = new Vue({
	    el: '#app1',
	    // 通过components属性在vm1实例中注册局部组件com1,并起名myCompent,这样组件com1就可以在vm1实例中使用了。
	    components: { my-component: com1 }
	})
	//创建Vue实例vm2
	var vm2 = new Vue({
	    el: '#app2'
	})
</script>

<html>
	<!--组件在vm1实例下可以使用-->
	<div id="app1">
		<!--使用组件com1,注意使用的是注册的名字my-component,而不是对象的名字com1-->
	    <my-component></my-component><!--正确-->
	</div>
	
	<!--组件在其他实例下(例如vm2)不可以使用,因为没有注册,必须先注册-->
	<div id="app2">
	    <my-component></my-component><!--报错-->
	</div>
</html>

2.全局注册:

Vue.component()方法用于全局注册组件,全局注册的组件可以用在其被注册之后的任何 (通过 new Vue) 新创建的 Vue 根实例,也包括其组件树中的所有子组件的模板中

<script>
	//创建组件com1并全局注册
	Vue.component(
		//这就是组件的名字,即<com1></com1>
		'com1', 
		{
			template: '<p>我是组件</p>'
		}
	)

	//创建Vue实例vm1
	var vm1 = new Vue({
	    el: '#app1'
	})
	//创建Vue实例vm2
	var vm2 = new Vue({
	    el: '#app2'
	})
</script>


<html>
	<div id="app1">
		<!--使用组件com1-->
	    <com1></com1><!--正确-->
	</div>
	
	<div id="app2">
	    <com1></com1><!--正确-->
	</div>
</html>

备注:看完全局注册你可能会有疑问:我没有见过这种组件创建的方式呀?上面讲的组件创建方式和这个不一样呀?这时候你再回去看(三)中的备注可能就会理解了.

五、组件的属性:

上面我们学到了组件的创建、注册与使用方法,学到了使用template属性,接下来我们再看一下组件内部还有什么属性。

  • 组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。多了template属性,但是没有el属性。
//这里以创建组件并全局注册为例
Vue.component('my-component', {
	template:'',
	//与Vue实例不同的是,在组件中data必须是一个函数
    data: function () {
        return {
            shuxing: ''
        }
    },
    methods: {
        // ... ...
    },
    computed: {
        // ... ...
    },
    created: function () {
        // ... ...
    },
    props:[]
})

六、组件的命名:

组件有两种命名方式:短横线分隔命名、驼峰命名。
注意:组件命名是在注册的时候命名,使用组件的时候也是使用注册的名字,而不是创建的组件对象的名字。

<script>
//创建组件并全局注册例子:
Vue.component('my-component-name', { /* ... */ })
Vue.component('MyComponentName', { /* ... */ })

//创建组件并局部注册例子:
var com1 = {
    /*...*/
}
//创建Vue实例vm1
var vm1 = new Vue({
    el: '#app1',
    //注册组件并给组件命名:
    components: { my-component: com1 }  
    components: { myComponent: com1 }  
})
</script>

不管使用哪种方式注册,使用时都是使用驼峰命名:my-component

<html>
	<my-component></my-component>
</html>

七、组件组件之间数据传递:

在这里插入图片描述

1.父组件向子组件传值 - Props:

props用来接收父组件中定义的数据,其值为数组。

(1)静态传值:
<html>
<!--父组件-->
<div class="container" id="app">
	<!--子组件-->
    <blog-post 
         title="博文1" 
         content="西游记"><!--这就是父组件向子组件传的值-->
    </blog-post>
	<!--子组件-->
    <div id="blog-post-demo">
        <blog-post 
          	title="博文2" 
          	content="水浒传"><!--这就是父组件向子组件传的值-->
        </blog-post>
    </div>
</div>
<!--template模板-->
<template id="blog-post-template">
    <div>
        <p>{{title}}</p>
        <p>{{content}}</p>
    </div>        
</template>
</html>

<script>
//创建组件并全局注册
Vue.component('blog-post', {
	//接收父组件传来的数据
  	props: ['title', 'content'],
  	template: '#blog-post-template’
})


let app = new Vue({
  el: '#app'
})
</script>
(2)动态传值:

使用vue指令将父组件data内的属性值传给子组件。

<html>
<!--父组件-->
<div class="container" id="app">
	<!--子组件-->
    <blog-post 
        v-for="post in posts"
		v-bind:title="post.title"
        v-bind:content="post.content"<!--这就是父组件向子组件传的值-->
    </blog-post>
</div>
<!--template模板-->
<template id="blog-post-template">
    <div>
        <p>{{title}}</p>
        <p>{{content}}</p>
    </div>        
</template>
</html>

<script>
//创建组件并全局注册
Vue.component('blog-post', {
	//接收父组件传来的数据
  	props: ['title', 'content'],
  	template: '#blog-post-template’
})


let app = new Vue({
  el: '#app',
    data: {
    posts: [
      {title: '博文1', content: '西游记'},
      {title: '博文2', content: '水浒传'}
    ]
  }
})
</script>

效果:
在这里插入图片描述

2.子组件向父组件传值 - $emit:

$emit(“b”)表示向父组件抛出事件b。

<html>
<!--父组件-->
<div class="container" id="app">
	<!--子组件-->
    <div>
        <blog-post 
        	@b="a()"
        	><!--2.自定义事件b,表示父组件接收到子组件抛出的事件b后会执行a()函数-->
        </blog-post>
    </div>
</div>
<!--template模板-->
<template id="blog-post-template">
    <div>
    	<button @click="$emit('b')"></button><!--1.点击子组件的按钮会向父组件抛出事件b-->
    </div>        
</template>
</html>

<script>
//创建组件并全局注册
Vue.component('blog-post', {
	//接收父组件传来的数据
  	props: ['title', 'content'],
  	template: '#blog-post-template’
})


let app = new Vue({
  el: '#app',
  methods:{
	   a(){
	        alert("你点击了子组件");
	   }
  }
})
</script>

效果:点击子组件的按钮会执行父组件的a()函数。

八、案例:实现图书列表界面:

在这里插入图片描述

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" >
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
    <title>Document</title>
    <style type="text/css" rel="stylesheet">
        .text-secondary {
            line-height: 1em;
            font-size: 0.8em;
            border-bottom: 1em;
        }
        #table_book{
            font: Arial, 微软雅黑;
        }
        a:hover {
            text-decoration: underline solid rgb(23, 82, 192);
        }
        a:link {
            text-decoration: none;
        }
        .borderprimary{
            border:solid 1px blue !important;
            /* 这里要加一个 !important表示最大优先级,该样式会覆盖原先的样式*/
        }
    </style>
</head>
<body>
    <!-- container居中 -->
    <div id="table_book" class="container">
        <div class="row">
            <one-book  
                v-for="(book,index) in books"
                :book="book"
                @delete="deleteBook(index)"
                ></one-book><!--监听delete事件,监听到时执行deleteBook方法--->
        </div>
    </div>
    <template id="book_temp">
        <div class="col-md-4 g-2 gy-md-3" >
            <div class="border" v-bind:class={borderprimary:!deleteCanHide}>
                <div class="row" @mouseenter="showDelete" @mouseleave="hideDelete">
                    <img class="col-4" v-bind:src="book.img_url" width="100%" height="100%">
                    <div class="col-8">
                        <p class="fs-5"><a href="book.jd_link" class="fs-5">{{book.title}}</a></p>
                        <p class="text-secondary">{{book.author}}</p>
                        <p class="text-secondary">出版社:{{book.publisher}}</p>
                        <p class="text-secondary">出版日期:{{book.publish_date}}</p>
                        <p class="text-secondary" style="color:red; width:10;">定价:¥{{book.price}}</p>
                        <div align="right">
                            <!-- invisible样式类是bootstrap自带的样式类,控制的是标签的invisible属性:invisible:hidden -->
                            <input type="button" class="btn btn-primary" v-bind:class={invisible:deleteCanHide} value="删除" @click="$emit('delete')"/><!--点击删除按钮会触发delete事件-->
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </template>
    <script>       
        Vue.component("one-book",{
            template:'#book_temp',
            props:['book'],
            data(){
                return{
                    deleteCanHide:true,
                }
            },
            methods:{
                hideDelete(){
                    this.deleteCanHide = true
                },
                showDelete(){
                    this.deleteCanHide = false
                }
            }  
        })
        var app = new Vue({
            el:'#table_book',
            created(){
                fetch("/books.json")
                .then(function(response){
                    return response.json();
                })
                .then(function(response){
                    app.books = response;
                })
            },
            data:{
                books:'',
            },
            methods:{
                deleteBook(index){
                    this.books.splice(index, 1);//books数组中从index位置开始删除1个数据
                }
            }
        })
    </script>       
</body>
</html>
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

姓蔡小朋友

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值