组件化的意义
什么是组件化:将一个大问题拆解为一个一个小问题,去解决一个一个的小问题,当解决完所有小问题,那么大问题也就解决了。
比如这个页面,可以先将这个大页面分为2部分,上部分导航栏,下部分显示内容。页面上部分的导航栏,有6个功能不同的按钮,可以看成6个组件,6个组件组成导航栏,然后导航栏和下部分组成整个页面。这里的组件都是可以复用。
组件化优势:
1.高内聚低耦合,拆分项目,团队分工,加快进展;
2.组件可复用,方便维护,方便阅读;
组件的基本使用
组件的使用主要是3步:
1.创建组件构造器:Vue.extend。
2.注册组件:Vue.component。
3.使用组件。
这是组件最原始的写法,看着比较繁琐:
<body>
<div id="app">
<!--3.使用组件-->
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<div>
<div>
<my-cpn></my-cpn>
</div>
</div>
</div>
<my-cpn></my-cpn>
<script>
// 1.创建组件构造器对象
const cpnC = Vue.extend({
template: `
<div>
<h2>你好</h2>
<p>今天天气很好</p>
</div>`
})
// 2.注册组件
Vue.component('my-cpn', cpnC)
const app = new Vue({
el: '#app',
data: {
message: 'hi'
}
})
</script>
</body>
组件分2种:
全局组件(全局可以用)和局部组件(只能在实例的vue里面用,这里只能在app块里面使用,app2块里面无法使用):
<body>
<div id="app">
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<div id="app2">
<cpn></cpn>
</div>
<script>
// 1.创建组件构造器
const cpnC = Vue.extend({
template: `
<div>
<h2>你好</h2>
<p>今天天气不错</p>
</div>
`
})
// 2.注册组件(全局组件, 意味着可以在多个Vue的实例下面使用)
// Vue.component('cpn', cpnC)
const app = new Vue({
el: '#app',
data: {
message: 'hi'
},
components: {
// cpn使用组件时的标签名
cpn: cpnC
}
})
const app2 = new Vue({
el: '#app2'
})
</script>
</body>
语法糖形式写法:
<body>
<div id="app">
<cpn1></cpn1>
<cpn2></cpn2>
</div>
<script>
// 1.全局组件注册的语法糖
// 1.创建组件构造器
// const cpn1 = Vue.extend()
// 2.注册组件
Vue.component('cpn1', {
template: `
<div>
<h2>你好</h2>
<p>今天天气不错</p>
</div>
`
})
// 2.注册局部组件的语法糖
const app = new Vue({
el: '#app',
data: {
message: 'hi'
},
components: {
'cpn2': {
template: `
<div>
<h2>你好2</h2>
<p>今天天气不错</p>
</div>
`
}
}
})
</script>
</body>
父子组件
可以理解为与继承相反的,大的包含小的叫父,小的叫子,这里cpnC1是子,cpnC2叫父:
<body>
<div id="app">
<cpn2></cpn2>
<!--<cpn1></cpn1>-->
</div>
<script src="../js/vue.js"></script>
<script>
// 1.创建第一个组件构造器(子组件)
const cpnC1 = Vue.extend({
template: `
<div>
<h2>你好</h2>
<p>今天天气不错</p>
</div>
`
})
// 2.创建第二个组件构造器(父组件)
const cpnC2 = Vue.extend({
template: `
<div>
<h2>你好2</h2>
<p>今天天气不错</p>
<cpn1></cpn1>
</div>
`,
components: {
cpn1: cpnC1
}
})
// root组件
const app = new Vue({
el: '#app',
data: {
message: 'hi'
},
components: {
cpn2: cpnC2
}
})
</script>
</body>
剥离组件代码
模块代码和js写在一起,看着很混乱,所以这里将模板剥离开来写,这里可以用script type=“text/x-template” id="“标签和template id=”"(id在注册的时候会用到)标签来写,常用第二种。
<body>
<div id="app">
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<!--1.script标签, 注意:类型必须是text/x-template-->
<!--<script type="text/x-template" id="cpn">-->
<!--<div>-->
<!--<h2>你好</h2>-->
<!--<p>今天天气不错</p>-->
<!--</div>-->
<!--</script>-->
<!--2.template标签-->
<template id="cpn">
<div>
<h2>你好</h2>
<p>今天天气不错</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// 1.注册一个全局组件
Vue.component('cpn', {
template: '#cpn'
})
const app = new Vue({
el: '#app',
data: {
message: 'hi'
}
})
</script>
</body>
组件数据
这里关于组件模块数据存放有几个注意:
1.不能直接从实例Vue(也就是app的data)里面取数据。
2.要从实例Vue或者其他模块里面取数据,涉及到父子组件通信问题,后面讲。
3.只能直接取用自己模块里面的data数据。
4.模块自己的data不是一个对象,而是一个有返回值的函数。
5.rops中的驼峰标识,在html中会报错,用-小写代替大写的写法,代替驼峰标识。my-cpn(myCpn)
<body>
<div id="app">
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>{{title}}</h2>
<p>今天天气不错</p>
</div>
</template>
<script>
Vue.component('cpn', {
template: '#cpn',
data() {
return {
title: 'abc'
}
}
})
const app = new Vue({
el: '#app',
data: {
message: '你好',
// title: '标题'
}
})
</script>
</body>
为什么模块里面的数据一定要是一个函数,而不是对象?
组件是可复用的vue实例,一个组件被创建好之后,就可能被用在各个地方,而组件不管被复用了多少次,组件中的data数据都应该是相互隔离,互不影响的,每次使用,都应该是新的数据,基于这一理念,组件每复用一次,data数据就应该被复制一次,之后,当某一处复用的地方组件内data数据被改变时,其他复用地方组件的data数据不受影响,这正好是函数的特性。
组件通信父传子
在子组件中定义属性props存放就收来的信息,再用v-bind去接收父组件的消息放在定义好的props里面。
<body>
<div id="app">
<!--<cpn v-bind:cmovies="movies"></cpn>-->
<!--<cpn cmovies="movies" cmessage="message"></cpn>-->
<cpn :cmessage="message" :cmovies="movies"></cpn>
</div>
<template id="cpn">
<div>
<ul>
<li v-for="item in cmovies">{{item}}</li>
</ul>
<h2>{{cmessage}}</h2>
</div>
</template>
<script>
// 父传子: props
const cpn = {
template: '#cpn',
// props: ['cmovies', 'cmessage'],
props: {
// 1.类型限制
// cmovies: Array,
// cmessage: String,
// 2.提供一些默认值, 以及必传值
cmessage: {
type: String,
default: 'abcd',
required: true
},
// 类型是对象或者数组时, 默认值必须是一个函数
cmovies: {
type: Array,
default() {
return []
}
}
},
data() {
return {}
},
methods: {
}
}
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
movies: ['海贼王', '火影', '死神']
},
components: {
cpn
}
})
</script>
组件通信子传父
子发射事件,并携带信息传给父组件,父组件用V-on去响应子组件的发射事件,并接收信息。
<body>
<div id="app">
<cpn @item-click="Total"></cpn>
<h1>点击总次数:{{total}}</h1>
</div>
</body>
<template id="cpnC">
<div>
<button @click="btnClickAdd">+</button>
<button @click="btnClickSup">-</button>
<h1>按钮显示数:{{showCount}}</h1>
</div>
</template>
<script>
const cpn = {
template:'#cpnC',
data(){
return{
count:0,
showCount:0,
}
},
methods:{
btnClickAdd(){
// 发射事件: 自定义事件
this.count++;
this.showCount++;
console.log(this.count);
this.$emit('item-click', this.count)
},
btnClickSup(){
// 发射事件: 自定义事件
this.count++;
this.showCount--;
console.log(this.count);
this.$emit('item-click', this.count)
}
}
}
Vue.component('cpn',cpn)
const app = new Vue({
el:'#app',
data:{
message:"你好",
total:0,
},
methods: {
Total(total){
this.total = total;
}
}
})
</script>