介绍
本文目的是对Vue组件化内容在网课学习的总结和复习。多有不足之处,请多多交流。
组件化
组件化基础
vue中的组件是学习vue的过程中避不开的一个名词,对组件化的理解越深,在开发过程中也就越发的得心应手。附上一句听来的话:万物皆组件。
vue的组件系统提供了一种抽象,让我们可以使用独立可复用的组件来构建大型应用。这是一个很大的好处,因为不管任意类型的应用界面都可以抽象为一个由许多组件构成的组件树。组件化能提高开发效率,方便重复使用,简化调试步骤,提升项目可维护性,便于多人协同开发。
组件化内容
组件化的学习内容主要分为两方面:
- 组件间通信
- 插槽
组件间通信
组件间通信的方式有很多,也是面试时Vue知识体系的必问内容,所以需要认真理解和记忆。
父子组件通信
props / $emit
- props负责在子组件中接收父组件传递数据
// child
props: [msg,info] // 必须都是字符串
props: { msg: String }
// parent
<HelloWorld msg="Welcome to Your Vue.js App"/>
复制代码
- $emit 负责子组件给父组件通过自定义事件的参数形式传递数据
// child
this.$emit('add', good) // good为参数,即为父组件中函数的$event
// parent
<Cart @add="cartAdd($event)"></Cart>
复制代码
$refs / ref
ref 作为标记挂载子组件上,通过 this.$refs.xxx 获取子组件数据。
// parent
<HelloWorld ref="hw"/>
mounted() {
this.$refs.hw.xx = 'xxx'
}
复制代码
另外在面试中常问到的一点是:
- 如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;
- 如果用在子组件上,引用就指向组件实例
children
-
parent 其实与parent功能相似的还有一个$root,二者的区别:
- $parent 可以用于获取当前组件的父组件实例,中间不跨层级。
- $root 只能用于获取根组件实例,即为div#app
// brother1
this.$parent.$on('foo', handle)
// brother2
this.$parent.$emit('foo')
复制代码
- $children
// parent
this.$children[0].xx = 'xxx'
复制代码
父组件可以通过this.children[x].xx 来访问子组件数据,但是有一点需要注意:children不能保证子组件顺序和页面上排列顺序相同
$children和ref的区别
-
$children
- children是一个所有组件的集合被保存在数组当中,需要通过数组下标访问。所以当子组件不固定数目时,这样是即有可能出错的。
- children数组中子组件的顺序是根据页面加载组子组件的顺序来决定,与子组件在页面中实际的显示顺序可能不同。
- children数组存储只是当前页面中未被销毁的子组件,如果有一个被销毁,在其位置之后的子组件顺序会发生前移。可以配合keep-alive功能来解决该问题
-
ref
- ref 是以键值对形式存储子组件关系的集合,保存在对象中,需要通过属性名去访问。所以需要先在组件上使用ref=”xxx“定义一个唯一的名字方可在methods方法中通过this.$refs.xxx来使用
- ref是根据明细来获取组件的,所以无论子组件数量多少并无影响。
隔代组件通信
listeners
-
$attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 ( class 和 style 除外 )。
- 当一个组件没有声明props属性时,attrs中包含所有父作用域的绑定 ( class 和 style 除外 ),并且可以通过 v-bind=“attrs” 传入内部组件。
// 父组件
<ComA title="multilayer" foo="foo"></ComA>
// 第一层组件
<ComB v-bind="$attrs"></ComB>
// 第二层组件
<ComC v-bind="$attrs"></ComC>
复制代码
- 单单通过 v-bind=“$attrs 给内部组件传入数据,在渲染时会把传递的数据挂载到子组件的根元素上,导致数据信息暴露且不雅观,所以通常配合在子组件中设置 inheritAttrs 为false一起使用。
<template>
<div>
<div>CompC</div>
</div>
</template>
<script>
export default {
inheritAttrs: false,
mounted() {
console.log('this.$attrs', this.$attrs)
}
}
复制代码
$listeners
:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on=“$listeners” 传入内部组件
// 给Grandson隔代传值,communication/index.vue
<Child2 msg="lalala" @some-event="onSomeEvent"></Child2>
// Child2做展开
<Grandson v-bind="$attrs" v-on="$listeners"></Grandson>
// Grandson使用
<div @click="$emit('some-event', 'msg from grandson')">
{{msg}}
</div>
复制代码
provide / inject
- 祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。不需要像$attrs那样需要一层层传递
- provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。
// 父组件
provide() {
return {
foo: "foo",
bar: "bar",
comA: this,
};
},
// 子组件
inject: ["foo", "bar", "comA"],
复制代码
非关系型组件通信
EventBus (on)
- 通过一个空的 Vue 实例/作为中央事件总线(事件中心),用它来触发事件和监听事件,从而实现任何组件间的通信。
// 创建bus实例
import Vue from "vue";
export const bus = new Vue();
// 子组件触发事件
import { bus } from "../bus";
<button @click="callComA">call comA</button>
callComA() {
// 过于灵活
bus.$emit("waitComC","heihei");
}
// 父组件执行事件
import { bus } from "../bus";
mounted() {
bus.$on("waitComC", (v) => {
console.log("comA - wait comC");
});
}
复制代码
- 原始的事件总线模式需要自行实现Bus类,重点是是on和emit事件。现在使用Vue代替Bus,是因为Vue已经实现了on和emit事件。
// Bus:事件派发、监听和回调管理
class Bus {
constructor(){
this.callbacks = {}
}
$on(name, fn){
this.callbacks[name] = this.callbacks[name] || []
this.callbacks[name].push(fn)
}
$emit(name, args){
if(this.callbacks[name]){
this.callbacks[name].forEach(cb => cb(args))
}
}
}
// main.js
Vue.prototype.$bus = new Bus()
// child1
this.$bus.$on('foo', handle)
// child2
this.$bus.$emit('foo')
复制代码
Vuex
Vuex在组件通信中的作用相信大家都比较熟悉了,在此就不在过多赘述。对于Vuex的使用有不明白的同学,可以看下官方文档中对于Vuex的使用介绍,附上文档链接:Vuex文档
插槽
插槽介绍
Vue中插槽是实现内容分发的API,多用复合组件的开发。
插槽分类
- 匿名插槽
- 具名插槽
- 作用域插槽
插槽使用
匿名插槽
匿名插槽的使用最为简单,会将子组件标签内的内容替换到slot插槽内部。
// parent
<comp>hello</comp>
// comp
<div>
<slot></slot>
</div>
复制代码
具名插槽
相对于匿名插槽来说,具名插槽为填充内容指定了要替换的插槽区域,也可以同时使用多个插槽
// parent
<Comp2>
<!-- 默认插槽用default做参数 -->
<template v-slot:default>具名插槽</template>
<!-- 具名插槽用插槽名做参数 -->
<template v-slot:content>内容...</template>
</Comp2>
// comp2
<div>
<slot></slot>
<slot name="content"></slot>
</div>
复制代码
作用域插槽
分发内容要用到子组件中的数据
// parent
<Comp3>
<!-- 把v-slot的值指定为作用域上下文对象 -->
<template v-slot:default="slotProps">
来自子组件数据:{{slotProps.foo}}
</template>
</Comp3>
// comp3
<div>
<slot :foo="foo"></slot>
</div>
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END