Vue组件
创建方法
1、创建组件构造器对象Vue.extend({template:})
template自定义的组件模板
2、注册组件Vue.component(注册组件的标签名,组件构造器)
// 1、创建组件构造器对象Vue.extend({template:})
// template自定义的组件模板
const cpnC = Vue.extend({
template:`
<div>
<h2>我是小恐龙</h2>
<p>真的是小恐龙</p>
</div>`
})
// 2、注册组件Vue.component(注册组件的标签名,组件构造器)
Vue.component('my_cpn',cpnC)
全局组件
按上述方法创建的就是全局组件Vue.component(’’,…)
意味着可以在多个Vue实例下使用
ps:是可以有多个vue实例,只不过常用一个
语法糖
//全局语法糖
Vue.component('my_cpn',{
template:`
<div>
<h2>我是小恐龙</h2>
<p>真的是小恐龙</p>
</div>`
})
局部组件
在vue实例的components中写出
<script>
const cpnC = Vue.extend({
template:`
<div>
<h2>我是小恐龙</h2>
<p>真的是小恐龙</p>
</div>`
})
const app=new Vue({
el:'#app',
components:{
//my_cpn是使用组件时的标签名
my_cpn:cpnC
}
})
</script>
语法糖
const app=new Vue({
el:'#app',
components:{
//引号加不加都可以
'my_cpn':{
template:`
<div>
<h2>我是小恐龙</h2>
<p>真的是小恐龙</p>
</div>`
}
}
})
组件模板分离写法
script标签类型必须是type=“text/x-template”
<!-- 1、写在script中,用id ,注意类型:!!! type="text/x-template"-->
<script type="text/x-template" id="cpn1">
<div>
<h2>我是小恐龙</h2>
<p>真的是小恐龙</p>
</div>
</script>
template标签
<!-- 2、template标签 -->
<template id="cpn2">
<div>
<h2>我是小恐龙</h2>
<p>真的是小恐龙</p>
</div>
</template>
注册组件
Vue.component('my_cpn',{
template:'#cpn2'
})
组件不能直接访问vue实例的数据
组件除了template,还有data属性,用于存放自己使用的数据;以及methods等属性
而data属性是个函数,还需要返回一个对象,对象内部中保存着数据
使得每个实例可以维护一份被返回对象的独立的拷贝
组件通信
父传子props
1、传数组,props里写{{}}中的变量
2、传对象,可以限制数据类型
3、传对象,可以限制数据类型,还能用default提供一些默认值,required+布尔值,是否是必须传入的值
要求如果type是对象或者数组时,要用default(){}工厂函数返回默认值
default(){ return []//{} }
<div id="app">
<!-- <my_cpn v-bind:cnames="names" :csex="sex" :cmessage='message'></my_cpn> -->
<my_cpn :csex="sex" :cmessage='message'></my_cpn>
</div>
<template id='cpn'>
<div>
<h2>{{cnames}}</h2>
<ul>
<li v-for='item in cnames'>{{item}}</li>
</ul>
<p>{{csex}}</p>
<p>{{cmessage}}</p>
</div>
</template>
<script src="js/vue.js"></script>
<script>
const cpn1 = Vue.extend({
template:'#cpn',
// 1、传数组,props里写{{}}中的变量
// props:['cnames','csex','cmessage'],
// 2、传对象,可以限制数据类型
// props:{
// cnames:Array,
// csex:Array
// },
// 3、传对象,可以限制数据类型,还能用default提供一些默认值
//required+布尔值,是否是必须传入的值
props:{
cnames:
{
type:Array,
// default:['a','b'],
default(){
return ['a','b']
},
required:true
},
csex:{type:Array,default:['a','b']},
cmessage:{type:String,default:'helloVue'}
},
data(){
return {}
}
})
// 有报错信息Invalid default value for prop "cnames": Props with type Object/Array must use a factory function to return the default value.
//要求如果type是对象或者数组时,要工厂函数返回默认值
const app=new Vue({
el:'#app',
data:{
names:['royan','eddy','loudy'],
sex:['female','male'],
message:'Hello'
},
components:{
my_cpn:cpn1
}
})
</script>
驼峰表示
HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名,
如果使用字符串模板,那么这个限制就不存在了
v-bind不支持驼峰,cInfo=>c-info
一般还需要在组件模板外嵌一个标签
< template>
< div>
…模板内容
< /div>
< /template>
子传父,自定义事件
子组件发射事件,用emit触发事件
父组件同v-on监听事件
父组件模板
<template id='fcpn'>
<div>
<my_cpn @item-click="cpnclick"></my_cpn>
</div>
</template>
子组件模板
<template id='cpn'>
<div>
<button v-for="item in categories"
@click="btnClick(item)">{{item.name}}</button>
</div>
</template>
创建子组件
const cpn1 = Vue.extend({
template:'#cpn',
data(){
return {
categories:[
{id:1,name:"royan",sex:'m'},
{id:2,name:"eddy",sex:'f'},
{id:3,name:"loudy",sex:'f'},
]
}
},
methods:{
//子组件发射事件,用emit触发事件
//此处触发了item-click这个自定义事件
btnClick(item){
this.$emit('item-click',item)
}
}
})
创建父组件
const cpn2 = Vue.extend({
template:'#fcpn',
components:{
my_cpn:cpn1
},
methods:{
// 父组件用v-on监听子组件事件
//实现事件监听的内容
cpnclick(item){
console.log('cpnclick',item);
}
}
})
组件通信–双向绑定
<div id="app">
<my_cpn :number1="num1"
:number2="num2"
@num1changef="num1changez"
@num2changef="num2changez"></my_cpn>
</div>
<template id="cpn">
<div>
<h2>{{number1}}</h2>
<h2>data:{{dnumber1}}</h2>
<!-- <input type="text" v-model="dnumber1"> -->
<!-- <input type="text" :value="dnumber1" @input="dnumber1=$event.target.value">、 -->
<input type="text" :value="dnumber1" @input="num1Input">
<h2>{{number2}}</h2>
<h2>data:{{dnumber2}}</h2>
<!-- <input type="text" v-model="dnumber2"> -->
<!-- <input type="text" :value="dnumber2" @input="dnumber2=$event.target.value"> -->
<input type="text" :value="dnumber2" @input="num2Input">
</div>
</template>
<script src="js/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
num1:1,
num2:2
},
components:{
my_cpn:{
template:"#cpn",
props:{
number1:Number,
number2:Number
},
//number与dnumber仅在初始状态是一致的
//input变化=>dnumber变化,但number不会改变
data(){
return {
dnumber1:this.number1,
dnumber2:this.number2
}
},
methods:{
num1Input(event){
// 1、将input中的value赋值到dnumber中
this.dnumber1=event.target.value
// 2、为了改变input时同时修改父组件的值(让number也改变),子组件发射事件
this.$emit('num1changef',this.dnumber1)
//3、实现number2是number1的10倍
this.dnumber2=this.dnumber1*10
this.$emit('num2changef',this.dnumber2)
},
num2Input(event){
this.dnumber2=event.target.value
this.$emit('num2changef',this.dnumber2)
this.dnumber1=this.dnumber2/10
this.$emit('num1changef',this.dnumber1)
},
}
}
},
methods:{
num1changez(value){
this.num1=parseFloat(value)
},
num2changez(value){
this.num2=parseFloat(value)
}
}
})
</script>
父子组件的访问方式
父组件访问子组件$children $refs
$children
$refs
要给组件加key,通过$refs.key.属性访问
<my_cpn ref="aaa"></my_cpn>
<!-- aaa是一个key -->
console.log(this.$refs);//默认是空对象
//必须在组件上添加 ref="key"
console.log(this.$refs.aaa);
console.log(this.$refs.aaa.messages);
子访问父
$parent
$root访问vue实例
slot插槽
是组件预留的空间,使封装的组件具有扩展性,让使用者决定组件内部的一些展示内容
就可以在组件中嵌入其他元素
<my_cpn>< button>Click< /button></my_cpn>
slot插槽使组件可扩展
slot可以有默认值,但在使用时会被覆盖
hello
<!-- slot插槽使组件可扩展 -->
<div id="app">
<my_cpn>
<button>点击</button>
</my_cpn>
<my_cpn></my_cpn>
</div>
<template id="cpn">
<div>
<h2>Hi!</h2>
<p>这是一个slot演示</p>
<!-- slot中写入的是默认值,会被覆盖 -->
<slot><i>check check</i></slot>
</div>
</template>
具名插槽
spot插槽的name属性
不指定name替换没有name属性的插槽
<!-- slot插槽使组件可扩展 -->
<div id="app">
<!-- 明确替换的是具体哪一个插槽 -->
<my_cpn><button slot="s1">替换了s1</button></my_cpn>
</div>
<template id="cpn">
<div>
<h2>Hi!</h2>
<p>这是一个slot演示</p>
<!-- slot中写入的是默认值,会被覆盖 -->
<slot></slot>
<slot name="s1"><span>s1</span></slot>
<slot name="s2"><span>s2</span></slot>
<slot name="s3"><span>s3</span></slot>
</div>
</template>
作用域插槽
父组件替换插槽的标签,但内容由子组件来提供
需求:案例中的数组定义在子组件中,需要在vue实例中用不同的格式展示
方法:插槽
问题:父组件不能直接拿到子组件的data内容
解决:1、为子组件中的插槽对象指定一个属性,用属性拿到子组件中的data内容
2、固定写法,用 < template slot-scope=“slot”></ template>
<div id="app">
<my_cpn></my_cpn>
<my_cpn>
<!-- 目的是让vue实例获取子组件中的数据 -->
<template slot-scope="slot">
<!-- <span v-for="item in slot.data">{{item}}**</span> -->
<span>{{slot.data.join('*')}}</span>
</template>
</my_cpn>
</div>
<template id="cpn">
<div>
<!-- data名称可以任意,是为slot指定了一个属性,用这个属性拿到子组件中的数据 -->
<slot :data="names">
<ul>
<li v-for="item in names">{{item}}</li>
</ul>
</slot>
</div>
</template>