组件Component
- vue.extend():调用vue.extend()创建的是一个组件构造器
通常在创建组件构造器时,传入template嗲表我们自定义组件的模板
该模板就是在使用到组件的地方,需要显示的HTML代码。 - 当我们通过调用Vue.component()注册组件时,组件的注册是全局的,这就意味着该组件可以在任意vue示例下使用
- 如果我们注册的组件挂载在某个实例中,那么就是一个局部组件
<body>
<div id="app">
<p>Hell vue</p>
<my-cpn></my-cpn>
<test></test>
</div>
<script>
//全局组件
let cpn = Vue.extend({
template:`
<div>
<h2>我是标题</h2>
<p>哈哈哈哈</p>
</div>
`
});
Vue.component("my-cpn",cpn);//注册组件为全局组件
//局部组件
var app = new Vue({
el: "#app",
components:{
//局部组件
test:cpn,
}
})
</script>
</body>
全局组件的引入
main.js
import Vue from 'vue';
import MyCpn from '@/components/MyCpn.vue'; // 导入自己写的组件文件
Vue.use(MyComponent); // 需要调用Vue.use(),跟使用插件一样
Vue.component('my-component', MyComponent); //初始化组件
new Vue({
el: '#app',
router,
components: {
App,
MyCpn
},
template: '<App/>',
});
父子组件
<body>
<div id="app">
<p>Hell vue</p>
<parentcpn></parentcpn>
</div>
<script>
//创建第一个组件构造器
const chidcpn = Vue.extend({
template:`
<div>
<h2>我是标题</h2>
<p>哈哈哈哈</p>
</div>
`
});
//创建第二个组件构造器
const parentcpn = Vue.extend({
template:`
<div>
<h2>我是标题</h2>
<p>我是第二个组件构造器</p>
<chidcpn></chidcpn>
</div>
`,
components:{
chidcpn:chidcpn
}
});
var app = new Vue({
el: "#app",
components:{
//局部组件
parentcpn:parentcpn,
}
})
</script>
</body>
组件的语法糖
Vue为了简化这个过程,提供了注册的语法糖
主要省去了调用vue.extend()的步骤,而是可以直接使用一个对象来替代
<body>
<div id="app">
<p>Hell vue</p>
<cpn></cpn>
</div>
<script>
// 方式一
Vue.component("my-cpn",{
template:`
<div>
<h2>我是标题</h2>
<p>哈哈哈哈</p>
</div>
`
});
//方式二
const cpn = {
template:`
<div>
<h2>我是标题</h2>
<p>哈哈哈哈</p>
</div>
`
};
var app = new Vue({
el: "#app",
components: {
cpn,
}
})
</script>
</body>
模板的分离写法
Vue提供了两种方案:
- 使用
<body>
<div id="app">
<p>Hell vue</p>
<my-cpn></my-cpn>
</div>
<!-- 方法一 -->
<!-- <script type="text/x-template" id="cpntemplate">
<div>
<h2>我是标题</h2>
<p>哈哈哈哈</p>
</div>
</script> -->
<!-- 方法二 -->
<template id="cpntemplate">
<div>
<h2>我是标题</h2>
<p>哈哈哈哈</p>
</div>
</template>
<script>
//全局组件
Vue.component("my-cpn",{
template:"#cpntemplate"
})
var app = new Vue({
el: "#app",
})
</script>
</body>
data是函数得原理
- 组件不可以访问vue实例中的数据,vue应该有自己存放数据的地方
- 组件也有一个data属性,且必须是有一个函数
- 这个函数返回一个对象,对象内部保存着数据
-为什么是一个函数,如果是一个对象的话,当组件多次使用的时候,会造成数据被共用的问题,为了让组件在每次使用的时候,都有独立的data对象,所以必须是函数
<body>
<div id="app">
<cpn></cpn>
<cpn></cpn>
</div>
<script>
const cpn = {
template:`
<div>
<p>个数是{{count}}</p>
<button @click="increment">+</button>
<button @click="decrement">-</button>
</div>
`,
data(){
return{
count:0,
}
},
methods: {
increment(){
this.count++;
},
decrement(){
this.count--;
}
},
}
var app = new Vue({
el: "#app",
data: {
},
components:{
cpn
}
})
</script>
</body>
当data 为对象时,报一下错误:
<body>
<div id="app">
<cpn></cpn>
<cpn></cpn>
</div>
<script>
const cpndata = {
count:0,
}
const cpn = {
template:`
<div>
<p>个数是{{count}}</p>
<button @click="increment">+</button>
<button @click="decrement">-</button>
</div>
`,
data(){
return cpndata;//导致多个vue实列返回得到是同一个data对象
},
methods: {
increment(){
this.count++;
},
decrement(){
this.count--;
}
},
}
var app = new Vue({
el: "#app",
data: {
},
components:{
cpn
}
})
</script>
</body>
父子组件的通信
如何进行父子组件间的通信呢
- 通过prop向子组件传递数据
- 通过事件向父组件发送消息
<body>
<div id="app">
<cpn :cmoives="movies" :cmessage="message"></cpn>
</div>
<!-- 子组件template -->
<template id="cpntemplate">
<div>
<p>{{cmessage}}</p>
<ul>
<li v-for="item in cmoives">{{item}}</li>
</ul>
</div>
</template>
<script>
// 子组件
const cpn = {
template:"#cpntemplate",
data(){
return {
};
},
// 方式一
// props:['cmoives','cmessage'],
//方式二
// props:{
// cmoives:Array,
// cmessage:String,
// }
//方式三
props:{
cmoives:{
type:Array,
// default:[]
//默认值是一个对象或数组时,default是一个函数
default(){
return [];
},
validator:function(value){
console.log(value);
return false;
}
},
cmessage:{
type:String,
default:'',
required:true,
},
}
}
//父组件
var app = new Vue({
el: "#app",
data: {
movies:['ddd','ccccc','eeeee'],
message:"aaaaa",
},
components:{
cpn
}
})
</script>
</body>
问题
当props中的属性是驼峰命名时,会报错,新版本可以用c-Message解决
子组件向父组件传递事件
this.$emit(‘itemclick’,item); 第一个参数需要传递事件的名字
<body>
<div id="app">
<cpn @itemclick="cpnclick"></cpn>
</div>
<!-- 子组件template -->
<template id="cpntemplate">
<div>
<button v-for="item in catgories" @click="itemclick(item)">{{item.name}}</button>
</div>
</template>
<script>
// 子组件
const cpn = {
template:"#cpntemplate",
data(){
return {
catgories:[
{id:'aaa',name:'热门推荐'},
{id:'bbb',name:'手机数码'},
{id:'ccc',name:'家用家电'},
]
};
},
methods:{
itemclick(item){
console.log(item);
this.$emit('itemclick',item);
}
}
}
//父组件
var app = new Vue({
el: "#app",
data: {
},
components:{
cpn
},
methods:{
cpnclick(item){
console.log("itemdddd",item);
}
}
})
</script>
</body>
父子组件通信案例
props中的属性不要直接v-model
错误实现方法
<body>
<div id="app">
<cpn :cnumber1="dnumber1" :cnumber2="dnumber2"></cpn>
</div>
<!-- 子组件template -->
<template id="cpntemplate">
<div>
<p>{{cnumber1}}</p>
<input type="text" v-model="cnumber1">
<p>{{cnumber2}}</p>
<input type="text" v-model="cnumber2">
</div>
</template>
<script>
// 子组件
const cpn = {
template:"#cpntemplate",
data(){
return {
};
},
props:{
cnumber1:Number,
cnumber2:Number,
}
}
//父组件
var app = new Vue({
el: "#app",
data: {
dnumber1:1,
dnumber2:2,
},
components:{
cpn
},
})
</script>
</body>
正确实现方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../vue.js"></script>
</head>
<body>
<div id="app">
<cpn :cnumber1="dnumber1" :cnumber2="dnumber2" @num1change="num1change" @num2change="num2change"></cpn>
<p>dnumber1: {{dnumber1}}</p>
<p>dnumber2: {{dnumber2}}</p>
</div>
<!-- 子组件template -->
<template id="cpntemplate">
<div>
<p>{{dcnumber1}}</p>
<input type="text" v-model="dcnumber1" >
<p>{{dcnumber2}}</p>
<input type="text" v-model="dcnumber2">
</div>
</template>
<script>
// 子组件
const cpn = {
template:"#cpntemplate",
data(){
return {
//通过data对象中的属性去接受从父组件中传来的数据
dcnumber1:this.cnumber1,
dcnumber2:this.cnumber2,
};
},
props:{
cnumber1:Number,
cnumber2:Number,
},
watch:{
//监听值是否发生变化,dcnumber1需要和data中的属性名一致,也需要v-model绑定
dcnumber1(newValue){
this.dcnumber1 = newValue;
this.$emit('num1change',newValue);
},
dcnumber2(newValue){
this.dcnumber2= newValue;
this.$emit('num2change', newValue);
}
}
}
//父组件
var app = new Vue({
el: "#app",
data: {
dnumber1:1,
dnumber2:2,
},
components:{
cpn
},
methods:{
num1change(value){
this.dnumber1=parseInt(value)
},
num2change(value){
this.dnumber2=parseInt(value);
}
}
})
</script>
</body>
</html>
注意:
- 1 需要用data对象中的属性去接受从父组件中传来的数据
- 2 v-model绑定data中的属性
- 3 watch监听v-model绑定的属性值是否发生变化
父子组件的访问方式
- 父组件访问子组件,使用 c h i l d r e n 或 children或 children或refs
- 子组件访问父组件,使用$parent
- $refs 可以通过在子组件中加上ref指定名字,获取指定的子组件对象
this.$children是一个数组类型,它包含所有子组件对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../vue.js"></script>
</head>
<body>
<div id="app">
<cpn ref="bbb"></cpn>
<cpn ref="aaa"></cpn>
<button @click="testclick">点我</button>
</div>
<!-- 子组件template -->
<template id="cpntemplate">
<div>
<p>Hello world</p>
</div>
</template>
<script>
// 子组件
const cpn = {
template:"#cpntemplate",
data(){
return {
};
},
methods:{
show(){
console.log("我是子组件");
}
}
}
//父组件
var app = new Vue({
el: "#app",
data: {
},
components:{
cpn
},
methods:{
testclick(){
console.log(this.$children);
console.log(this.$refs);
console.log(this.$refs["aaa"])
}
}
})
</script>
</body>
</html>