组件化
组件化的实现和使用步骤
组件化:
如果我们将一个页面所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利用后续的管理以及扩展
但如果我们将整个页面拆分为一个个小的功能块,每个功能块完成属于自己这部分的功能,那么之后整个页面的管理和维护就简易多了
组件化是是Vue.js的重要思想:
□ 它提供了一种抽象,让我们可以开发出一个个复用的小组件来构造应用
□ 任何的应用都会被抽象成组件树
1.1 注册组件的基本步骤
组件的使用分为三个步骤:
1.创建组件构造器 (调用Vue.extend()方法创建组件构造器)
2.注册组件 (调用Vue.component()方法注册组件)
3.使用组件 (在Vue实例的作用范围内使用组件)
■ 1.Vue.extend():
调用Vue.extend()创建一个组件构造器
在构建组件构造器时,传入template代表我们自定义组件的模板
该模板是在使用到组件的 地方要显示的HTML代码
■ 2.Vue.component():
调用Vue.component()是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称。
所以需要传递两个参数:1、注册组件的标签名 2、组件构造器
■ 3.组件必须挂载在某个Vue实例下,否则它不会生效。
1.2 组件化的基本使用
组件名不能用myCpn类似名
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<my-cpn></my-cpn>
</div>
<script src="/js/vue.js"></script>
<script>
// 创建组件构造器
const cpnC = Vue.extend({
template: `
<div>
<h2>我是标题</h2>
<p>我是内容, 哈哈哈哈</p>
<p>我是内容, 呵呵呵呵</p>
</div>`
})
// 注册组件
Vue.component('my-cpn', cpnC)
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
}
})
</script>
</body>
</html>
全局组件和局部组件
全局组件
当我们通过Vue.component()注册组件时,组件的注册是全局的
意味着该组件可以在任何Vue示例下使用
局部组件
如果我们创建的组件是挂载到某个实例上,组件的注册是局部的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<my-cpn></my-cpn>
</div>
<div id="app2">
<my-cpn></my-cpn>
</div>
<script src="/js/vue.js"></script>
<script>
// 构建组件构造器
const cpnC = Vue.extend({
template: `
<div>
<p>哈哈哈</p>
<h4>i am good</h4>
</div>
`
})
// 注册组件(全局组件)
// Vue.component('my-cpn',cpnC)
const app = new Vue({
el: '#app',
// 局部组件
components: {
'my-cpn': cpnC
}
})
const app2 = new Vue({
el: '#app2'
})
</script>
</body>
</html>
父组件和子组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn1></cpn1>
<cpn2></cpn2>
</div>
<script src="/js/vue.js"></script>
<script>
// 创建组件构造器
const cpnC1 = Vue.extend({
template: `
<div>
<p>这里显示的是:11111</p>
<p>这里显示的是:22222</p>
</div>
`
})
const cpnC2 = Vue.extend({
template:`
<div>
<h4>cpn2显示</h4>
<p>这里显示:444</p>
<cpn1></cpn1>
</div>
`,
component: {
cpn1: cpnC1,
}
})
// 注册组件
Vue.component('cpn1',cpnC1)
const app = new Vue({
el: "#app",
components: {
cpn2: cpnC2
}
})
</script>
</body>
</html>
注册组件语法糖
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<my-cpn></my-cpn>
</div>
<script src="/js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
components: {
"my-cpn": {
template: `
<div>
<p>辣辣辣</p>
<p>甜甜甜</p>
</div>
`
}
}
})
</script>
</body>
</html>
组件模块抽离方法
将HTML分离出来,挂载到对应组件上
有两种方式,一种用script标签,一种用template标签
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn></cpn>
</div>
<!-- 1.使用<script>标签 -->
<!-- <script type="x-template" id="cpn1">
<div>
<p>你的名字是</p>
<h4>吕狗蛋</h4>
</div>
</script> -->
<!-- 2.使用template标签 -->
<template id="cpn1">
<div>
<p>你的名字是</p>
<h4>吕狗蛋</h4>
</div>
</template>
<script src="/js/vue.js"></script>
<script>
Vue.component('cpn',{
template: "#cpn1"
})
const app = new Vue({
el: "#app"
})
</script>
</body>
</html>
组件数据的存放
组件是一个单独封装的模块,这个模块有属于自己的HTML模板,也有属于自己的数据data
在组件中,不能直接访问Vue实例中的data
Vue组件应该有保存自己数据的地方
■ 组件数据的存放:
□ 组件对象有一个data属性,也有methods等属性
□ data属性必须是函数
□ 函数返回一个对象,对象中保存着数据
数据存放在组建的data中
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn></cpn>
</div>
<!-- 1.使用<script>标签 -->
<!-- <script type="x-template" id="cpn1">
<div>
<p>你的名字是</p>
<h4>吕狗蛋</h4>
</div>
</script> -->
<!-- 2.使用template标签 -->
<template id="cpn1">
<div>
<p>你的名字是</p>
<h4>吕狗蛋</h4>
<h5>{{message}}</h5>
</div>
</template>
<script src="/js/vue.js"></script>
<script>
Vue.component('cpn',{
template: "#cpn1",
// data函数
data() {
return {
message: "lyf"
}
}
})
const app = new Vue({
el: "#app"
})
</script>
</body>
</html>
组件中的data为什么必须是函数?
■ 首先,如果不是一个函数,Vue直接就会报错。
■ 其次,原因是在于Vue让每个组件对象都返回一个新的对象,因为如果是同一个对象的,组件在多次使用后会相互影响。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn></cpn>
</div>
<script src="/js/vue.js"></script>
<template id="cpn1">
<div>
<p>是我想太多</p>
<p>我总这样说</p>
<p>而你却没有</p>
<p>真的心疼我</p>
</div>
<!-- 计算器 -->
<h4>{{number}}</h4>
<button @click="decrement">-</button>
<button @click="increment">+</button>
</template>
<script>
Vue.component('cpn',{
template: "#cpn1",
data() {
return {
number: 0
}
},
methods: {
decrement() {
this.number--
},
increment() {
this.number++
}
}
})
const app = new Vue({
el: "#app"
})
</script>
</body>
</html>
父子组件的通信
子组件是不能引用父组件或者Vue实例的数据的
但是,在开发中,往往一些数据确实需要从上层传递到下层:
比如在一个页面中,我们从服务器请求到了很多的数据。
其中一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示。
这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件)将数据传递给小组件(子组件)
父子组件通信方式:
1.通过props向子组件传递信息
2.通过事件向父组件传递消息
父传子props驼峰标识
props下的对象需用驼峰写法,v-bind不能使用驼峰命名
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- v-bind不能使用驼峰命名 -->
<cpn :c-info="info"></cpn>
</div>
<template id="cpn">
<h3>{{cInfo}}</h3>
<!-- <div>
<p>zzy</p>
<p>my love</p>
</div> -->
</template>
<script src="/js/vue.js"></script>
<script>
const cpn = {
template: "#cpn",
props: {
cInfo: {
type: Object,
default() {
return {}
}
}
}
}
const app = new Vue({
el: "#app",
data: {
info: {
name: 'zzy',
age: 18,
hobby: 'shopping'
}
},
components: {
cpn
}
})
</script>
</body>
</html>
子传父
子组件传递数据或事件到父组件中,我们需要用自定义事件完成
何时需要 自定义事件:
当子组件需要向父组件传递数据时,就可以用到自定义事件
v-on不仅可以用来监听DOM事件,也可以用于组件间的自定义事件
自定义事件的流程:
在子组件中,通过$emit()来触发事件
在父组件中,通过v-on来监听子组件事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn @item-click="cpnClick"></cpn>
</div>
<template id="cpn">
<div>
<button v-for="item in categories" @click=btnClick(item)>{{item.name}}</button>
</div>
</template>
<script src="/js/vue.js"></script>
<script>
const cpn = {
template: "#cpn",
data() {
return {
categories: [
{id:"aaa", name:"热门推荐"},
{id:"bbb", name:"手机数码"},
{id:"ccc", name:"家用电器"},
{id:"ddd", name:"电脑办公"},
]
}
},
methods: {
btnClick(item) {
this.$emit('item-click',item)
}
}
}
const app = new Vue({
el: "#app",
data: {
message: ''
},
components: {
cpn
},
methods: {
cpnClick() {
console.log('cpnClick',item);
}
}
})
</script>
</body>
</html>
父子组件通信—结合双向绑定案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn :number1="num1" :number2="num2" @num1change="num1change" @num2change="num2change"/>
</div>
<template id="cpn">
<div>
<p>props: {{number1}}</p>
<p>data: {{dnumber1}}</p>
<input type="text" :value="dnumber1" @input="num1Input">
<p>props: {{number2}}</p>
<p>data: {{dnumber2}}</p>
<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: 0
},
methods: {
num1change(value) {
this.num1 = parseFloat(value)
},
num2change(value){
this.num2 = parseFloat(value)
}
},
components: {
cpn: {
template: "#cpn",
props: {
// 确定类型
number1: Number,
number2: Number
},
data() {
return {
dnumber1: this.number1,
dnumber2: this.number2
}
},
methods: {
num1Input(event) {
// 将input中的value值赋值到dnumber中
this.dnumber1 = event.target.value;
// 为了让父组件的值可以修改,发出一个事件
this.$emit('num1change',this.dnumber1)
// 让number2的值对应改变
this.dnumber2 = this.dnumber1*100
this.$emit('num2change',this.dnumber2)
},
num2Input(event) {
this.dnumber2 = event.target.value;
this.$emit('num2change',this.dnumber2)
this.dnumber1 = this.dnumber2 / 100
this.$emit('num1change',this.dnumber1)
}
}
}
}
})
</script>
</body>
</html>
父子组件访问
父访问子 children-refs
$children
$refs 引用