VUE组件
自定义组件
这里我们主要使用局部注册,首先需要在components
文件夹中创建vue组件,且<script>
中name
即组件的名称:
然后在需要使用该组件的vue文件中执行:
- 在
<script>
中引入该组件; - 在
<compontents>
中注册该组件; - 在
<template>
中使用该组件;
另外需要注意的是:自定义组件的数据data必须是一个函数,重复使用的组件间的data是相互独立的。
data: function () {
return {
count: 0
}
}
组件单向数据流
将父组件中的数据传入到子组件中去,这时候我们需要使用props
属性,父子prop
之间形成一个单向下行绑定:父级的prop
的更新会流动到子组件中,反之则不行。
<template>
<div id="app">
<!-- 注意!title1 和 title2 是父组件的 data 中定义的数据,title 则是子组件中接收数据时的变量名 -->
<HelloVue :title="title1"></HelloVue>
<HelloVue :title="title2"></HelloVue>
</div>
</template>
需要注意的是:在title前加冒号是因为后面的title1是一个变量,如果不加冒号,则认为后面的是一个字符串。
在子组件中使用props
进行数据的接收,且props
是一个数组:
<template>
<div class="hello">
<!-- 第二步:在页面上显示 title 的值,写法和显示 data 里定义的数据一样 -->
<h1>{{ title }}</h1>
</div>
</template>
<script>
export default {
name: 'HelloVue',
// 第一步:在 prop 属性中接收 title
props: ['title']
};
</script>
另外如果需要知道传入数据的数据类型,则需要这么使用(类型首字母大写):
props: {
title: String,
// 多类型
likes: [String, Number],
// 带有默认值
isPublished: {
type: Boolean,
default: true
},
// 必填
commentIds: {
type: Array,
required: true
},
author: Object,
callback: Function,
contactsPromise: Promise
}
如果需要对传入的数据进行处理,则可以使用计算属性对数据进行处理;如果需要将传入的数据保存为本地数据,则可以这么使用:
props: ['initialTitle'],
data: function () {
return {
// 要读取 prop 里的 initialTitle,要在前面加 “this.”
// 将传入的 initialTitle 作为本地属性 title 的初始值
title: this.initialTitle
}
}
事件修饰符
在自定义组件的根元素上监听一个原生事件和在html
中原生标签上监听一个原生事件是有区别的,即在子组件和父组件的某个标签上分别都设置监听器时,只有子组件的监听器会生效,而父组件的监听器是不会生效的。
在App.vue(父)中:
<!-- 给自定义组件添加点击事件 print -->
<Article
v-for="article in articleList"
:key="article.title"
:article="article"
@click="print(article)"
></Article>
在Article.vue(子)中:
<div class="article-title" @click="printTitle">{{ article && article.title }}</div>
如果想要父组件中的监听器也生效则需要使用修饰符:.native
<Article
v-for="article in articleList"
:key="article.title"
:article="article"
@click.native="print(article)"
></Article>
也可改为按键修饰符:
@keyup.enter
即回车键监听
自定义事件
我们不能在子组件中直接修改props
中传入的数据,如果需要修改父组件中的数据,则需要给子组件绑定自定义事件,其功能主要有两个,一是在子组件中调用父组件中的方法;二是把子组件中的数据以参数的方式传入父组件。
- 在App.vue(父)中:
<!-- 自定义事件 upVote,调用该事件时会执行 handleLikes 方法 -->
<article
v-for="article in articleList"
:key="article.title"
:article="article"
v-on:upVote="handleLikes"
></article>
- 在Article.vue(子)中:
<!-- 在 template 中直接调用自定义事件 upVote -->
<button @click="$emit('upVote', this.article)">点赞</button>
<!-- 如果需要在调用事件的同时进行其他相关操作 -->
<button @click="childEvent">点赞</button>
methods: {
childEvent: function() {
// 调用自定义事件 upVote
this.$emit('upVote', this.article);
// do other things
}
}
自定义事件的参数
在App.vue(父)中的方法,其中需要传入参数,但是在绑定自定义事件时并没有传入参数:
// 在 `methods` 对象中定义方法
methods: {
handleLikes(article) {
article.likes++
}
}
在Article.vue(子)中,调用自定义事件upVote
时会把参数传入:
methods: {
childEvent: function() {
// 调用自定义事件 upVote,这里的第二个参数最后会传到父组件中的 handleLikes 方法里
this.$emit('upVote', this.article);
// do other things
}
}
自定义事件的双向绑定
使用修饰符.sync
使某个属性在子组件与父组件之间完成双向绑定,在App.vue(父)中:
<MyCount class="count" :count.sync="count"></MyCount>
// 在 `methods` 对象中定义方法
data: function() {
return {
count: 0
}
}
在Article.vue(子)中:
<div class="my-count">
<button @click="$emit('update:count', count+1)">加一</button>
{{ count }}
</div>
props: ['count'],
this.$emit('update:visible',false);
可以直接改变 visible 的值。
组件函数的调用
调用子组件的方法
- 给子组件添加
ref
属性:
<template>
<Modal ref="modal"></Modal>
</template>
- 在父组件中调用子组件的方法:
<script>
export default {
methods: {
showModal() {
// 调用子组件中的 show 方法
this.$refs.modal.show();
}
}
};
</script>
ref 访问子元素
<template>
<div id="app">
<input ref="input" type="text" />
<button @click="focusInput">点击使输入框获取焦点</button>
</div>
</template>
<script>
export default {
name: 'app',
methods: {
focusInput() {
// this.$refs.input 访问输入框元素,并调用 focus() 方法使其获取焦点
this.$refs.input.focus();
}
}
}
</script>
组件 slot(插槽)
slot
即一个插槽,相当于在子组件的DOM中留下一个位置,父组件如果有需要,则可以在插槽中添加任何内容。
slot 的基础使用
- 在子组件 Modal.vue 中添加插槽,标签中的内容是后备内容,可以为空,如果父组件不插入内容,则默认显示后备内容:
<div class="modal-content">
<slot>这是个弹框</slot>
<div class="footer">
<button @click="close">close</button>
<button @click="confirm">confirm</button>
</div>
</div>
- 在父组件中使用子组件,并向插槽中添加内容:
<Modal :visible.sync="visible">这是添加在插槽中的内容</Modal>
除此之外还可以在父组件中为子组件添加类名,同时将类名的CSS代码书写在父组件中:
<Modal
customClassName="textarea-modal"
title="留言"
:visible.sync="visible"
@confirm="confirm"
>
</Modal>
<style lang="scss">
.textarea-modal {
//书写其CSS代码
}
</style>
<div :class="['modal', customClassName]" v-if="visible">
<div class="modal-content">
<div class="modal-header">
</div>
<div class="modal-body">
<slot></slot>
</div>
<div class="modal-footer">
</div>
</div>
</div>
具名插槽
上面我们使用的都是“匿名插槽”,即<slot>
标签没有加name属性,则默认为“default”。而如果我们需要在一个组件中插入多个插槽,则需要使用“具名插槽”。
- 首先在子组件中的
<slot>
标签上添加name属性:
<div class="modal" v-if="visible">
<div class="modal-content">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</div>
- 在父组件中使用子组件,并在
<template>
标签中使用v-slot
指令,该指令的参数即其名称,如果不使用该方法则默认添加到“匿名插槽”中:
<Modal :visible.sync="visible">
<template v-slot:header>
<h1>Modal title</h1>
</template>
<div>main content</div>
<div>main content</div>
<template v-slot:footer>
<p>Modal footer</p>
</template>
</Modal>