组件的构成
组件模块化是Vue的渐进式框架的特点,了解组件的构成是迈开模块化开发的第一步,组件分为全局组件和局部组件。
- 全局组件:
<body>
<div id="app">
<cpn></cpn>
</div>
</body>
`//全局组件
const cpn1 = Vue.extend({
template:`<div>
<h1>我是全局标题1</h1>
<p>我是全局内容1</p>
</div>`,
})`
// 注册全局组件,可以全局使用
Vue.component("cpn",cpn1)
- 局部组件
<body>
<div id="app">
<cpn1></cpn1>
</div>
</body>
const app = new Vue({
el:"#app",
// 局部组件
components:{
cpn1:{
template:`<div>
<h1>我是局部组件标题1</h1>
<p>我是局部内容1</p>
</div>`
}
},
})
父子组件的区分
存在组件就会有组件的嵌套,就会上下级的关系,就是父组件,和子组件
//第一个组件构造器
const cpn1 = Vue.extend({
template:`<div>
<h1>我是第一个标题</h1>
<p>我是第一个内容</p>
</div>`,
})
//第二个组件构造器
const cpn2 = Vue.extend({
template:`<div>
<h1>我是第二个标题</h1>
<cpn1></cpn1>
<p>我是第二个内容</p>
</div>`,
components:{
cpn1:cpn1
}
})
父子组件的通信
- 父传子(驼峰命名的问题,props属性直接传递)
- 子传父(对象方式)
<!--
父传子,通过props传递
子传父,自定义事件
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<!-- 父组件模板 -->
<div id="app">
<h1>父组件</h1>
<Child :cmovies="movies" :cmessage="message" :cactors="actors"></Child>
<Child2></Child2>
<!-- 子传父 -->
<Cpn @item-click="cpnClick"></Cpn>
</div>
</body>
<!-- 子组件模板 -->
<template id="child">
<div>
<h2>父组件传值的子组件</h2>
<p>父传子后cmessage:{{cmessage}}</p>
<ul>
<li v-for="item in cmovies">{{item}}</li>
</ul>
父传子后cactors:演员名
<ul>
<li v-for="items in cactors">{{items}}</li>
</ul>
</div>
</template>
<!-- 子传父模板 -->
<template id="cpn">
<div>
<button v-for="item in Lists"
@click="cpnClick(item)"
>{{item.name}}</button>
</div>
</template>
<script>
const Cpn = {
template:"#cpn",
data(){
return{
Lists:[
{id:0,name:'热门推荐'},
{id:1,name:'手机数码'},
{id:2,name:'电脑科技'},
{id:3,name:'家电设备'},
]
}
},
methods:{
cpnClick(item){
// 子传父的方法,使用$emit('定义方法名')
this.$emit("item-click",item)
}
}
}
//构建子组件
const Child = {
template:'#child',
// props:["cmovies","cmessage"],
props:{
cmovies:{
type:Array,
// 如果类型是数组或者对象,default必须是个函数,返回一个默认值
default(){
return["未上映电影!"]
},
// true:使用时值必须传值,不传就报错;fales:使用时可传可不传
required:false
},
cmessage:{
type:String,
// 默认值(string)
default:"你好!",
// true:使用时值必须传值,不传就报错;fales:使用时可传可不传
required:false
},
cactors:{
type:Object,
default(){
return{
}
}
}
},
// 这里的data必须是函数,且有返回值
data(){
return{
}
}
}
// 父组件
const app = new Vue({
el:"#app",
data:{
message:'蜘蛛侠电影集',
movies:["蜘蛛侠",'蜘蛛侠2','蜘蛛侠3','超凡蜘蛛侠','超凡蜘蛛侠2','蜘蛛侠:英雄归来-返校季','蜘蛛侠:英雄远征'],
actors:{
name:'托比马奎尔',
name1:'荷兰弟'
}
},
methods:{
// 子传父方法
cpnClick(item){
// 父组件接收到子组件传值
console.log( item.id,item.name )
}
},
components:{
Child,
Cpn,
Child2:{
template:`<div>
<h2>父组件不传值显示默认值的子组件2</h2>
<p>父未传子显示默认值的bmessage:{{bmessage}}</p>
<p>父未传子显示默认值的bmessage</p>
<ul>
<li v-for="item in bmovies">{{item}}</li>
</ul>
<ul>
<li v-for="items in bactors">{{items}}</li>
</ul>
</div> `,
props:{
bmovies:{
type:Array,
// 如果类型是数组或者对象,default必须是个函数,返回一个默认值
default(){
return["未上映电影!"]
},
// true:使用时值必须传值,不传就报错;fales:使用时可传可不传
required:false
},
bmessage:{
type:String,
// 默认值(string)
default:"你好!",
// true:使用时值必须传值,不传就报错;fales:使用时可传可不传
required:false
},
bactors:{
type:Object,
default(){
return{
name:'尚未演员0',
name1:'尚未演员1'
}
}
}
}
}
}
})
</script>
</html>
- 父子传递双向绑定案例
基础普通写法
<!--
需求:
1.使用input的双向绑定实现修改子组件的值更改父组件的值
2.在修改num时,num1的值为num值的100倍
3.在修改num1时,num的值为num1的0.01倍
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<!-- 父组件模板 -->
<div id="app">
<h1>number:{{number}}</h1>
<h1>number1:{{number1}}</h1>
<cpn :cnumber="number" :cnumber1="number1"
@changenum="childNum"
@changenum1="childNum1"
/>
</div>
</body>
<!-- 子组件模板 -->
<template id="child">
<div>
<h1>Props-Number:{{cnumber}}</h1>
<h1>data-Number:{{bnumber}}</h1>
<!-- 使用v-model实现了在子组件中的双向绑定,但不能改变父组件的值,需要通过watch方法才可以实现 -->
<input type="text" :value="bnumber" @Input="changeNum($event)">
<h1>Props-Number1:{{cnumber1}}</h1>
<h1>data-Number1:{{bnumber1}}</h1>
<input type="text" :value="bnumber1" @Input="changeNum1($event)">
</div>
</template>
<script>
const app = new Vue({
el:"#app",
data:{
number:1,
number1:2
},
methods:{
childNum(e){
// console.log(e)一般过传过来的参数都是strin类型,所以需要转换一下
this.number = parseFloat(e)
},
childNum1(e){
// console.log(e)一般过传过来的参数都是strin类型,所以需要转换一下
this.number1 = parseFloat(e)
},
},
components:{
// 在修改父组件传给子组件的值时,不要直接修改,要通过data或者computeds修改
cpn:{
template:"#child",
props:{
cnumber:{
type:Number,
default:'none',
},
cnumber1:{
type:Number,
default:'none',
}
},
data(){
return{
bnumber:this.cnumber,
bnumber1:this.cnumber1
}
},
methods:{
changeNum(event){
// console.log( event.target.value )获取到input框的值
this.bnumber = event.target.value;
// 将子组件修改的值传给父组件中
this.$emit( "changenum",this.bnumber )
// 修改关联值,并再次传给父组件(我这里一开始写错了,又写一个方法,其实传第二个值直接用第二个方法就行了)
this.bnumber1 = this.bnumber*100
this.$emit( "changenum1",this.bnumber1 )
},
changeNum1(event){
// console.log( event.target.value )获取到input框的值
this.bnumber1 = event.target.value;
// 将子组件修改的值传给父组件中(我这里一开始写错了,又写一个方法,其实传第一个值直接用第一个方法就行了)
this.$emit( "changenum1",this.bnumber1 )
// 修改关联值,并再次传给父组件
this.bnumber = this.bnumber1/100
this.$emit( "changenum",this.bnumber )
}
},
// 组件的一个属性,可以省略通过改变子组件的data来传递给父组件
}
}
})
</script>
</html>
使用watch属性写法:
<!--
需求:
1.使用input的双向绑定实现修改子组件的值更改父组件的值
2.在修改num时,num1的值为num值的100倍
3.在修改num1时,num的值为num1的0.01倍
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<!-- 父组件模板 -->
<div id="app">
<h1>number:{{number}}</h1>
<h1>number1:{{number1}}</h1>
<cpn :cnumber="number" :cnumber1="number1"
@changenum="childNum"
@changenum1="childNum1"
/>
</div>
</body>
<!-- 子组件模板 -->
<template id="child">
<div>
<h1>Props-Number:{{cnumber}}</h1>
<h1>data-Number:{{bnumber}}</h1>
<!-- 使用v-model实现了在子组件中的双向绑定,但不能改变父组件的值,需要通过watch方法才可以实现 -->
<input type="text" v-model="bnumber">
<h1>Props-Number1:{{cnumber1}}</h1>
<h1>data-Number1:{{bnumber1}}</h1>
<input type="text" v-model="bnumber1">
</div>
</template>
<script>
const app = new Vue({
el:"#app",
data:{
number:1,
number1:2
},
methods:{
childNum(e){
// console.log(e)一般过传过来的参数都是strin类型,所以需要转换一下
this.number = parseFloat(e)
},
childNum1(e){
// console.log(e)一般过传过来的参数都是strin类型,所以需要转换一下
this.number1 = parseFloat(e)
},
},
components:{
// 在修改父组件传给子组件的值时,不要直接修改,要通过data或者computeds修改
cpn:{
template:"#child",
props:{
cnumber:{
type:Number,
default:'none',
},
cnumber1:{
type:Number,
default:'none',
}
},
data(){
return{
bnumber:this.cnumber,
bnumber1:this.cnumber1
}
},
methods:{
changeNum(event){
// console.log( event.target.value )获取到input框的值
this.bnumber = event.target.value;
// 将子组件修改的值传给父组件中
this.$emit( "changenum",this.bnumber )
// 修改关联值,并再次传给父组件(我这里一开始写错了,又写一个方法,其实传第二个值直接用第二个方法就行了)
this.bnumber1 = this.bnumber*100
this.$emit( "changenum1",this.bnumber1 )
},
changeNum1(event){
// console.log( event.target.value )获取到input框的值
this.bnumber1 = event.target.value;
// 将子组件修改的值传给父组件中(我这里一开始写错了,又写一个方法,其实传第一个值直接用第一个方法就行了)
this.$emit( "changenum1",this.bnumber1 )
// 修改关联值,并再次传给父组件
this.bnumber = this.bnumber1/100
this.$emit( "changenum",this.bnumber )
}
},
// 组件的一个属性,可以省略通过改变子组件的data来传递给父组件
watch:{
bnumber(newVaule){
// console.log( "newVaule:"+newVaule )
this.bnumber1 = newVaule*100
this.$emit( "changenum1",this.bnumber1 )
},
bnumber1(newVaule){
// console.log( "newVaule:"+newVaule )
this.bnumber = this.bnumber1/100
this.$emit( "changenum",this.bnumber )
}
}
}
}
})
</script>
</html>
- 父组件拿到子组件的方法($ children 或者$ refs ),或者子组件拿父组件的方法($ parent或者$ root),需要注意的细节都写到代码注释里。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<cpn></cpn>
<cpn></cpn>
<cpn ref="getChild"></cpn>
<button @click="btnClick">按钮</button>
<h2>展示子组件的data值:{{childrenVaule}}</h2>
</div>
<template id="cpn">
<h1>我是子组件</h1>
</template>
</body>
<script>
var app = new Vue({
el:'#app',
data:{
childrenVaule:""
},
methods:{
btnClick(){
// 调取子组件方法 $children $refs,由于this.$children是一个数组,所以如果组件
// 数量多,则不好选择,所以一般使用$refs方法,只用当需要拿到所有子组件的值的时候,才会
// 使用$children的方法
// console.log( this.$children )
// 在父组件上调取了子组件的children方法
// this.$children[0].showMessage()
//在父组件上调取了子组件的refs方法(推荐使用)也可以拿到子组件的data值:
console.log( this.$refs )
this.$refs.getChild.showMessage()
this.childrenVaule = this.$refs.getChild.name
}
},
components:{
cpn:{
template:"#cpn",
data(){
return{
name:"子组件的name"
}
},
methods:{
showMessage(){
console.log( "showMessage" )
}
}
}
}
})
</script>
</html>