Vue 组件化开发
案例式驱动,每个案例一个小例子,带领大家领略
一、组件注册
1.1 全局组件
基本语法:
<script>
Vue.component('组件名称', {
data: '组件数据',
template: '组件模板内容(HTML 标签)'
})
</script>
示例:
我们以一个点击按钮数据累加计算来演示组件的使用
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>组件化开发</title>
</head>
<body>
<div id="app">
<!-- 每个组件的都是相互独立的 -->
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>
<script src="../vue.js"></script>
<script>
// Vue.component('button-counter' 两种方式
Vue.component('ButtonCounter', {
// 使用函数,形成一个闭包的环境,保证每个数据都是独立的。
data() {
return {
count: 0
}
},
// template: '<button v-on:click="count++">点击了{{count}}次</button>',
template: '<button @click="handle">点击了{{count}}次</button>',
methods: {
handle: function() {
this.count++;
},
}
})
var app = new Vue({
el: "#app",
data: {
}
})
</script>
</body>
</html>
1.2 局部组件
我们在 Vue 实例中添加 components 属性,在这里我们就可以自定义自己的局部组件,使用方式和全局组件的差别不大
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>局部组件化开发</title>
</head>
<body>
<div id="app">
<component-a></component-a>
</div>
<!--
局部组件注册
局部组件只能在注册他的父组件中使用
-->
<script src="../vue.js"></script>
<script>
let ComponetA = {
data() {
return {
msg: 'Hello World'
}
},
template: '<div>{{msg}}</div>',
methods: {
}
}
var app = new Vue({
el:"#app",
data:{
},
components: {
'component-a': ComponetA
}
})
</script>
</body>
</html>
演示效果
1.3 组件注册基本注意事项
注册基本事项:
- data 必须是一个函数
- 组件模板内容必须是单个根元素
- 组件模板内容可以是模板字符串 ES6语法
- 如果使用驼峰式命名组件,要么在使用组件得时候,只能在字符串模板中用驼峰得方式使用组件
但是在普通得模板标签中,必须使用横线的方式使用组件
1.4 props 属性值类型
- 字符串 String
- 数值 Number
- 布尔值 Boolean
- 数组 Array
- 对象 Object
二、组件之间数据交互
2.1 父组件向子组件传递数据
- 父组件向子组件传递数据,分为静态数据 和 动态数据(属性绑定实现)
- 子组件接收父组件传递过来的内容,要通过 props 来接收父组件的数据
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>组件数据传递</title>
</head>
<body>
<div id="app">
<!--
1. 组件内部通过 props 接收传递过来的值
2. 父组件通过属性传值给子组件
3. props 属性名规则
在 props 中使用驼峰形式,模板中需要使用短横线的形式
字符串形式的模板中没有这个限制
-->
<h3>父组件向子组件传递数据</h3>
<div>
{{pmsg}}
</div>
<!-- 父组件向子组件传递数据 -->
<h3>二、静态数据传递</h3>
<!-- 静态传递 -->
<menu-item title="来自父组件的值"></menu-item>
<h3>三、动态数据传递</h3>
<!-- 动态传递 -->
<menu-item :title="ptitle"></menu-item>
<menu-item :title="pmsg" content="hello"></menu-item>
<h3>四、短横线绑定</h3>
<!-- 属性名称规则 短横线 -->
<my-menu menu-title="Hello"></my-menu>
<!-- 数组 -->
<h3>五、数组传递</h3>
<my-array :arr="parray"></my-array>
<h3>六、对象传递</h3>
<my-obj :person="person"></my-obj>
</div>
<script src="../vue.js"></script>
<script type="text/javascript">
Vue.component('menu-item', {
// props 接收父组件传过来的内容
props: ['title', 'content'],
data() {
return {
msg: '子组件本身的数据'
}
},
template: '<div>{{msg + "----" + title + "------" + content}}</div>'
});
Vue.component('my-menu', {
props: ['menuTitle'],
template: '<div>{{menuTitle}}</div>'
});
// 遍历数组
Vue.component('my-array', {
props: ['arr'],
template: '<ul><li v-for="(item,index) of arr" :key="index">{{item}}</li></ul>'
});
Vue.component('my-obj', {
props: ['person'],
template: '<div>{{person.name}} {{person.age}}</div>'
})
var app = new Vue({
el:"#app",
data:{
pmsg: '父组件的内容',
ptitle: '动态绑定属性',
parray: ['apple','orange','banana'],
person: {
name: 'coco',
age: 18
}
}
})
</script>
</body>
</html>
2.2 子组件向父组件传递数据
子组件向父组件传值
props 传递数据原则,单向数据流
- 子组件通过自定义事件向父组件传递信息 $emit 向父组件传递数据
- 父组件监听子组件的事件
接下来我们以一个改变字体大小 以及 传递数组的实例来给给大家演示子组件向父最贱传递数据的流程
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>子组件向父组件传递数据</title>
</head>
<body>
<div id="app">
<!-- style 属性绑定 -->
<div :style="{fontSize: fontSize + 'px'}">{{pmsg}}</div>
<!-- 这个监听事件的名称必须和子组件的事件完全一致 -->
<menu-item :parr="parr" @enlarge-text="handle"></menu-item>
</div>
<script src="../vue.js"></script>
<script>
// 子组件向父组件传值 —— 基本用法
Vue.component('menu-item', {
props: ['parr'],
template:`
<div>
<ul>
<li :key="index" v-for="(item, index) of parr">{{item}}</li>
</ul>
<button @click="parr.push('lemon')">点击</button>
<button @click="$emit('enlarge-text')">扩大父组件中字体大小</button>
</div>
`
})
var app = new Vue({
el: "#app",
data: {
pmsg: '我是父组件的信息',
parr: ['orange','apple','banana','watermelen'],
fontSize: 10
},
methods: {
handle: function() {
// 扩大字体大小
this.fontSize += 5;
}
}
})
</script>
</body>
</html>
2.3 子组件通过自定义事件向父组件传递信息
基本和上面一致
子组件传递的数据,在父组件中要通过 $event 来接收数据
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>子组件向父组件传递数据</title>
</head>
<body>
<div id="app">
<div :style="{fontSize: fontSize + 'px'}">{{pmsg}}</div>
<menu-item :parr="parr" @enlarge-text="handle($event)"></menu-item>
</div>
<script src="../vue.js"></script>
<script>
// 子组件自定义向父组件传值 —— 基本用法
/**
* props 传递数据原则,单向数据流
* 1. 子组件通过自定义事件向父组件传递信息 $emit
* 2. 父组件监听子组件的事件
* 3. 父组件通过 $event 获取值
* */
Vue.component('menu-item', {
props: ['parr'],
template:`
<div>
<ul>
<li :key="index" v-for="(item, index) of parr">{{item}}</li>
</ul>
<button @click="parr.push('lemon')">点击</button>
<button @click="$emit('enlarge-text',5)">扩大父组件中字体大小</button>
<button @click="$emit('enlarge-text',10)">扩大父组件中字体大小</button>
</div>
`
})
var app = new Vue({
el: "#app",
data: {
pmsg: '我是父组件的信息',
parr: ['orange','apple','banana','watermelen'],
fontSize: 10
},
methods: {
handle: function(val) {
// 扩大字体大小
this.fontSize += val;
}
}
})
</script>
</body>
</html>
2.4 非父子组件间传值 (兄弟组件之间数据交互)
通过事件中心完成交互
使用方式:
- 单独创建一个 Vue 实例
- 然后处理事件的监听 和 销毁
// 单独创建时间中心管理组件间的通信
var eventHub = new Vue();
// 监听事件和销毁事件
eventHub.$on('add-todo',addTodo); // 自定义事件名称 事件函数
eventHub.$off('add-tod');
// 触发事件
eventHub.$emit('add-todo',id);
示例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>组件化开发</title>
</head>
<body>
<div id="app">
<div>事件销毁</div>
<div>
<button @click="handle">点击</button>
</div>
<!-- 每个组件的都是相互独立的 -->
<test-tom></test-tom>
<test-jerry></test-jerry>
</div>
<script src="../vue.js"></script>
<script>
/**
* 兄弟组件传值
* */
// 提供事件中心
var eventHub = new Vue();
Vue.component('test-tom', {
data() {
return {
num: 0
}
},
template: `
<div>
<div>TOM:{{num}}</div>
<div>
<button @click='handle'>点击</button>
</div>
</div>
`,
methods: {
handle: function() {
// 触发兄弟组件的事件
eventHub.$emit('jerry-event',2);
}
},
mounted() {
// 监听事件, 使用箭头函数,使用 this
eventHub.$on('tom-event', (val)=> {
this.num+=val;
})
}
})
Vue.component('test-jerry', {
data() {
return {
num: 0
}
},
template: `
<div>
<div>JERRY:{{num}}</div>
<div>
<button @click='handle'>点击</button>
</div>
</div>
`,
methods: {
handle: function() {
// 触发兄弟组件的事件
eventHub.$emit('tom-event',1);
}
},
mounted() {
// 监听事件, 使用箭头函数,使用 this
eventHub.$on('jerry-event', (val)=> {
this.num+=val;
})
}
})
var app = new Vue({
el: "#app",
data: {
},
methods: {
// 事件销毁
handle: function() {
eventHub.$off('tom-event');
eventHub.$off('jerry-event');
}
}
})
</script>
</body>
</html>
2.5 组件插槽
- 父组件向子组件传递模板内容
- 子组件通过插槽 slot 显示父组件中模板的数据
slot 是 vue 提供的 API,使用 slot 在子组件,我们可以在模板中插入我们想要的数据,我们还可以在 slot 中添加默认的数据
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>组件化开发</title>
</head>
<body>
<div id="app">
<test-box>
<h3>我没有通过 slot,我进不来</h3>
</test-box>
<hr>
<alert-box>
<h3>我通过 slot 插入进来了</h3>
</alert-box>
</div>
<script src="../vue.js"></script>
<script>
Vue.component('test-box',{
template: `
<div>
<strong>没插槽的时候</strong>
<div>
`,
})
Vue.component('alert-box', {
template: `
<div>
<strong>有插槽的时候</strong>
<slot></slot>
<div>
`,
})
var app = new Vue({
el: "#app",
data: {
}
})
</script>
</body>
</html>
2.6 具名插槽
在一个组件中,我们可以指定多个插槽插入数据,但是为了区分这些数据,我们会设置一个 name 值来标识唯一的插槽
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>组件化开发</title>
</head>
<body>
<div id="app">
<!-- slot 根据名称匹配
匹配的到的就会进入指定插槽
匹配不到的就会进入默认插槽
使用 template 可以包含多个标签,不使用它只能包含一个标签
-->
<base-layout>
<p slot="header">标题信息</p>
<p>我是主要内容</p>
<p>我是主要内容</p>
<p slot="footer">底部信息</p>
</base-layout>
<hr />
<base-layout>
<template slot="header">
<p>标题信息1</p>
<p>标题信息2</p>
</template>
<p>我是主要内容</p>
<p>我是主要内容</p>
<template slot="footer">
<p>底部信息1</p>
<p>底部信息2</p>
</template>
</base-layout>
</div>
<script src="../vue.js"></script>
<script>
Vue.component('base-layout', {
template: `
<div>
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
`
})
var app = new Vue({
el: "#app"
})
</script>
</body>
</html>
2.7 作用域插槽
应用场景: 父组件对子组件的内容进行加工处理。
使用方式:
- 在子组件中设置 slot,并自定义一个属性,接收父组件的内容
- 父组件创建 template,通过设置 slot-scope 就可以接收到子组件的内容
- 然后得到数据,就可以对数据进行显示了。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>作用域插槽</title>
</head>
<style>
.current {
color: orange;
}
</style>
<body>
<!-- 父组件对子组件的内容加工处理 -->
<div id="app">
<first-fruit :list="list">
<!-- 得到子组件通过 slot 传过来的数据, 并对数据加工处理 -->
<template slot-scope="slotProps">
<strong v-if="slotProps.info.id == 2" class="current">
{{slotProps.info.name}}
</strong>
<span v-else>
{{slotProps.info.name}}
</span>
</template>
</first-fruit>
</div>
<script src="../vue.js"></script>
<script type="text/javascript">
Vue.component('first-fruit',{
props: ['list'],
template: `
<div>
<ul>
<li v-for="item of list" :key="item.id">
<slot :info='item'>
{{item.name}}
</slot>
</li>
</ul>
</div>
`
})
var vm = new Vue({
el:"#app",
data: {
list: [{
id: 1,
name: 'apple'
},{
id: 2,
name: 'banana'
},{
id: 3,
name: 'orange'
}]
}
})
</script>
</body>
</html>