组件化是Vue中很重要的思想:
组件化思想的应用:
<div id="example">
<my-component>my-component>
div>
<script>
// 注册组件
// 1、 my-component 就是组件中自定义的标签名
Vue.component('my-component', {
template: 'A custom component!'
})
// 创建根实例
new Vue({
el: '#example'
})
script>
<div id="app">
<my-component>my-component>
div>
<script>
// 定义组件的模板
var Child = {
template: 'A custom component!'
}
new Vue({
//局部注册组件
components: {
// 将只在父模板可用 一定要在实例上注册了才能在html文件中使用
'my-component': Child
}
})
script>
局部注册的组件只能在注册它的Vue实例中使用
<html>
<head>
<meta charset="utf-8">
<title>title>
head>
<body>
<div id="app">
<cpn>cpn>
<cpn>cpn>
<cpn>cpn>
div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
Vue.component('cpn', {
template: '',
data() {
return {
counter: 0
}
},
methods:{
handle() {
this.counter++;
}
}
})
const app = new Vue({
el: "#app",
data: {
}
});
script>
body>
html>
如上的代码中,名为cpn
的组件被使用了三次,从运行效果可以看出,每个组件的counter变量是独立的
上述注册组件的方法,我们将template模板放在了注册组件的内部,这样做会带来两个坏处
因此一般我们会采用模板的分离写法,nVue提供了两种方案来定义HTML模块内容:
使用标签
使用标签
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<cpn :c-movies="movies" :c-message="message">cpn>
div>
<template id="cpn">
<div>
<h2>{{cMessage}}h2>
<div>
<ul>
<li v-for="item in cMovies">{{item}}li>
ul>
div>
div>
template>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
const app = new Vue({
el: "#app",
data: {
message: '你好啊',
movies: ['星际穿越', '火星救援', '星际迷航', '星球大战']
},
components: {
cpn: {
template: '#cpn',
data() {
return {
}
},
methods: {
},
props: {
cMovies: {
type: Array,
default () {
return ['随便什么电影']
}
},
cMessage: {
type: String,
default: 'aaaaaa'
}
}
}
},
});
script>
body>
html>
上面的代码中,props属性采用的是对象写法,好处就是可以进行类型校验,默认值设置等操作,而另外的数组写法则不能进行这些操作:
props: ['cMovies', 'cMessage']
$emit()
触发事件$emit()
第一个参数为 自定义的事件名称 第二个参数为需要传递的数据
<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>
<template id="cpn">
<div>
<ul>
<li :key='index' v-for='(item,index) in parr'>{{item}}li>
ul>
<button @click='$emit("enlarge-text", 5)'>扩大父组件中字体大小button>
<button @click='$emit("enlarge-text", 10)'>扩大父组件中字体大小button>
div>
template>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
pmsg: '父组件中内容',
parr: ['apple','orange','banana'],
fontSize: 10
},
methods: {
handle: function(val){
// 扩大字体大小
this.fontSize += val;
}
},
components:{
'menu-item': {
props: ['parr'],
template: '#cpn'
}
}
});
script>
body>
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="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script type="text/javascript">
/*
兄弟组件之间数据传递
*/
//1、 提供事件中心
var hub = new Vue();
Vue.component('test-tom', {
data: function(){
return {
num: 0
}
},
template: `
TOM:{{num}}
`,
methods: {
handle: function(){
//2、传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据) 触发兄弟组件的事件
hub.$emit('jerry-event', 2);
}
},
mounted: function() {
// 3、接收数据方,通过mounted(){} 钩子中 触发hub.$on(方法名
hub.$on('tom-event', (val) => {
this.num += val;
});
}
});
Vue.component('test-jerry', {
data: function(){
return {
num: 0
}
},
template: `
JERRY:{{num}}
`,
methods: {
handle: function(){
//2、传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据) 触发兄弟组件的事件
hub.$emit('tom-event', 1);
}
},
mounted: function() {
// 3、接收数据方,通过mounted(){} 钩子中 触发hub.$on()方法名
hub.$on('jerry-event', (val) => {
this.num += val;
});
}
});
var vm = new Vue({
el: '#app',
data: {
},
methods: {
handle: function(){
//4、销毁事件 通过hub.$off()方法名销毁之后无法进行传递数据
hub.$off('tom-event');
hub.$off('jerry-event');
}
}
});
script>
body>
html>
组件最大的特性就是复用性,而插槽作为组件的扩展大大提高了组件的可复用能力
在vue组件的模板中,可用
添加一个插槽,插槽内的内容是默认值,当没有被覆盖时,将显示默认值
<html>
<head>
<meta charset="utf-8">
<title>title>
head>
<body>
<div id="app">
<cpn>cpn>
<cpn><span>这里是一些文字span>cpn>
<cpn>
<b>加粗文字b>
<i>斜体文字i>
<span>这里是一些文字span>
cpn>
div>
<template id="cpn">
<div>
<h2>我是组件标题h2>
<p>我是组件的文本内容p>
<slot><button type="button">这是按钮button>slot>
div>
template>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
const app = new Vue({
el: '#app',
data: {
},
components:{
cpn: {
template:'#cpn'
}
}
})
script>
body>
html>
中的 “name” 属性绑定元素
<html>
<head>
<meta charset="utf-8">
<title>title>
head>
<body>
<div id="app">
<cpn>
<button type="button" slot="left">按钮button>
<span slot="center"><b>这是中间的标题b>span>
<a href="" slot="right">一个链接a>
cpn>
div>
<template id="cpn">
<div>
<slot name="left"><span>左边span>slot>
<slot name="center"><span>中间span>slot>
<slot name="right"><span>右边span>slot>
div>
template>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
const app = new Vue({
el: '#app',
data: {
},
components:{
cpn: {
template:'#cpn'
}
}
})
script>
body>
html>
<html>
<head>
<meta charset="utf-8">
<title>title>
head>
<body>
<div id="app">
<cpn>cpn>
<cpn>
<template v-slot="slot">
<span v-for="item in slot.data">{{item}} - span>
template>
cpn>
<cpn>
<template v-slot="slot">
<span>{{slot.data.join(' * ')}}span>
template>
cpn>
div>
<template id="cpn">
<div>
<slot :data="items">
<ul>
<li v-for="item in items">{{item}}li>
ul>
slot>
div>
template>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
const app = new Vue({
el: '#app',
data: {
},
components:{
cpn: {
template:'#cpn',
data() {
return {
items: ['A', 'B', 'C', 'D', 'E', 'F', 'G']
}
}
}
}
})
script>
body>
html>
在接下来的实例中,我将模拟在有网络请求的情况下,用户点击不同的分类tab时显示不通内容的案例。
<html>
<head>
<meta charset="utf-8">
<title>title>
head>
<style type="text/css">
.active{
background-color: #00aaff;
}
.btn{
margin-right: 10px;
border: 0;
border-radius: 2px;
padding-left: 10px;
padding-right: 10px;
height: 30px;
}
style>
<body>
<div id="app">
<cpn :categories='categories' :items='items' @btnclick='changeitems'>cpn>
div>
body>
<template id="cpn">
<div>
<button type="button" class="btn" v-for="(category, index) in categories"
@click="btnclick(category, index)" :class="{active: currentIndex == index}">
{{category.name}}
button>
<br>
<ul>
<li v-for="item in items">{{item}}li>
ui>
div>
template>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
const cpn = {
template: '#cpn',
props: {
items: {
type: Array,
default() {
return ['无数据']
}
},
categories: {
type: Array,
required: true
}
},
methods: {
btnclick(category, index){
//console.log(category);
this.currentIndex = index;
this.$emit('btnclick', category);
},
},
data() {
return {
currentIndex: 0
}
}
};
const app = new Vue({
el: '#app',
data: {
categories: [
{
id: 'movies',
name: '电影'
},
{
id: 'fruits',
name: '水果'
},
{
id: 'musics',
name: '音乐'
}
],
//ff
lists: {
movies: ['星际穿越', '火星救援', '星际迷航', '星球大战'],
fruits: ['苹果', '香蕉', '草莓', '鸭梨'],
musics: ['生命因你而火热', '没有理想的人不伤心', '你要跳舞吗']
},
items: []
},
components: {
cpn
},
mounted() {
//模拟网络请求和数据绑定
this.items = this.lists.movies;
},
methods: {
changeitems(category) {
//模拟网络请求和数据绑定
this.items = this.lists[category.id];
}
}
})
script>
html>