组件系统是Vue.js其中一个重要的概念,它提供了一种抽象,让我们可以使用独立可复用的小组件来构建大型应用,任意类型的应用界面都可以抽象为一个组件树。
一、四个核心组成
1、props 2、自定义事件 event 3、插槽及作用域插槽 slot 4、组件方法 method。
任何组件均离不开以上4点,我们在开发过程中,以这4点入手,封装我们想要的组件。
以element-ui table组件为例
Table Attributes 指的是 props
props data 显示的数据
Table Events 指的是 自定义事件
自定义事件 selection-change 当选择项发生变化时会触发该事件
Table Slot 插槽
append 插入至表格最后一行之后的内容,如果需要对表格的内容进行无限滚动操作,可能需要用到这个 slot。若表格有合计行,该 slot 会位于合计行之上。
Table-column Scoped Slot 插槽及作用域插槽 slot
Table-column 是 el-table-column组件,有一个默认插槽 自定义列的内容,参数为 { row, column, $index }
我们通常这样使用
查看
编辑
slot-scope="scope" 父组件通过scope访问子组件作用域。
Table Methods 指的是 组件方法
组件方法 clearSelection 用于多选表格,清空用户的选择 。 组件方法是通过 添加ref 索引,获取组件实例后调用。this.$refs.组件ref标识.组件方法
以上是组件核心概念。
二、组件注册
1、组件名
组件名应该始终是多个单词的,根组件 App 除外
这样做可以避免跟现有的以及未来的 HTML 元素相冲突,因为所有的 HTML 元素名称都是单个单词的。
单文件组件的文件名应该要么始终是单词大写开头 (PascalCase),要么始终是横线连接 (kebab-case)
混用文件命名方式有的时候会导致大小写不敏感的文件系统的问题,这也是横线连接命名同样完全可取的原因
使用 kebab-case
当使用 kebab-case (短横线分隔命名) 定义一个组件时,你也必须在引用这个自定义元素时使用 kebab-case,例如
Vue.component('my-component-name', { /* ... */ })
使用 PascalCase
当使用 PascalCase (驼峰式命名) 定义一个组件时,你在引用这个自定义元素时两种命名法都可以使用。也就是说
Vue.component('MyComponentName', { /* ... */ })
2、全局注册
以上方法都属于全局注册, 也就是说它们在注册之后可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中, 比如
HTML
JS
Vue.component('component-a', { /* ... */ })
Vue.component('component-b', { /* ... */ })
Vue.component('component-c', { /* ... */ })
new Vue({ el: '#app' })
在所有子组件中也是如此,也就是说这三个组件在各自内部也都可以相互使用.
3、局部注册
如果不需要全局注册,或者是让组件使用在其它组件内,可以用选项对象的 components 属性实现局部注册, 这里不做详述。
三、父子组件通信
在vue组件通信中其中最常见通信方式就是父子组件之中的通信,而父子组件的设定方式在不同情况下又各有不同。最常见的就是父组件为控制组件子组件为视图组件。父组件传递数据给子组件使用,遇到业务逻辑操作时子组件触发父组件的自定义事件。无论哪种组织方式父子组件的通信方式都是大同小异
父组件到子组件通讯
父组件到子组件的通讯主要为:子组件接受使用父组件的数据,这里的数据包括属性和方法(String, Number, Boolean, Object, Array, Function)。vue提倡单项数据流,因此在通常情况下都是父组件传递数据给子组件使用,子组件触发父组件的事件,并传递给父组件所需要的参数
通过 props 传递数据 (推荐)
父子通讯中最常见的数据传递方式就是通过props传递数据,就好像方法的传参一样,父组件调用子组件并传入数据,子组件接受到父组件传递的数据进行验证使用
props 可以是数组或对象,用于接收来自父组件的数据。props 可以是简单的数组,或者使用对象作为替代,对象允许配置高级选项,如类型检测、自定义校验和设置默认值
prop 的定义应该尽量详细,至少需要指定其类型
{{ parentMessage }}
子组件到父组件通讯
子组件到父组件的通讯主要为父组件如何接受子组件之中的数据。这里的数据包括属性和方法(String, Number, Boolean, Object, Array, Function)
通过 $emit 传递父组件数据 (推荐)
与父组件到子组件通讯中的$on配套使用,可以向父组件中触发的方法传递参数供父组件使用
子组件
不推荐的通信方式
this.children
this.$refs
四、兄弟组件通信
1、vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,它是响应式的,状态发生变化,组件会更新。
vuex实现兄弟组件通信非常简单,组件A引用vuex数据,组件B通过方法改变vuex数据,vuex状态是响应式的,数据放生变化,组件A会更新。
2、eventBus又称为事件总线
在vue中可以使用eventBus来作为沟通桥梁, 就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件, 所有组件都可以通知其他组件。
初始化
首先需要创建一个事件总线并将其导出, 以便其他模块可以使用或者监听它。
我们在src/components/目录下新建文件bus.js。
import Vue from 'vue'
export default new Vue()
发送事件
假设你有两个兄弟组件: ComA和ComB,ComA发送消息给ComB。
ComA这样
很显然,ComA中使用bus.$emit(事件名,数据);向事件中心注册发送事件。
接收事件
ComB接受ComA发送过来的消息。
{{fromA}}
于是,当ComA发送了一个手机号码phone给ComB时,comB就会接收并显示。
父组件
在父组件中调用ComA和ComB两个兄弟组件。
五、多层级组件通讯
1、vuex
2、eventBus
3、provide / inject
provide 和 inject 主要在开发高阶插件/组件库时使用。并不推荐用于普通应用程序代码中。
这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。
// 父级组件提供 'foo'
var Provider = {
provide: {
foo: 'bar'
},
// ...
}
// 子组件注入 'foo'
var Child = {
inject: ['foo'],
created () {
console.log(this.foo) // => "bar"
}
// ...
}
六、作用域插槽
有时让插槽内容能够访问子组件中才有的数据是很有用的。例如,设想一个带有如下模板的
{{ user.lastName }}
我们可能想换掉备用内容,用名而非姓来显示。如下:
{{ user.firstName }}
然而上述代码不会正常工作,因为只有
为了让 user 在父级的插槽内容中可用,我们可以将 user 作为
{{ user.lastName }}
绑定在
{{ slotProps.user.firstName }}
在这个例子中,我们选择将包含所有插槽 prop 的对象命名为 slotProps,但你也可以使用任意你喜欢的名字。
七、组件的方法
组件方法,就是我们写组件时,在methods选项里边定义的一些方法,他通常是对数据的CURD。
element-ui我们常用的有
this.refs.[treeName].getCheckedKeys() tree 返回目前被选中的节点的 key 所组成的数组
八、vue 语法糖
1、v-model
v-model可以实现数据双向的绑定,自动为组件添加了props 名为 value 和 自定义事件 名为 input。
实际上,上面的代码是下面代码的语法糖。
自定义组件的 v-model
一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是像单选框、复选框等类型的输入控件可能会将 value attribute 用于不同的目的。model 选项可以用来避免这样的冲突:
Vue.component('base-checkbox', {
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean
},
template: `
`
})
现在在这个组件上使用 v-model 的时候:
这里的 lovingVue 的值将会传入这个名为 checked 的 prop。同时当
2、.sync 修饰符
在有些情况下,我们可能需要对一个 prop 进行“双向绑定”。不幸的是,真正的双向绑定会带来维护上的问题,因为子组件可以变更父组件,且在父组件和子组件都没有明显的变更来源。
这也是为什么我们推荐以 update:myPropName 的模式触发事件取而代之。举个例子,在一个包含 title prop 的假设的组件中,我们可以用以下方法表达对其赋新值的意图:
this.$emit('update:title', newTitle)
然后父组件可以监听那个事件并根据需要更新一个本地的数据 property。例如:
为了方便起见,我们为这种模式提供一个缩写,即 .sync 修饰符:
九、组件选项推荐顺序
十、组件样式推荐使用CSS 作用域