vue组件
1、创建vue组件
建组件的方式有两种,一种是局部组件,一种是全局组件
1.1局部组件
介绍
通过对象的方式创建了一个组件,由对象的template属性指定组件的结构,然后组件必须在根实例中的components属性中注册才能实现引用,这种创建方式称为局部组件
代码示例
<div id="app">
<!-- 3.引用组件 -->
<component-a></component-a>
</div>
<script>
// 1.创建组件
var ComponentA = { template: `<div>hello vue</div>` }
new Vue({
el: '#app',
// 2.注册局部组件
components: {
'component-a': ComponentA,
}
})
</script>
1.2 全局组件
介绍
全局组件使用Vue.component的方法创建,第一个参数为组件名,第二个参数是对象由template属性指定组件结构,并且不需要通过components属性注册就能直接调用
代码示例
<div id="app">
<!-- 2.引用组件 -->
<component-a></component-a>
</div>
<script>
// 1.创建全局组件
Vue.component("component-a", {
template: `<div>hello vue</div>`
})
new Vue({
el: '#app'
})
</script>
注意
对比后可以明显发现两者的创建方式不一样,
可以理解为需要在components中注册才可以在页面调用的为局部组件,使用Vue.component的方法创建为全局组件
1.3 组件实例的属性
介绍
不管是局部组件还是全局组件中,我们留意到了它们共同存在一个对象,对象下都是通过template来指定结构,但是这个对象可远不止只有template这个属性,它包含了根实例可用的所有属性(包data、methods、computed、watch等), 也就是说组件也是一个Vue实例对象。
比如在组件定义data属性
代码示例
<div id="app">
<!-- 2.引用组件 -->
<component-a></component-a>
</div>
<script>
// 1.创建全局组件
Vue.component("component-a", {
data: function(){
return {
message: "hello vue"
}
},
template: `<div>{{message}}</div>`
})
new Vue({
el: '#app'
})
</script>
注意
组件中的data数据声明和根实例中不一样, 组件的data声明必须是一个函数,在函数中返回一个对象,该对象才是组件的数据,data数据可以在template中渲染。除了data的定义和根实例不一样外,其他属性都是一样的。
2、组件传值
2.1 组件的props
介绍
组件传值的关键属性props,实现步骤:先在组件中通过props声明可接收的属性,然后在引用组件时传递对应的属性和值
代码示例
<div id="app">
<!-- 3.引用组件 -->
<goods-item title="原装正品 iphone x" price="10000"></goods-item>
<!-- 4.再次引用组件-->
<goods-item title="假一赔十 iphone 8" price="8000"></goods-item>
</div>
<script>
// 1.创建商品组件
Vue.component("goods-item", {
// 2.定义props
props: ["title", "price"],
template: `<div>
<h4>{{title}}</h4>
<p>{{price}}</p>
</div>`
})
new Vue({
el: '#app'
})
</script>
注意
props的值是一个数组,里面声明了组件可接收的属性,如上例title,price,注意组件内部访问props的值和访问data中的值是一样的
2.2 动态的props值
介绍
上例中我们发现props的属性都是属于一个商品的信息,但是一个商品的信息往往是使用对象来表示的,那么组件的props也是可以接收对象类型值的,并且有两种传递方式
- 直接传递对象
- 传递data数据
代码示例
1.直接传递对象
<div id="app">
<!-- 3.引用组件 -->
<goods-item :goods="{title: '原装正品 iphone x', price: '10000'}"></goods-item>
<!-- 4.再次引用组件-->
<goods-item :goods="{title: '假一赔十 iphone 8', price: '8000'}"></goods-item>
</div>
<script>
// 1.创建商品组件
Vue.component("goods-item", {
// 2.定义props
props: ["goods"],
template: `<div>
<h4>{{goods.title}}</h4>
<p>{{goods.price}}</p>
</div>`
})
new Vue({
el: '#app'
})
</script>
注意
只需要声明一个props属性goods,goods是一个对象,在传值时需要使用v-bind:或缩写:来传递动态值,如果不使用v-bind:,值会解释为普通的字符串
2.3 组件接收props的多种方法
介绍
我们都是使用数组的方式定义组件的props,但其实定义props的方法有很多种,数组只是常用的一种
代码示例
Vue.component('my-component', {
props: {
// 基础的类型检查 (`null` 匹配任何类型)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
})
3、组件传递事件
3.1 传递普通事件
介绍
往上例的商品组件中新增一个购买商品的按钮,然后点击该按钮时触发外部传递过来的购买事件,之所以把购买事件封装到外部,是因为在大型的应用中往往有多个地方可以进行购买操作
代码示例
<div id="app">
<!-- 2.传递事件, buyGoods是事件名 -->
<goods-item :goods="goods" @buy-goods="buyGoods"></goods-item>
</div>
<script>
Vue.component("goods-item", {
props: ["goods"],
methods: {
// 4.购买事件
handleBuy: function(){
// 5.主动触发传递的事件
this.$emit("buy-goods");
}
},
template: `<div>
<h4>{{goods.title}}</h4>
<p>{{goods.price}}</p>
<!-- 3.新增按钮,添加点击事件 -->
<button @click="handleBuy">立即购买</button>
</div>`
})
new Vue({
el: '#app',
data: {
goods: {
title: '原装正品 iphone x',
price: 10000
}
},
methods: {
// 1.封装购买商品事件
buyGoods: function(){
alert("购买了商品")
}
}
})
</script>
注意
给组件传递事件的流程:
- 先在外部定义需要传入的事件,如根实例的buyGoods
- 将该事件传入组件,@buy-goods="buyGoods"注意buy-goods是自定义事件名(不要使用大写字母)
- 组件使用实例的
emit("buy-goods");
3.2 子组件传值到父组件
介绍
组件之间是采用单向数据流,也就是父组件可以使用props往子组件传递数据,如果想在子组件中往父组件传递数据,可以通过事件方式实现。
代码示例
<div id="app">
<goods-item :goods="goods" @buy-goods="buyGoods"></goods-item>
</div>
<script>
Vue.component("goods-item", {
props: ["goods"],
methods: {
handleBuy: function(){
// 1.第二个参数可以传数据给父组件
this.$emit("buy-goods", { title: this.goods.title });
}
},
template: `<div>
<h4>{{goods.title}}</h4>
<p>{{goods.price}}</p>
<button @click="handleBuy">立即购买</button>
</div>`
})
new Vue({
el: '#app',
data: {
goods: {
title: '原装正品 iphone x',
price: 10000
}
},
methods: {
// 2.通过参数接收子组件的数据
buyGoods: function(data){
alert("购买了商品:" + data.title)
}
}
})
</script>
注意
在子组件中使用this.@emit()触发事件时,可以通过参数传入数据,父组件事件中可接收对应的数据
3.3 兄弟组件传值
子传父》父传兄
3.4 自定义事件通讯
介绍
兄弟组件,跨层级组件都可以通过自定义事件来进行通讯
event.js
import Vue from 'vue'
export default new Vue()
说明
1.Vue本身具有自定义事件的能力
2.event是new Vue()的实例,已经具备emit自定义事件的能力,所以不需要引入eventBus
index父组件
<template>
<div>
<Input @add="addHandler"/>
<List :list="list" @delete="deleteHandler"/>
</div>
</template>
<script>
import Input from './Input' //子组件
import List from './List' //子组件
export default {
components: {
Input,
List
},
data() {
return {
list: [
{
id: 'id-1',
title: '标题1'
},
{
id: 'id-2',
title: '标题2'
}
]
}
},
methods: {
addHandler(title) {
this.list.push({
id: `id-${Date.now()}`,
title
})
},
deleteHandler(id) {
this.list = this.list.filter(item => item.id !== id)
}
},
created() {
// eslint-disable-next-line
console.log('index created')
},
mounted() {
// eslint-disable-next-line
console.log('index mounted')
},
beforeUpdate() {
// eslint-disable-next-line
console.log('index before update')
},
updated() {
// eslint-disable-next-line
console.log('index updated')
},
}
</script>
Input 子组件
说明
通过event.$emit调用自定义事件获取其他组件传递的数据
<template>
<div>
<input type="text" v-model="title"/>
<button @click="addTitle">add</button>
</div>
</template>
<script>
import event from './event'
export default {
data() {
return {
title: ''
}
},
methods: {
addTitle() {
// 调用父组件的事件
this.$emit('add', this.title)
// 调用自定义事件
event.$emit('onAddTitle', this.title)
this.title = ''
}
}
}
</script>
htmlList 子组件
说明
通过event.$on绑定自定义事件并传递数据过去
<template>
<div>
<ul>
<li v-for="item in list" :key="item.id">
{{item.title}}
<button @click="deleteItem(item.id)">删除</button>
</li>
</ul>
</div>
</template>
<script>
import event from './event'
export default {
// props: ['list']
props: {
// prop 类型和默认值
list: {
type: Array,
default() {
return []
}
}
},
data() {
return {
}
},
methods: {
deleteItem(id) {
this.$emit('delete', id)
},
addTitleHandler(title) {
// eslint-disable-next-line
console.log('on add title', title)
}
},
created() {
// eslint-disable-next-line
console.log('list created')
},
mounted() {
// eslint-disable-next-line
console.log('list mounted')
// 绑定自定义事件
event.$on('onAddTitle', this.addTitleHandler)
},
beforeUpdate() {
// eslint-disable-next-line
console.log('list before update')
},
updated() {
// eslint-disable-next-line
console.log('list updated')
},
beforeDestroy() {
// 及时销毁,否则可能造成内存泄露
event.$off('onAddTitle', this.addTitleHandler)
}
}
</script>
注意
绑定自定义事件的组件,要及时销毁该事件,否则可能造成内存泄露
4、生命周期
说明
- created和mounted的区别 created是把vue的实例初始化,只存在js内存的一个变量而已,并没有渲染; mounted 是真正在页面上绘制完成,页面已经渲染完成,这个时候才可以操作DOM元素
- beforeDestroy 中可能要做什么 解除绑定,销毁子组件以及事件监听
4.1 单个组件
- 挂载阶段
- beforeCreated
- created
- beforeMounted
- mounted
- 更新阶段
- beforeUpdate
- updated
- 销毁阶段
- beforeDestroy
- destroyed
4.3 父子关系
- 创建,渲染
创建初始化实例是从外到内,渲染是从内到外
- 父 beforeCreated
- 父 created
- 父 beforeMounted
- 子 beforeCreated
- 子 created
- 子 beforeMounted
- 子 mounted
- 父 mounted
- 更新
更上面类似
- 父 beforeUpdate
- 子 beforeUpdate
- 子 updated
- 父 updated
- 销毁
- 父 beforeDestroy
- 子 beforeDestroy
- 子 destroyed
- 父 destroyed