文章目录
1、定义局部组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="js/vue.js"></script>
<div id="box">
<global_f></global_f>
<div @click="clickHandler">我是数据</div>
<child1></child1>
<child2></child2>
</div>
<script>
// 定义全局组件
Vue.component('global_f', {
template: `
<div>
<div @click="showInfo">我是全局组件</div>
<div v-if="isShow">
<div>显示或消失</div>
<div>名字是: {{name}}</div>
<div>年龄是: {{age}}</div>
</div>
</div>
`,
data() {
return {
name: 'allen',
age: 18,
isShow: true,
}
},
methods: {
showInfo() {
console.log('全局组件的方法: ', this.name, this.age);
this.isShow = !this.isShow;
},
}
});
var vm = new Vue({
el: '#box',
data: {},
methods: {
clickHandler() {
console.log('我被点击了');
}
},
// 定义子组件
components: {
child1: {
template: `
<div @click="clickC1">我是子组件1</div>
`,
methods: {
clickC1() {
console.log('child1被点击了');
}
}
},
child2: {
template: `
<div @click="clickC2">我是子组件2</div>
`,
methods: {
clickC2() {
console.log('child2被点击了');
}
}
}
},
})
</script>
</body>
</html>
2、组件编写方式与Vue实例的区别
1、自定义组件需要有一个root element,一般包裹在一个div中,跟vue实例一样
2、父子组件的data是无法共享的
3、组件可以有data,methods,computed...,但是data必须是一个函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="js/vue.js"></script>
<div id="box">
<navbar></navbar>
{{xxx}}
</div>
<script>
// 定义全局组件
Vue.component('navbar', {
// 全局组件中定义子组件child
template: `
<div>
<button @click="handleClick">返回</button>
我是NavBar: {{x}}
<button style="background: red">主页</button>
<br>
<child></child>
</div>
`,
methods: {
handleClick() {
console.log('nav nav');
},
},
components: {
child: {
template: `
<button @click="clickHandle">子组件</button>
`,
methods: {
clickHandle() {
console.log('子组件被点击了');
}
},
}
},
data() {
return {
x: 'hello world',
}
},
});
var vm = new Vue({
el: '#box',
data: {
xxx: 18,
},
methods: {},
})
</script>
</body>
</html>
3、组件通信
1、父子组件传值 (props down, events up)
2、父传子之属性验证props:{name:Number}Number,String,Boolean,Array,Object,Function,null(不限制类型)
3、事件机制a.使用 $on(eventName) 监听事件b.使用 $emit(eventName) 触发事件
4、Ref<input ref="mytext"/> this.$refs.mytext
5、事件总线var bus = new Vue();* mounted生命周期中进行监听
3.1、父传子通信
3.1.1、具体步骤
(1) 父子通信
(2) 在全局组件上自定义属性
<info myname="allen" mybtn="btn1"></info>
(3) 在组件中获取
props: ['myname', 'mybtn'] # myname=allen mybtn=btn1
(4) 注意区分以下赋值方式
<info myname="allen" mybtn="btn1"></info>
<info :myname="'jack'" mybtn="btn2"></info>
<info :myname="name" :mybtn="btn"></info>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="js/vue.js"></script>
<div id="box">
<!--myname和mybtn是自定义属性-->
<info myname="allen" mybtn="btn1"></info>
<info :myname="'jack'" mybtn="btn2"></info>
<info :myname="name" :mybtn="btn"></info>
</div>
<script>
// 定义全局组件
Vue.component('info', {
template: `
<div>
<div>我要传递的数据: {{myname}}</div>
<button style="background: red">{{mybtn}}</button>
</div>
`,
props: ['myname', 'mybtn'],
});
var vm = new Vue({
el: '#box',
data: {
name: 'tom',
btn: 'btn3',
},
methods: {},
})
</script>
</body>
</html>
3.1.2、属性验证
1、限制父传子的变量类型
props: {
myname:String,
isshow:Boolean
}
2、父传子时注意以下的区别
<info myname="allen" :isshow=isshow></info>
<info myname="jack" :isshow=true></info>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="js/vue.js"></script>
<div id="box">
<!--myname和mybtn是自定义属性-->
<info myname="allen" :isshow=isshow></info>
<info myname="jack" :isshow=true></info>
</div>
<script>
// 定义全局组件
Vue.component('info', {
template: `
<div>
<div>我要传递的数据: {{myname}}</div>
<div>我是info: {{isshow}}</div>
</div>
`,
props: {
myname: String,
isshow: Boolean,
},
});
var vm = new Vue({
el: '#box',
data: {
name: 'allen',
isshow: false,
},
methods: {},
})
</script>
</body>
</html>
3.2、子传父通信
3.2.1、具体步骤
通过事件实现: 点击一下子组件,就会触发父组件某个函数的执行
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="js/vue.js"></script>
<div id="box">
<navbar @myevent="clickHandler"></navbar>
<div v-model="mydata">子组件传递过来的数据: {{mydata}}</div>
</div>
<script>
// 定义全局组件
Vue.component('navbar', {
template: `
<div>
<button @click="navHandler">点击触发父组件的某个函数执行</button>
</div>
`,
data() {
return {
name: 'allen',
}
},
methods: {
navHandler() {
console.log('我是nav函数我执行了');
this.$emit('myevent', this.name);
},
}
});
var vm = new Vue({
el: '#box',
data: {
mydata: '',
},
methods: {
clickHandler(a) {
console.log(a);
this.mydata = a;
},
},
})
</script>
</body>
</html>
3.2.2、小案例
子组件有一个按钮,有一个输入框,当输入完内容,点击按钮,数据在父组件中展示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="js/vue.js"></script>
<div id="box">
<navbar @myevent="getData"></navbar>
<hr>
<div>展示得到的数据: {{mytext}} {{mydata}}</div>
</div>
<script>
// 定义全局组件
Vue.component('navbar', {
template: `
<div>
<div>请输入数据: <input type="text" v-model="mytext"></div>
<button @click="clickHandler">点我在父组件中展示输入内容</button>
</div>
`,
data() {
return {
mytext: '',
mydata: '这个数据默认的',
}
},
methods: {
clickHandler() {
this.$emit('myevent', this.mytext, this.mydata)
}
}
});
var vm = new Vue({
el: '#box',
data: {
mytext: '',
mydata: '',
},
methods: {
getData(x, y) {
this.mytext = x;
this.mydata = y;
}
},
})
</script>
</body>
</html>
3.3、ref属性
也可以实现组件间通信,子向父,父向子传递数据都可以使用
1、ref放在标签上,拿到的是原生节点
2、ref放在组件上,拿到的是组件对象
通过这种方式实现子传父(this.$refs.mychild.text)
通过这种方式实现父传子(调用子组件方法传参数)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="js/vue.js"></script>
<div id="box">
<child ref="mychild"></child>
<hr>
<button @click="handleButton">点我</button>
</div>
<script>
// 定义全局组件
Vue.component('child', {
template: `
<div>
<input type="text" v-model="mytext">
<hr>
我是子组件的input
</div>
`,
data() {
return {
mytext: '',
}
},
methods: {
add(x) {
console.log('我是子组件的add方法');
console.log(x);
return '返回数据';
}
}
});
var vm = new Vue({
el: '#box',
data: {
name: 'allen',
},
methods: {
handle(a) {
this.name = a
},
handleButton() {
console.log(this.$refs.mychild.mytext);
console.log(this.$refs.mychild.add(this.name));
}
},
})
</script>
</body>
</html>
4、事件总线
不同层级的不同组件之间的通信
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="js/vue.js"></script>
<div id="box">
<child1></child1>
<hr>
<child2></child2>
</div>
<script>
// 定义一个事件总线
var bus = new Vue();
// 定义全局组件1
Vue.component('child1', {
template: `
<div>
<input type="text" v-model="text">
<button @click="handleClick">点我传递数据到另一个组件</button>
</div>
`,
data() {
return {
text: '',
}
},
methods: {
handleClick() {
console.log(this.text);
// 通过事件总线发送
bus.$emit('senddata', this.text);
},
}
});
// 定义全局组件2
Vue.component('child2', {
template: `
<div>
收到的消息是:{{recv_text}}
</div>
`,
data() {
return {
recv_text: '',
}
},
mounted() {
// 组件挂载(生命周期钩子函数中的一个),开始监听事件总线上的senddata
bus.$on('senddata', (item) => {
console.log('收到数据: ', item);
this.recv_text = item;
})
}
});
var vm = new Vue({
el: '#box',
data: {},
methods: {},
})
</script>
</body>
</html>
5、动态组件
1、<component> 元素,动态地绑定多个组件到它的 is 属性
2、<keep-alive> 保留状态,避免重新渲染
5.1、component
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="js/vue.js"></script>
<div id="box">
<ul>
<li @click="who='child1'">首页</li>
<li @click="who='child2'">商品</li>
<li @click="who='child3'">订单</li>
</ul>
<component :is="who"></component>
</div>
<script>
var vm = new Vue({
el: '#box',
data: {
who: 'child1',
},
components: {
child1: {
template: `
<div>我是首页</div>
`
},
child2: {
template: `
<div>我是商品页</div>
`
},
child3: {
template: `
<div>我是订单页</div>
`
}
},
})
</script>
</body>
</html>
5.2、keep-alive
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="js/vue.js"></script>
<div id="box">
<ul>
<li @click="who='child1'">首页</li>
<li @click="who='child2'">商品</li>
<li @click="who='child3'">订单</li>
</ul>
<keep-alive> <!--加上keep-alive后input框中的数据在点击其他按钮后还存在-->
<component :is="who"></component>
</keep-alive>
</div>
<script>
var vm = new Vue({
el: '#box',
data: {
who: 'child1',
},
components: {
child1: {
template: `
<div>我是首页 <input type="text"></div>
`
},
child2: {
template: `
<div>我是商品页</div>
`
},
child3: {
template: `
<div>我是订单页</div>
`
}
},
})
</script>
</body>
</html>
6、slot插槽
1、单个slot
2、具名slot
*混合父组件的内容与子组件自己的模板 ---> 内容分发
*父组件模板的内容在父组件作用域内编译;子组件模板的内容在子组件作用域内编译
6.1、基本使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="js/vue.js"></script>
<div id="box">
<child1>
<ul>
<li v-for="item in 4">{{item}}</li>
</ul>
</child1>
<child2></child2>
<child3></child3>
</div>
<script>
var vm = new Vue({
el: '#box',
data: {},
components: {
child1: {
template: `
<div>
<slot></slot>
<hr>
我是首页
</div>
`
},
child2: {
template: `
<div>我是商品页</div>
`
},
child3: {
template: `
<div>我是订单页</div>
`
}
},
})
</script>
</body>
</html>
6.2、插槽小案例
一个组件通过插槽控制另一个组件的显示隐藏
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="js/vue.js"></script>
<div id="box">
<child1>
<button @click="isShow=!isShow">点我显示/隐藏child2</button>
</child1>
<child2 v-if="isShow"></child2>
</div>
<script>
var vm = new Vue({
el: '#box',
data: {
isShow: true,
},
components: {
child1: {
template: `
<div>
<slot></slot>
</div>
`,
},
child2: {
template: `
<div>
<ul>
<li v-for="i in 4">{{i}}</li>
</ul>
</div>
`
},
},
})
</script>
</body>
</html>
6.3、具名插槽
指定标签放到组件的某个插槽中
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="js/vue.js"></script>
<div id="box">
<child1>
<button @click="isShow=!isShow" slot="button1">点我显示/隐藏child2</button>
<div slot="div1"></div>
</child1>
<child2 v-if="isShow"></child2>
</div>
<script>
var vm = new Vue({
el: '#box',
data: {
isShow: true,
},
components: {
child1: {
template: `
<div>
<slot name="button1"></slot>
<hr>
我是华丽的分割线
<hr>
<slot name="div1"></slot>
</div>
`,
},
child2: {
template: `
<div>
<ul>
<li v-for="i in 4">{{i}}</li>
</ul>
</div>
`
},
},
})
</script>
</body>
</html>