Vue的组件化开发
组件注册
- 全局组件注册的语法
Vue.component(组件名称,{
data: 组件数据,
template: 组件模板内容
});
注意:
1.data必须是一个函数(目的是形成闭包环境,使数据独立)
2.组件模板的内容必须是单根元素
3.组件模板内容可以是模板字符串(ES6)
4.组件命名方式
其中,若使用驼峰的方式命名,则只能在字符串模板中使用,在普通的标签模板中还必须转换为短横线的方式(全部字母小写,并使用短横线相连)
* 短横线方式
Vue.component("my-component",{});
* 驼峰方式
Vue.component("MyComponent",{});
- 组件用法
1.可直接将名称作为标签,在html中使用
例:
<div id="app">
<button-counter></button-counter>
</div>
2.组件可以充分使用,并且组件之间的数据相互隔离互不影响
<div id="app">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>
实例:使用组件的方式,实现点击按钮触发数据自增的功能。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>
<script src="js/jquery-3.4.1.js"></script>
<script src="js/vue.js"></script>
<script>
Vue.component("button-counter",{
data: function(){
return {
num: 0
}
},
template: "<button @click='handle'>点击了{{num}}次</button>",
methods: {
handle: function(){
this.num++;
}
}
});
var vm = new Vue({
el: "#app",
data: {
}
});
</script>
</body>
</html>
- 局部组件注册
局部组件只能在注册的父容器中使用,在其他组件中无法使用。此处,与自定义指令和过滤器类似。
var ComponentA = {};
var ComponentB = {};
var ComponentC = {};
new Vue({
el: "#app",
components: {
'component-a':ComponentA,
'component-b':ComponentB,
'component-c':ComponentC
}
});
实例:通过局部组件注册几个组件,并调用测试。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<hello-yorick></hello-yorick>
<hello-tom></hello-tom>
</div>
<script src="js/jquery-3.4.1.js"></script>
<script src="js/vue.js"></script>
<script>
var helloYorick = {
data: function(){
return {
msg: "Hello yorick"
};
},
template: "<div>{{msg}}</div>"
};
var helloTom = {
data: function(){
return {
msg: "Hello tom"
};
},
template: "<div>{{msg}}</div>"
};
var vm = new Vue({
el: "#app",
data: {
},
components:{
"hello-yorick": helloYorick,
"hello-tom": helloTom
}
});
</script>
</body>
</html>
组件间的数据交互
- 父组件向子组件传值
- 组件内部通过props接收传递过来的数值
Vue.component("menu-item",{
props:["title"],
template: "<div>{{title}}</div>"
});
注意:
1.若在props中使用驼峰形式,则普通标签模板中需要使用短横线的形式传递。而在字符串形式的模板中没有此限制。
2.props是单向数据流。
- 父组件通过属性将值传递给子组件,有以下三种传递方式
1.传递静态的值
<menu-item title="来自父组件的数据"></menu-item>
2.传递动态的值
<menu-item :title="ptitle"></menu-item>
3.两者结合使用
<menu-item :title="ptitle" content="父组件传递的静态content值"></menu-item>
实例:通过父组件向子组件传递数据,并显示数据内容。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<div>{{fmsg}}</div>
<menu-item title="父组件传递的静态title值"></menu-item>
<menu-item :title="ptitle"></menu-item>
<menu-item :title="ptitle" content="父组件传递的静态content值"></menu-item>
</div>
<script src="js/jquery-3.4.1.js"></script>
<script src="js/vue.js"></script>
<script>
Vue.component("menu-item",{
props: ["title","content"],
data: function(){
return {
smsg: "子组件内容"
}
},
template: "<div>{{smsg + '---' + title + '----' + content}}</div>"
});
var vm = new Vue({
el: "#app",
data: {
ptitle: "父组件传递的动态title值",
fmsg: "父组件的内容"
}
});
</script>
</body>
</html>
- props传递不同类型数据
字符串 String
数值 Number(通过v-bind绑定的属性,未绑定的为String类型)
布尔值 Boolean(通过v-bind绑定的属性,未绑定的为String类型)
数组 Array
对象 Object
实例:父组件向子组件传递不同类型的数据,并显示。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<menu-item :pstr="pstr" :pnum="233" :pboo="true" :parr="parr" :pobj="pobj"></menu-item>
</div>
<script src="js/jquery-3.4.1.js"></script>
<script src="js/vue.js"></script>
<script>
Vue.component("menu-item",{
props: ["pstr","pnum","pboo","parr","pobj"],
template: `
<div>
<div>{{pstr}}</div>
<div>{{typeof pnum + '---' + pnum}}</div>
<div>{{typeof pboo + '---' + pboo}}</div>
<ul>
<li :key='index' v-for='(item,index) in parr'>{{item}}</li>
</ul>
<div>
<span>{{pobj.uname}}</span>
<span>{{pobj.age}}</span>
</div>
</div>
`
});
var vm = new Vue({
el: "#app",
data: {
pstr: "String",
parr: ["apple","lemon","banana"],
pobj: {
uname: "alice",
age: 13
}
}
});
</script>
</body>
</html>
- 子组件向父组件传值
- 子组件通过自定义事件向父组件传递信息
<button v-on:click="$emit('enlarge-text')">
扩大字体
</button>
- 父组件需要监听子组件传递的事件
<menu-item v-on:enlarge-text="fontSize+=0.1"></menu-item>
实例:通过子组件向父组件传递信息,使得父组件的文本大小增加。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<div :style="{fontSize:fontSize+'px'}">{{fmsg}}</div>
<menu-item v-on:enlarge-text="handle"></menu-item>
</div>
<script src="js/jquery-3.4.1.js"></script>
<script src="js/vue.js"></script>
<script>
Vue.component("menu-item",{
template: `
<button @click="$emit('enlarge-text')">点击按钮</button>
`
});
var vm = new Vue({
el: "#app",
data: {
fmsg: "父文本内容",
fontSize: 10
},
methods: {
handle: function(){
this.fontSize += 5;
}
}
});
</script>
</body>
</html>
- 子组件向父组件传递信息时携带参数
<button v-on:click="$emit('enlarge-text',0.1)">
扩大字体
</button>
- 父组件获取子组件传递的参数
<menu-item v-on:enlarge-text="fontSize+=$event"></menu-item>
实例:改进上述实例代码,实现将增加的值由子组件传递过来。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<div :style="{fontSize:fontSize+'px'}">{{fmsg}}</div>
<menu-item v-on:enlarge-text="handle($event)"></menu-item>
</div>
<script src="js/jquery-3.4.1.js"></script>
<script src="js/vue.js"></script>
<script>
Vue.component("menu-item",{
template: `
<button @click="$emit('enlarge-text',5)">点击按钮</button>
`
});
var vm = new Vue({
el: "#app",
data: {
fmsg: "父文本内容",
fontSize: 10
},
methods: {
handle: function(val){
this.fontSize += val;
}
}
});
</script>
</body>
</html>
- 兄弟组件之间的数据传递
- 单独的事件中心负责管理组件间的通信
var hub = new Vue();
- 监听事件与销毁事件
hub.$on("add-todo",addTodo);
hub.$off("add-todo");
- 触发事件
hub.$emit("add-todo",id);
实例:通过兄弟组件之间的数据传递,实现1号控制2号的数据变化,2号控制1号的数据变化。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<test-tom></test-tom>
<test-jerry></test-jerry>
<div>
<button @click="handle">销毁事件</button>
</div>
</div>
<script src="js/jquery-3.4.1.js"></script>
<script src="js/vue.js"></script>
<script>
var hub = new Vue();
Vue.component("test-tom",{
data: function(){
return {
num: 0
}
},
template: `
<div>
<div>TOM:{{num}}</div>
<button @click='handle'>点击</button>
</div>
`,
methods: {
handle: function(){
hub.$emit("jerry-event",2);
}
},
mounted: function(){
hub.$on("tom-event",(val)=>{
this.num += val;
});
}
});
Vue.component("test-jerry",{
data: function(){
return {
num: 0
}
},
template: `
<div>
<div>JERRY:{{num}}</div>
<button @click='handle'>点击</button>
</div>
`,
methods: {
handle: function(){
hub.$emit("tom-event",1);
}
},
mounted: function(){
hub.$on("jerry-event",(val)=>{
this.num += val;
});
}
});
var vm = new Vue({
el: "#app",
data: {
},
methods: {
handle: function(){
hub.$off("tom-event");
hub.$off("jerry-event");
}
}
});
</script>
</body>
</html>
组件插槽
- 组件插槽的基本使用
- 插槽位置
Vue.component("alert-box",{
template: `
<div>
<strong>Error:</strong>
<slot></slot>
</div>
`
});
- 插槽内容
<alert-box>出现异常</alert-box>
实例:使用插槽可以获取父组件中标签内部的数据。若数据为空,则会显示slot标签内的默认数据。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<alert-box>出现异常</alert-box>
<alert-box></alert-box>
</div>
<script src="js/jquery-3.4.1.js"></script>
<script src="js/vue.js"></script>
<script>
Vue.component("alert-box",{
template: `
<div>
<strong>Error:</strong>
<slot>默认内容</slot>
</div>
`
});
var vm = new Vue({
el: "#app",
data: {
}
});
</script>
</body>
</html>
- 具名插槽的用法
- 具名插槽定义
Vue.component("container",{
template: `
<div>
<header>
<slot name='header'></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name='footer'></slot>
</footer>
</div>
`
});
- 具名插槽的内容
1.单个标签添加slot属性
<container>
<h1 slot="header">标题内容</h1>
<p>主要内容1</p>
<p>主要内容2</p>
<p slot="footer">底部内容</p>
</container>
2.多个标签添加slot属性
<container>
<template slot="header">
<h1>标题内容1</h1>
<h1>标题内容2</h1>
</template>
<p>主要内容1</p>
<p>主要内容2</p>
<template slot="footer">
<p>底部内容1</p>
<p>底部内容2</p>
</template>
</container>
实例:按照插槽给予的名字,实施对应数据的绑定
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<container>
<h1 slot="header">标题内容</h1>
<p>主要内容1</p>
<p>主要内容2</p>
<p slot="footer">底部内容</p>
</container>
<container>
<template slot="header">
<h1>标题内容1</h1>
<h1>标题内容2</h1>
</template>
<p>主要内容1</p>
<p>主要内容2</p>
<template slot="footer">
<p>底部内容1</p>
<p>底部内容2</p>
</template>
</container>
</div>
<script src="js/jquery-3.4.1.js"></script>
<script src="js/vue.js"></script>
<script>
Vue.component("container",{
template: `
<div>
<header>
<slot name='header'></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name='footer'></slot>
</footer>
</div>
`
});
var vm = new Vue({
el: "#app",
data: {
}
});
</script>
</body>
</html>
- 作用域插槽
- 作用域插槽的定义
<div>
<li :key='item.id' v-for='item in list'>
<slot :info='item'>
{{item.name}}
</slot>
</li>
</div>
- 作用域插槽的内容
<container :list="list">
<template slot-scope="slotProps">
<strong v-if='slotProps.info.id==2' class="current">
{{slotProps.info.name}}
</strong>
<span v-else>{{slotProps.info.name}}</span>
</template>
</container>
实例:通过作用域插槽实现对子组件中某个数据内容进行样式的加工处理。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<style type="text/css">
.current {
color: orange;
}
</style>
<body>
<div id="app">
<container :list="list">
<template slot-scope="slotProps">
<strong v-if='slotProps.info.id==2' class="current">
{{slotProps.info.name}}
</strong>
<span v-else>{{slotProps.info.name}}</span>
</template>
</container>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
/*
作用域插槽
*/
Vue.component("container",{
props:["list"],
template: `
<div>
<li :key='item.id' v-for='item in list'>
<slot :info='item'>
{{item.name}}
</slot>
</li>
</div>
`
});
var vm = new Vue({
el: '#app',
data: {
list: [{
id: 1,
name: 'apple'
},{
id: 2,
name: 'orange'
},{
id: 3,
name: 'banana'
}]
}
});
</script>
</body>
</html>
案例
购物车组件化开发案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
.container {
}
.container .cart {
width: 300px;
/*background-color: lightgreen;*/
margin: auto;
}
.container .title {
background-color: lightblue;
height: 40px;
line-height: 40px;
text-align: center;
/*color: #fff;*/
}
.container .total {
background-color: #FFCE46;
height: 50px;
line-height: 50px;
text-align: right;
}
.container .total button {
margin: 0 10px;
background-color: #DC4C40;
height: 35px;
width: 80px;
border: 0;
}
.container .total span {
color: red;
font-weight: bold;
}
.container .item {
height: 55px;
line-height: 55px;
position: relative;
border-top: 1px solid #ADD8E6;
}
.container .item img {
width: 45px;
height: 45px;
margin: 5px;
}
.container .item .name {
position: absolute;
width: 90px;
top: 0;left: 55px;
font-size: 16px;
}
.container .item .change {
width: 100px;
position: absolute;
top: 0;
right: 50px;
}
.container .item .change a {
font-size: 20px;
width: 30px;
text-decoration:none;
background-color: lightgray;
vertical-align: middle;
}
.container .item .change .num {
width: 40px;
height: 25px;
}
.container .item .del {
position: absolute;
top: 0;
right: 0px;
width: 40px;
text-align: center;
font-size: 40px;
cursor: pointer;
color: red;
}
.container .item .del:hover {
background-color: orange;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<cart></cart>
</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var cartHeader = {
props:["uname"],
template: `
<div>
<div class="title">{{uname}}的商品</div>
</div>
`
};
var cartList = {
props:["list"],
template: `
<div>
<div class="item" :key="item.id" v-for="item in list">
<img :src="item.img"/>
<div class="name">{{item.name}}</div>
<div class="change">
<a href="" @click.prevent="subNum(item.id)">-</a>
<input type="text" class="num" :value="item.num" @blur="changeNum(item.id,$event)" />
<a href="" @click.prevent="addNum(item.id)">+</a>
</div>
<div class="del" @click="del(item.id)">×</div>
</div>
</div>
`,
methods: {
changeNum: function(id,event){
this.$emit("change-num",{
id:id,
type: "change",
num:event.target.value
});
},
subNum: function(id){
this.$emit("change-num",{
id:id,
type: "sub"
});
},
addNum: function(id){
this.$emit("change-num",{
id:id,
type: "add"
});
},
del: function(id){
this.$emit("del",id);
}
}
};
var cartFooter = {
props: ["list"],
template: `
<div class="total">
<span>总价:{{total}}</span>
<button>结算</button>
</div>
`,
computed: {
total: function(){
var sum = 0;
this.list.forEach(item=>{
sum += item.price * item.num;
});
return sum;
}
}
};
Vue.component("cart",{
data: function(){
return {
uname: "yorick",
list: [{
id: 1,
name: 'TCL彩电',
price: 1000,
num: 1,
img: 'img/a.jpg'
},{
id: 2,
name: '机顶盒',
price: 1000,
num: 1,
img: 'img/b.jpg'
},{
id: 3,
name: '海尔冰箱',
price: 1000,
num: 1,
img: 'img/c.jpg'
},{
id: 4,
name: '小米手机',
price: 1000,
num: 1,
img: 'img/d.jpg'
},{
id: 5,
name: 'PPTV电视',
price: 1000,
num: 2,
img: 'img/e.jpg'
}]
}
},
template: `
<div class="cart">
<cart-header :uname='uname'></cart-header>
<cart-list :list='list' @del="del($event)" @change-num="changeNum($event)"></cart-list>
<cart-footer :list='list'></cart-footer>
</div>
`,
components: {
"cart-header": cartHeader,
"cart-list": cartList,
"cart-footer": cartFooter
},
methods: {
del: function(id){
this.list = this.list.filter(item=>{
return item.id != id;
});
},
changeNum: function(val){
if(val.type == "change"){
this.list.some(item=>{
if(item.id == val.id){
item.num = val.num;
return true;
}
});
}else if(val.type == "sub"){
this.list.some(item=>{
if(item.id == val.id){
item.num--;
return true;
}
});
}else if(val.type == "add"){
this.list.some(item=>{
if(item.id == val.id){
item.num++;
return true;
}
});
}
}
}
});
var vm = new Vue({
el: '#app',
data: {
}
});
</script>
</body>
</html>
网友评论