Vue组件化基本使用
(1)创建组件构造器对象,写上自定义组件的模板
// 1.创建组件构造器对象
const cpnContructor = Vue.extend({
// 自定义组件模板
template: `
<div>
<h2>我是组件模板</h2>
<p>我是内容</p>
</div>`
})
(2)注册组件(参数1:注册组件的标签名,参数2:创建的组件构造器名)
Vue.component('my-cpn' ,cpnContructor)
(3)使用组件
<div id="app">
<my-cpn></my-cpn>
</div>
全局组件和局部组件
用上述注册方法注册的组件为全局组件。
注册局部组件的方法如下:
(1)创建组件构造器对象,写上自定义组件模板(同上)
// 1.创建组件构造器
const cpnC = Vue.extend({
templete:`
<div>
<h2>我是局部组件</h2>
<p>我是内容</p>
</div>
`
})
(2)局部组件要在Vue实例下注册:
const vm = new Vue({
el: '#app',
data: {},
methods: {},
// 局部组件,在Vue实例下注册
components: {
// 标签名 构造器
cpn: cpnC
}
});
(3)使用方法同上
父组件和子组件
(1)创建第一个组件cpnC1
const cpnC1 = Vue.extend({
template:`
<div>
<h2>我是C1组件</h2>
<p>我是C1组件内容</p>
</div>
`
})
(2)创建第二个组件cpnC2(此组件中使用components注册了第一个组件cpnC1)
const cpnC2 = Vue.extend({
template:`
<div>
<h2>我是C2组件</h2>
<p>我是C2组件内容</p>
<cpn1></cpn1>
</div>
`,
//第二个组件构造器中包含components,注册了第一个组件
components: {
cpn1: cpnC1
}
})
(3)将第二个组件在vue实例下进行注册
// root组件
const vm = new Vue({
el: '#app',
data: {},
methods: {},
// 注册了第二个组件
components: {
cpn2: cpnC2
}
});
(4)对组件进行使用
<div id="app">
<cpn2></cpn2>
<!--cpn1是无法在这里使用的 -->
</div>
注意:此处是无法直接使用cpn1组件的,因为cpn1注册在cpn2组件下,此时我们称cpn1是cpn2的子组件。而cpn2是cpn1的父组件.
结果:
先显示了cpnC2组件中的内容,又因此C2组件中注册了C1组件,因此同样显示了C1组件的内容,注意此处显示是有先后关系的。
注册组件的语法糖写法
(1)全局组件的语法糖写法:使用 Vue.component() 直接创建和注册组件。
Vue.component('my-button', {
template: `<button>点击我</button>`
})
let app = new Vue({
el: '#app'
})
第一个参数是组件标签名称,第二个参数是一个选项对象,使用选对象的 template 属性定义组件模板。
(2)局部组件的语法糖写法:在选项对象 components 属性中注册局部组件的语法糖。
let app = new Vue({
el: '#app',
components: {
'my-button': {
template: `<button>点击我</button>`
}
}
})
组件模板抽离的写法
即便有了注册组件的语法糖写法,但是看起来还是不够清晰,为了避免这种高耦合性,可以将组件的模板单独抽离出来
(1)注册一个组件
// 1.注册一个全局组件
Vue.component('cpn' , {
template: '#cpn',
})
(2)定义template标签并为他加上id属性
<template id="cpn">
<div><h2>我是组件模板</h2></div>
</template>
组件data必须是函数
为什么组件中data必须是一个函数?
组件是需要被复用的,如果data中的数据并没有写成一个函数的话,它就不会有自己的作用域。写成函数后可以保证每一次调用这个组件中的数据,它都是新的数据,不会造成数据的混乱。
组件通信-父向子
父组件通过props向子组件传递数据 (Pass props)
<div id="app">
<!-- 2 -->
<!-- cmovies得到了movies的值,cmessage得到了message的值 -->
<cpn :c-movies="movies" :cmessage="message"></cpn>
</div>
<!-- 模板 -->
<template id="cpn">
<div>
<ul>
<!-- 3 -->
<li v-for="items in cMovies">{{items}}</li>
</ul>
</div>
</template>
<script>
const vm = new Vue({
el: '#app',
data: {
// 父组件里的data,要传到下面的子组件cpn里
message:'你好啊',
movies:['1', '2', '3']
},
methods: {},
components: {
'cpn' : {
template : '#cpn',
// 1.
props: {
// 为什么不写驼峰标识
cMovies : Array,
cmessage : String
},
data() {
return{
}
}
}
}
});
props有三种写法:
// 第一种
// props:['cmovies', 'cmessage'],
// 第二种写法
// props: {
// cmovies: Array, //验证,数据必须是一个数组
// cmessage: String,
// 第三种写法:提供一些默认值,以及必传值
props: {
cmessage: {
type : String,
// 显示默认值
default: 'aaaaaaaa' ,
// 必须要cmessage传值
required: true
},
cmovies: {
type : Array,
default: []
}
},
子向父
子组件通过事件向父组件发送消息 ($emit Events)
!-- 父组件模板 -->
<div id="app">
<!--3.监听自定义事件(子组件发射出去的事件) -->
<cpn v-on:itemclick="cpnClick"></cpn>
</div>
<!-- 子组件模板 -->
<template id="cpn">
<div>
<!-- 1.在子组件中获得用户所点击的对象 -->
<button v-for="items in categories"
@click="btnClick(items)"> <!--点击之后得到所点击的对象-->
{{items.name}}</button>
</div>
</template>
<script>
// 1.子组件:告诉父组件你点击了谁,父组件根据谁去请求对应的数据
const cpn = {
template : "#cpn",
data(){
return{
categories:[
{id: 'aaa', name: '热门推荐',},
{id: 'bbb', name: '手机数码',},
{id: 'ccc', name: '家电数码',},
{id: 'aaa', name: '电脑办公',}
]
}
},
// 定义btnClick方法,将这个自定义事件,发射给父组件
methods:{
btnClick(items){
// 把item传给父组件
// 发射
this.$emit('itemclick' , items) //发射事件名称为itemClick
}
}
}
// 2.父组件
const vm = new Vue({
el: '#app',
data: {},
methods: {},
components: {
cpn
},
methods: {
// 得到发射来的点击目标
cpnClick(items) {
console.log('cpnClick' , items)
}
}
});
父子组件通信案例
<!-- 子传父:监听子组件发射来的自定义事件 -->
<div id="app">
<cpn :cnum1="num1"
:cnum2="num2"
@num1change="num1change"
@num2change="num2change"/>
</div>
<!-- 模板 -->
<template id="cpn">
<div>
<h2>props:{{cnum1}}</h2>
<h2>data:{{dnumber1}}</h2>
<!-- v-model绑定到了props属性上,不要这样做 -->
<!-- <input type="text" v-model="dnumber1"> -->
<input type="text" v-bind:value="dnumber1" @input="num1Input">
<h2>props:{{cnum2}}</h2>
<h2>data:{{dnumber2}}</h2>
<!-- 绑定要绑定到data函数里 -->
<!-- <input type="text" v-model="dnumber2"> -->
<!-- 监听 -->
<input type="text" v-bind:value="dnumber2" @input="num2Input">
</div>
</template>
<script>
const vm = new Vue({
el: '#app',
data: {
num1: 1,
num2: 0
},
methods: {
// 得到点击而来的value,默认传过来是string类型
num1change(value) {
this.num1 = parseFloat(value)
},
num2change(value) {
this.num2 = parseFloat(value)
}
},
// 注册子组件
components :{
cpn: {
template: '#cpn',
props : {
cnum1 : {
type: Number,
},
cnum2 : {
type: Number
}
},
data () {
return {
dnumber1 : this.cnum1,
dnumber2 : this.cnum2
}
},
methods: {
num1Input (event) {
// 1.将input中的value赋值到dnumber中
this.dnumber1= event.target.value
// 2.为了让父组件可以修改值,发出一个自定义事件
this.$emit('num1change' , this.dnumber1)
// 3.同时修改dnumber2的值
this.dnumber2 = this.dnumber1 * 100
this.$emit('num2change' , this.dnumber2)
},
num2Input (event) {
this.dnumber2= event.target.value
this.$emit('num2change' , this.dnumber2)
// 3.同时修改dnumber1的值
this.dnumber1 = this.dnumber2 / 100
this.$emit('num1change' , this.dnumber1)
}
}
}
},
})
</script>
组件访问-父组件访问子组件children{refs}
父组件直接调用子组件里的方法
<div id="app">
<cpn></cpn>
<cpn></cpn>
<!-- ref -->
<cpn ref="aaa"></cpn>
<button @click="btnClick">案例</button>
</div>
<!-- 模板 -->
<template id="cpn">
<div>
我是子组件
</div>
</template>
<script>
const vm = new Vue({
el: '#app',
data: {},
methods: {
btnClick() {
// 1.$children
// console.log(this.$children)
// this.$children[0].showMessage
// 2.$refs => 对象类型,默认是一个空的对象
console.log(this.$refs.aaa.showMessage)
}
},
components: {
cpn: {
template: '#cpn',
methods: {
showMessage() {
console.log('showMessage')
}
}
}
}
});
</script>
组件访问-子访问父-parent-root
<div id="app">
<cpn></cpn>
</div>
<!-- 模板 -->
<template id="cpn">
<div>
<h2>我是cpn组件</h2>
<ccpn></ccpn>
</div>
</template>
<template id="ccpn">
<div>
<h2>我是子组件</h2>
<button @click="btnClick">按钮</button>
</div>
</template>
<script>
const vm = new Vue({
el: '#app',
data: {
message: '你好啊'
},
components: {
cpn: {
template: '#cpn',
data() {
return {
name: '我是cpn组件的name'
}
},
components: {
ccpn: {
template: '#ccpn',
methods: {
btnClick() {
// 1.访问父组件
console.log(this.$parent.name)
// 2.访问跟组件$root
console.log(this.$root);
console.log(this.$root,message);
}
},
}
}
}
}
});
</script>
组件化开发高级—插槽的使用
1.插槽的基本使用 <slot></slot> 2.插槽的默认值 <slot>button</slot> 3.如果有多个值,同时放入到组件进行替换时,一起作为替换元素
(1)插槽的基本使用
<div id="app">
<cpn><button>按钮</button></cpn>
<cpn><span>1</span></cpn>
<cpn><i>2</i></cpn>
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>我是组件</h2>
<p>我是组件,哈</p>
<!-- 插槽 -->
<!-- 设定默认值 -->
<slot><button>按钮</button></slot>
</div>
</template>
<script>
const vm = new Vue({
el: '#app',
data: {},
methods: {},
components: {
cpn : {
template: '#cpn'
}
}
});
(2)slot具名插槽的使用
<div id="app">
<cpn><span slot="center">标题</span></cpn>
</div>
<template id="cpn">
<div>
<slot name="left"><span>左边</span></slot>
<slot name="center"><span>中间</span></slot>
<slot name="left"><span>右边</span></slot>
</div>
</template>
<script>
const vm = new Vue({
el: '#app',
data: {},
methods: {},
components: {
cpn : {
template: '#cpn'
}
}
});
</script>
(2)作用域插槽的使用
作用域插槽的主要作用就是:可以换一种展示同种数据的方法。
<div id="app">
<cpn></cpn>
<!-- 换一种方法展示数据 -->
<cpn>
<!-- 获取子组件中的pLanguages -->
<template slot-scope="slot">
<span v-for="items in slot.data">{{items}}--</span>
</template>
</cpn>
</div>
<template id="cpn">
<div>
<!-- 插槽 -->
<!-- 指向pLanguages -->
<slot :data="pLanguages">
<ul>
<li v-for="items in pLanguages">{{items}}</li>
</ul>
</slot>
</div>
</template>
<script>
const vm = new Vue({
el: '#app',
data: {
},
methods: {},
components: {
cpn : {
template: '#cpn',
data() {
return {
pLanguages:['JavaScript','C++','Java','Python']
}
}
}
}
});
</script>
结果如下: