Vue组件
- 组件是可复用的实例,比如:一个网页中某一个部分需要在多场景中使用,可以抽成一个组件进行复用,提高代码复用性
- 组件化开发能大幅度的提高应用开效率、测试性、复用性
1. 组件注册
组件分类:全局组件、局部组件
- 全局组件:全局可用,可以用在所有新创建的Vue根实例(new Vue)的模板中
- 局部组件:只能在自己的根实例模板中
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue组件</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div class="app1">
<!-- 将全局组件加载到页面挂载点中 -->
<global-component-1></global-component-1>
<!-- 将局部组件加载到页面挂载点中 -->
<local-component-1></local-component-1>
<local-component-2></local-component-2>
</div>
<hr />
<div class="app2">
<global-component-1></global-component-1>
<!-- 局部组件不能用在其他根实例的挂载点中 -->
<local-component-1></local-component-1>
</div>
<script>
// 1. 全局组件
// Vue.component(组件名,组件模板内容)
// 组件名又叫组件标签,模板内容是一个对象
Vue.component("global-component-1", {
template: "<p>我是一个全局组件</p>",
});
const vm1 = new Vue({
el: ".app1",
// 2. 局部组件:仅限当前根实例使用
components: {
"local-component-1": {
template: "<p>这是局部组件一</p>",
},
"local-component-2": {
template: "<p>这是局部组件二</p>",
},
},
});
const vm2 = new Vue({
el: ".app2",
});
</script>
</body>
</html>
小总结:组件注册的注意点:
1. 全局组件注册以后,要加载到页面中,需要一个挂载点,这个挂载点就是Vue实例的挂载点,所有也必须要创建Vue实例
2. 在当前根实例中注册的局部组件,可以一次性注册多个,但是仅限使用在当前根实例的模板中或挂载点中
3. template,其字面意思就是模板,里面写用户自定义的组件模板内容
小问题:在实际开发中,肯定不可能像上述案例中,组件模板只有这样一条语句,所以通常会将组件模板进行优化,可以创建多条语句
2. 组件模板优化
关于组件模板优化的方式有很多,比如:在template
中使用ES6的模板语法,也可以书写多行语句,如下:
<body>
<div class="app">
<local-component-1></local-component-1>
</div>
<script>
const vm = new Vue({
el: ".app",
components: {
"local-component-1": {
template: `
<ul>
<li>文本一</li>
<li>文本二</li>
<li>文本三</li>
</ul>
`,
},
},
});
</script>
</body>
另外,也可以在HTML中借助<script>
标签来绑定模板内容,如下:
<div class="app">
<local-component-2></local-component-2>
</div>
<script type="x-template" id="local-com-2">
<div>
<h3>我的列表</h3>
<ul>
<li>列表一</li>
<li>列表二</li>
<li>列表三</li>
</ul>
</div>
</script>
<script>
const vm = new Vue({
el: ".app",
components: {
"local-component-2": {
template: "#local-com-2",
},
},
});
</script>
注意点:组件模板中的内容,必须是一个容器包裹的,如上述中,我用了一个div将h3和ul包裹了起来,如果没有这个div,那么页面只会显示h3的内容
上面的方式显然比第一种要好些,但是使用的是script
标签,始终感觉怪怪的,所以在实际开发中,采用得最多的方式就是下面这一种:
使用<template>
标签编写模板内容,再结合id
属性将模板内容与组件模板template
绑定,如下:
<div class="app">
<local-component-3></local-component-3>
</div>
<template id="local-com-3">
<div>
<h3>我的列表</h3>
<ul>
<li>列表一</li>
<li>列表二</li>
<li>列表三</li>
</ul>
</div>
</template>
<script>
const vm = new Vue({
el: ".app",
components: {
"local-component-3": {
template: "#local-com-3",
},
},
});
</script>
以上三种方式渲染出来的页面效果如下:
3. 组件的参数传递
先总结一下组件的参数传递有如下几种方式
- 通过自定义属性传参(自定义属性写在挂载点中,如:div)
- 通过
data()
方法传参,(data()
方法写在组件中,返回一个对象,键名就是参数名,键值就是参数值) - 自定义属性和
data()
方法两者结合传参 - 父组件向子组件传参(父组件即Vue根实例(
new Vue
),其实也是通过对象传参,只不过此时的对象被写在了Vue实例的数据对象data
中) - 子组件向父组件传参(通过事件来呼叫父组件响应,其响应过程其实也是通过自定义属性去调用父组件中的方法)
3.1 使用自定义属性向组件传参
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>使用自定义属性向组件传参</title>
<link
href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
rel="stylesheet"
/>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script>
</head>
<body>
<div class="app">
<!-- 将自定义属性写在组件标签中 -->
<g-component id="1001" username="Jack" email="jack@qq.com"></g-component>
</div>
<template id="g-com">
<div class="container">
<div class="row">
<div class="col-sm-4 col-md-offset-4">
<table class="table table-bordered">
<tr>
<th>ID</th>
<th>Username</th>
<th>Email</th>
</tr>
<tr>
<!-- 使用插值语法,将变量插入 -->
<td>{{id}}</td>
<td>{{username}}</td>
<td>{{email}}</td>
</tr>
</table>
</div>
</div>
</div>
</template>
<script>
Vue.component("g-component", {
template: "#g-com",
// 在组件中,使用 props 来接收组件标签传过来的参数
props: ["id", "username", "email"],
});
const vm = new Vue({
el: ".app",
});
</script>
</body>
</html>
3.2 使用data()向组件传参
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>使用data()向组件传参</title>
<link
href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
rel="stylesheet"
/>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script>
</head>
<body>
<div class="app">
<g-component></g-component>
</div>
<template id="g-com">
<div class="container">
<br /><br /><br />
<div class="row">
<div class="col-sm-8 col-md-offset-2">
<table class="table table-bordered">
<tr>
<th>ID</th>
<th>Username</th>
<th>Email</th>
</tr>
<tr>
<td>{{id}}</td>
<td>{{username}}</td>
<td>{{email}}</td>
</tr>
</table>
</div>
</div>
</div>
</template>
<script>
// data()应该写在组件中
Vue.component("g-component", {
template: "#g-com",
props: [],
data() {
return {
id: 1002,
username: "Tom",
email: "tom@qq.com",
};
},
});
const vm = new Vue({
el: ".app",
});
</script>
</body>
</html>
3.3 使用data()和属性向组件传参
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>使用data()和属性向组件传参</title>
<link
href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
rel="stylesheet"
/>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script>
</head>
<body>
<div class="app">
<!-- 使用自定义属性传参 -->
<g-component blog="https://www.myblog.com"></g-component>
</div>
<template id="g-com">
<div class="container">
<br /><br /><br />
<div class="row">
<div class="col-sm-8 col-md-offset-2">
<table class="table table-bordered">
<tr>
<th>ID</th>
<th>Username</th>
<th>Email</th>
<th>Blog</th>
</tr>
<tr>
<td>{{id}}</td>
<td>{{username}}</td>
<td>{{email}}</td>
<td><a :href="blog">{{blog}}</a></td>
</tr>
</table>
</div>
</div>
</div>
</template>
<script>
// data()应该写在组件中
Vue.component("g-component", {
template: "#g-com",
// 这里要接收组件标签中自定义属性传来的参数
props: ["blog"],
// 使用data()传参
data() {
return {
id: 1002,
username: "Tom",
email: "tom@qq.com",
};
},
});
const vm = new Vue({
el: ".app",
});
</script>
</body>
</html>
3.4 父组件向子组件传参
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>父组件向子组件传参</title>
<link
href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
rel="stylesheet"
/>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script>
</head>
<body>
<!-- 父组件:就是根实例 new Vue , 它是唯一的 -->
<div class="app">
<!-- 子组件:它可以有多个,可以由全局组件和局部组件共同组成 -->
<!-- 使用自定义属性来实现父组件向子组件传参 -->
<!-- 可以理解为使用一个自定义属性绑定了父组件中的参数,这个参数可以是对象,也可以是数组 -->
<g-component :data="users"></g-component>
</div>
<template id="g-com">
<div class="container">
<br /><br /><br />
<div class="row">
<div class="col-sm-8 col-md-offset-2">
<table class="table table-bordered">
<tr>
<th>ID</th>
<th>Username</th>
<th>Email</th>
</tr>
<!-- 这里就不能遍历users了,因为已经使用自定义属性data接管这个数据对象了 -->
<!-- 当然通常情况下,可以将自定义属性和数据对象命名一致,这里只是为了方便理解 -->
<tr v-for="item of data">
<td>{{item.id}}</td>
<td>{{item.username}}</td>
<td>{{item.email}}</td>
</tr>
</table>
</div>
</div>
</div>
</template>
<script>
Vue.component("g-component", {
template: "#g-com",
// 接收从父组件传过来的数据
props: ["data"],
});
// 父组件传给子组件的参数应该在数据对象中
const vm = new Vue({
el: ".app",
data: {
users: [
{ id: 1001, username: "alice", email: "alice@qq.com" },
{ id: 1002, username: "mike", email: "mike@qq.com" },
{ id: 1003, username: "lose", email: "lose@qq.com" },
],
},
});
</script>
</body>
</html>
3.5 子组件向父组件传参
通过一个购物车案例来演示这个传参过程
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>子组件向父组件传参:购物车案例</title>
<link
href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
rel="stylesheet"
/>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script>
</head>
<body>
<!-- Vue实例挂载点 -->
<div class="app">
<!-- 子组件通过事件呼叫父组件,使用自定义属性来完成这个交互 -->
<g-component :products="products" @total='cal' :cal='cal'></g-component>
<div class="container">
<div class="row">
<div class="col-md-4 col-md-offset-8">
<p>总数量:{{counts}} 件</p>
<p>总金额:{{totalPrice}} 元</p>
</div>
</div>
</div>
</div>
<!-- 组件模板 -->
<template id="g-com">
<div class="container">
<br><br>
<div class="row">
<div class="col-md-8 col-md-offset-2">
<table class="table table-bordered table-hover">
<thead >
<tr class="info" >
<th>商品ID</th>
<th>商品名称</th>
<th>商品价格</th>
<th>商品数量</th>
</tr>
<tbody>
<tr v-for='item of products'>
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.price}}</td>
<td><input type="number" v-model='item.count' @change='resetCal'></td>
</tr>
</tbody>
</thead>
</table>
</div>
</div>
</div>
</template>
<script>
// 全局组件
Vue.component('g-component',{
template:'#g-com',
props:['products'],
methods:{
resetCal(){
// 子组件向父组件呼叫,用来调用父组件中的cal()方法计算总数量和总金额
this.$emit('total');
}
},
});
// Vue实例
const vm = new Vue({
el: ".app",
data: {
products:[
{id:1001,name:'商品一',price:100,count:1},
{id:1002,name:'商品二',price:200,count:1},
{id:1003,name:'商品三',price:300,count:1},
],
totalPrice:0,
counts:0,
},
// 计算总数量与总金额的方法
methods: {
cal(){
this.totalPrice = 0;
this.counts = 0 ;
this.products.forEach((item)=>{
// 禁止输入框数量低于0
if(item.count<=0){item.count=0}
this.totalPrice += item.price * item.count;
this.counts += item.count*1;
});
},
},
// 挂载成功,立即执行这个函数
mounted() {
this.cal();
},
});
</script>
</body>
</html>
4. 小总结
1. 组件模板的优化首选采用<template>
标签,添加id
属性与组件模板 template
绑定
2. 使用 props
来接收组件的参数
3. 自定义属性传参是在组件标签中进行
4. data() 传参是在组件中进行
5. 父组件传参子组件,是通过子组件的自定义属性,绑定父组件数据对象 data
中的数据,然后通过 props
来接收这个数据对象
6. 子组件传参父组件,是通过事件呼叫父组件响应,而这个交互也是通过组件标签的自定义属性来实现的,记住$emit
,还有组件标签中的属性设置