Skip to content

Commit

Permalink
attrs
Browse files Browse the repository at this point in the history
  • Loading branch information
wuyawei committed Mar 23, 2019
1 parent e242775 commit cfb9f6d
Showing 1 changed file with 111 additions and 3 deletions.
114 changes: 111 additions & 3 deletions vue/组件间通信/1.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
## 前言
Vue.js 在现今使用有多广泛不用多说,而 Vue 的一大特点就是组件化。本期要讲的,便是 Vue 组件间通信方式的总结,这也几乎是近年 Vue 面试中的必考题。注:文中示例都基于 Vue 脚手架讲解。
Vue.js 在现今使用有多广泛不用多说,而 Vue 的一大特点就是组件化。本期要讲的,便是 Vue 组件间通信方式的总结,这也几乎是近年 Vue 面试中的必考题。注:文中示例都基于 Vue 脚手架讲解,会用到一些 [Element UI](http://element-cn.eleme.io/#/zh-CN/component/input) 示例
* 文中示例依然在 [🍧🍭😻 webrtc-stream](https://github.com/wuyawei/webrtc-stream)
* 文章仓库 [🍹🍰 fe-code](https://github.com/wuyawei/fe-code)

Expand Down Expand Up @@ -111,5 +111,113 @@ let obj = JSON.parse(JSON.stringify(obj));
inheritAttrs: false,
props: ['title']
```
利用 inheritAttrs,我们可以方便的把组件绑定的其它特性,转移到我们指定的元素上。这就需要用到下一个我们要讲的 $attrs 了。
### $attrs
利用 inheritAttrs,我们还可以方便的把组件绑定的其它特性,转移到我们指定的元素上。这就需要用到下一个我们要讲的 `$attrs` 了。
### attrs、listeners
我们在使用组件库的时候经常会这么写:
``` javascript
<el-input v-model="input" placeholder="请输入内容"></el-input>
```
实际渲染后:

![](https://user-gold-cdn.xitu.io/2019/3/23/169aad7bc51ca555?w=645&h=190&f=png&s=5050)

可以看到我们指定的的 placeholder 是渲染在 input 上的,但是 input 并不是根元素。难道都用 Props 声明后,再赋值给 input?这种情况就可以用到 `$attrs` 了,改造一下我们之前那个栗子。
``` javascript
// communication.vue
<template>
<div class="communication">
<communication-sub v-bind="dataProps" class="input" type="text" placeholder="请输入内容">
</communication-sub>
</div>
</template>
<script>
import communicationSub from './communication-sub.vue';
export default{
name: 'communication',
data() {
return {
dataProps: {
title: '我是 communication 的值',
}
}
},
components: {
communicationSub
}
}
</script>

// communication-sub.vue
···
<div class="communication-sub">
<input v-bind="$attrs" v-model="title"></input>
</div>
···
export default {
inheritAttrs: false
}
```

![](https://user-gold-cdn.xitu.io/2019/3/23/169ab45f5907f85a?w=630&h=29&f=png&s=2745)

可以看到,type 已经转移到了子元素 input 标签上,但是 class 没有。这是因为 `inheritAttrs: false` 选项不会影响 style 和 class 的绑定。**可以看出 `$attrs` 则是将没有被组件内部 Props 声明的传值(也叫非 Props 特性)收集起来的一个对象**,再通过 v-bind 将其绑定在指定元素上。这也是 Element 等组件库采用的策略。

**这里需要注意一点,通过 $attrs 指定给元素的属性,不会与该元素原有属性发生合并或替换,而是以原有属性为准**。举个例子,假如我将上述 input 的 type 默认设置为 password。
``` javascript
<input v-bind="$attrs" v-model="title" type="password"></input>
```
则不会采用 $attrs 中的 type: 'text',将以 password 为准,所以如果需要默认值的属性,建议不要用这种方式。

`$listeners``$attrs` 类似,**可以看做是一个包含了组件上所有事件监听器(包括自定义事件、不包括.native修饰的事件)的对象**。它也支持上述的写法,适用于将事件安放于组件内指定元素上。
``` javascript
// communication.vue
<communication-sub v-bind="dataProps"
class="input"
type="text"
placeholder="请输入内容"
@focus="onFocus" >
</communication-sub>
···
methods: {
onFocus() {
console.log('onFocus');
}
}
// communication-sub.vue
<input v-bind="$attrs" v-model="title" v-on="$listeners"></input>
```
给之前的栗子绑定一个聚焦事件,在子组件中通过 `$listeners` 绑定给 input,则会在 input 聚焦时触发。

那么除了用在这种给组件内指定元素绑定特性和事件的情况,还有哪些场景可以用到呢?官方说明:在创建更高层次的组件时非常有用。比如在祖孙组件中传递数据,在孙子组件中触发事件后要在祖辈中做相应更新。我们继续之前的栗子:在孙辈组件触发点击事件,然后在祖辈中修改相应的 data。

![](https://user-gold-cdn.xitu.io/2019/3/24/169ab66f3b29cf39?w=1084&h=172&f=gif&s=16201)
``` javascript
// communication.vue
<communication-sub v-bind="dataProps" @click="onMyclick">
</communication-sub>
···
methods: {
onMyclick() {
this.dataProps.title = '我是点击之后的值';
}
};

// communication-sub.vue
<communication-min-sub v-on="$listeners"></communication-min-sub> // 子组件中将事件透传到孙辈

// communication-min-sub.vue
<template>
<div class="communication-min-sub">
<p>我是 communication-min-sub</p>
<button v-on="$listeners">click</button>
</div>
</template>
<script>
export default{
name: 'communication-min-sub',
inheritAttrs: false
}
</script>
```
这样就能很方便的在多级组件的子级组件快速访问到父组件中的数据和方法。在刚才的例子中,button 点击时,是直接调用的 communication.vue 中定义的方法。
### emit、v-on

0 comments on commit cfb9f6d

Please sign in to comment.