组件的分类
常规页面组件
由 vue-router 产生的每个页面,它本质上也是一个组件( .vue),主要承载当前页面的 HTML 结构,会包含数据获取、数据整理、数据可视化等常规业务。
功能性抽象组件
不包含业务,独立、具体功能的基础组件,比如日期选择器、弹窗警告等。这类组件作为项目的基础控件,会被大量使用,因此组件的 API 进行过高强度的抽象,可以通过不同配置实现不同的功能。
业务组件
它不像第二类独立组件只包含某个功能,而是在业务中被多个页面复用的,它与独立组件的区别是,业务组件只在当前项目中会用到,不具有通用性,而且会包含一些业务,比如数据请求;而独立组件不含业务,在任何项目中都可以使用,功能单一,比如一个具有数据校验功能的输入框。
父组件向子组件传值 prop
英式发音:[prɒp]
prop接收的数据 类型
refAge: {
type: Number,
default: 0
},
refName: {
type: String,
default: ''
},
hotDataLoading: {
type: Boolean,
default: false
},
hotData: {
type: Array,
default: () => {
return []
}
},
getParams: {
type: Function,
default: () => () => {}
},
meta: {
type: Object,
default: () => ({})
}
代码:
// Js
let Child = Vue.extend({
template: '{{ content }}
',
props: {
content: {
type: String,
default: () => { return 'from child' }
}
}
})
new Vue({
el: '#app',
data: {
message: 'from parent'
},
components: {
Child
}
})
浏览器输出:from parent
子组件向父组件传值 $emit
英式发音:[iˈmɪt]
let MyButton = Vue.extend({
template: '',
data () {
return {
greeting: 'vue.js!'
}
},
methods: {
triggerClick () {
this.$emit('greet', this.greeting)
}
}
})
new Vue({
el: '#app',
components: {
MyButton
},
methods: {
sayHi (val) {
alert('Hi, ' + val) // 'Hi, vue.js!'
}
}
})
EventBus
思路就是声明一个全局Vue实例变量 EventBus , 把所有的通信数据,事件监听都存储到这个变量上。这样就达到在组件间数据共享了,有点类似于 Vuex。但这种方式只适用于极小的项目,复杂项目还是推荐 Vuex。下面是实现 EventBus 的简单代码:
// 全局变量
let EventBus = new Vue()
// 子组件
let Child = Vue.extend({
template: 'child
',
created () {
console.log(EventBus.message)
// -> 'hello'
EventBus.$emit('received', 'from child')
}
})
new Vue({
el: '#app',
components: {
Child
},
created () {
// 变量保存
EventBus.message = 'hello'
// 事件监听
EventBus.$on('received', function (val) {
console.log('received: '+ val)
// -> 'received: from child'
})
}
})
Vuex
有文章:https://www.jianshu.com/p/79f8d6ab54ca
任意组件的通信
找到任意组件实例——findComponents 系列方法
它适用于以下场景:
由一个组件,向上找到最近的指定组件;
由一个组件,向上找到所有的指定组件;
由一个组件,向下找到最近的指定组件;
由一个组件,向下找到所有指定的组件;
由一个组件,找到指定组件的兄弟组件。
5 个不同的场景,对应 5 个不同的函数,实现原理也大同小异。
1、向上找到最近的指定组件——findComponentUpward
// 由一个组件,向上找到最近的指定组件
function findComponentUpward (context, componentName) {
let parent = context.$parent;
let name = parent.$options.name;
while(parent && (!name || [componentName].indexOf(name) < 0)) {
parent = parent.$parent;
if(parent)
name = parent.$options.name;
}
return parent;
}
export{
findComponentUpward
};
比如下面的示例,有组件 A 和组件 B,A 是 B 的父组件,在 B 中获取和调用 A 中的数据和方法:
组件 A
组件 B
2、向上找到所有的指定组件——findComponentsUpward
// 由一个组件,向上找到所有的指定组件
function
findComponentsUpward
(
context
,
componentName
)
{
let
parents
=
[];
const
parent
=
context
.
$parent
;
if
(
parent
)
{
if
(
parent
.
$options
.
name
===
componentName
)
parents
.
push
(
parent
);
return
parents
.
concat
(
findComponentsUpward
(
parent
,
componentName
));
}
else
{
return
[];
}
}
export
{
findComponentsUpward
};
3、向下找到最近的指定组件——findComponentDownward
// 由一个组件,向下找到最近的指定组件
function
findComponentDownward
(
context
,
componentName
)
{
const
childrens
=
context
.
$children
;
let
children
=
null
;
if
(
childrens
.
length
)
{
for
(
const
child of childrens
)
{
const
name
=
child
.
$options
.
name
;
if
(
name
===
componentName
)
{
children
=
child
;
break
;
}
else
{
children
=
findComponentDownward
(
child
,
componentName
);
if
(
children
)
break
;
}
}
}
return
children
;
}
export
{
findComponentDownward
};
4、向下找到所有指定的组件——findComponentsDownward
// 由一个组件,向下找到所有指定的组件
function
findComponentsDownward
(
context
,
componentName
)
{
return
context
.
$children
.
reduce
((
components
,
child
)
=>
{
if
(
child
.
$options
.
name
===
componentName
)
components
.
push
(
child
);
const
foundChilds
=
findComponentsDownward
(
child
,
componentName
);
return
components
.
concat
(
foundChilds
);
},
[]);
}
export
{
findComponentsDownward
};
5、找到指定组件的兄弟组件——findBrothersComponents
// 由一个组件,找到指定组件的兄弟组件
function
findBrothersComponents
(
context
,
componentName
,
exceptMe
=
true
)
{
let
res
=
context
.
children
.
filter
(
item
=>
{
return
item
.
$options
.
name
===
componentName
;
});
let
index
=
res
.
findIndex
(
item
=>
item
.
_uid
===
context
.
_uid
);
if
(
exceptMe
)
res
.
splice
(
index
,
1
);
return
res
;
}
export
{
findBrothersComponents
};
相比其它 4 个函数, findBrothersComponents 多了一个参数 exceptMe,是否把本身除外,默认是 true。寻找兄弟组件的方法,是先获取 context.children,也就是父组件的全部子组件,这里面当前包含了本身,所有也会有第三个参数 exceptMe。Vue.js 在渲染组件时,都会给每个组件加一个内置的属性 _uid,这个 _uid 是不会重复的,借此我们可以从一系列兄弟组件中把自己排除掉。