前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Vue组件开发三板斧:prop、event、slot

Vue组件开发三板斧:prop、event、slot

作者头像
娜姐
发布2020-09-22 10:26:23
2.1K0
发布2020-09-22 10:26:23
举报
文章被收录于专栏:娜姐聊前端

组件,有些可以完全独立运行完全不依赖外部属性,比如层级较高的页面组件。但大多时候,组件还是需要使用方做一些定制操作,并可以在状态变化时通知给使用方,于是,一个组件最基本的API就是prop、event、slot,只要了解它们,那么再复杂的功能也能够做出来。

1.组件属性定义 prop

prop 定义了这个组件有哪些可配置的属性,可通过父组件传递进来。如果是对象类型,比如对象或数组,它的默认值必须从一个工厂函数获取。 格式如下:

代码语言:javascript
复制
// 子组件  my-component
<template>
    <div>
       <span v-for="name in columns" :key="name">{{name}}</span>
    </div>
</template>
<script>
    export default {
        props: {
            total: {
                type: Number, 
                default: 0
            },
            show: {
                type: Boolean, 
                default: true
            },
            data: {
                type: Object,  // 对象类型
                default () {
                    return {}
                }
            },
            columns: {
                type: Array,  // 对象类型
                default () {
                    return []
                }
            },
            value: {
              type: [String, Number, Boolean],  // 支持多种类型
              default: false
            },
        }
    }
</script>

很多情况下,组件的数据属性是动态的。通常,父组件通过接口拉取数据,然后再更新组件属性数据。如果不做些特殊处理,你会发现,当组件属性变化时,组件不会更新。 这时,需要用watch监听组件属性,同时在组件内声明一个变量,当监听的属性发生变化时,同步修改该变量。 模板中,不要直接使用属性,而改为组件变量值。

代码语言:javascript
复制
props: {
   show: {
     type: Boolean,
      default: true
   }
 },
data() {
   return {
     isVisible: true
   };
 },
watch: {
   show: function(val) {
     this.isVisible = val;
   }
 }

上面的例子比较简单,监听了一个简单类型属性。如果是对象类型,且该对象可能包含子对象,且属性发生变更就是内部子对象变化,那么,需要用到深度监听。 那么,改造一下props: data的监听器。

代码语言:javascript
复制
watch: {
    data: {
      deep: true,
      immediate: true,
      handler: function(val) {
        this.formData = { ...val };
      }
    }
  },

这样看起来貌似完美了。但是,如果父组件定义了一个空对象属性数据,但真实值是复杂对象呢?

代码语言:javascript
复制
// 父组件
<template>
    <div>
       <my-component :data={data}  />
    </div>
</template>
<script>
export default {
    data() {
      return {
        // 定义了一个空对象
        data: { }
  };
},
mounted() {
   // 返回值包含对象
    this.data = {list: [1,2,3], size: 3}
},
</script>

运行之后你会发现,哪怕对组件属性data做了深度监听,但是,当数据发生变化时,不会触发watch。 为什么呢? 很简单,Vue2.0用的是Object.defineProperties监听数据变化,而此方法不能监听到对象新增或者删除操作。 解法也很简单,在初始化组件数据时,把可能用到的子对象都定义好。

代码语言:javascript
复制
export default {
    data() {
      return {
        data: { list:[], size:0 }
  };
},

然后再试试,OK了。

吐槽一下,相同功能,React不需要都这么大弯子。当组件属性变化,React会自动触发组件的render函数。这是因为Vue本身对组件更新渲染做了底层处理,而React把这些交给使用者,使用方可根据shouldComponentUpdate做优化

2. 组件间通信 event

$emit函数

这个比较简单,当组件状态发生变化或者完成某些操作后,需要通知父组件,调用$emit函数。

代码语言:javascript
复制
// 子组件
export default {
  methods: {
    onSubmit() {
      this.$emit("getList", this.formData);
    }
  }
}
// 父组件
<template>
  <my-component @getList="getDataList" />
</template>
export default {
  methods: {
    getDataList(formData) {
      // doing something
    }
  }
}

除了$emit,还有另外两种方式(这两种方法的弊端是,无法在兄弟间通信):

  • ref:给元素或组件注册引用信息;
  • parent / children:访问父 / 子实例。
ref引用
代码语言:javascript
复制
// 子组件:my-component
export default {
  data () {
    return {
      title: 'my component'
    }
  },
  methods: {
    sayHello () {
      window.alert('Hello');
    }
  }
}
代码语言:javascript
复制
// 父组件
<template>
  <my-component ref="myCom"></my-component>
</template>
<script>
  export default {
    mounted () {
      const com = this.$refs.myCom;
      console.log(com.title);  // my component
      com.sayHello();  // 弹窗
    }
  }
</script>

ref最常见的就是表单引用,可以在父组件里面做一些表单验证,或者表单清理的工作。

代码语言:javascript
复制
// form是组件的ref
// mainForm是组件内表单的ref
this.$refs.form.$refs.mainForm.clearValidate();
$parent$children

基于当前上下文访问父组件或全部子组件。 场景:使用element-ui的抽屉组件时,'visible'值很重要。如果把它设置为组件内部变量,同时监听属性'visible',那么,当关闭抽屉时,必须通知父组件更新属性变量(打开对话框的行为,是父组件发起的)。如果不更新父组件状态,那么关闭之后再次点击按钮不会触发属性变化,那么自然watch事件不会被触发。

代码语言:javascript
复制
// 子组件 my-drawer
<template>
 <el-drawer title="修订记录" :visible.sync="isDrawerVisible" :before-close="handleClose">
  </el-drawer>
</template>
export default {
  props: {
    visible: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      isDrawerVisible: false
    };
  },
watch: {
    visible: function(val) {
      this.isDrawerVisible = val;
    }
  },
methods: {
    handleClose() {
      // 修改父组件变量值
      this.$parent.visible = false;
    },
}
代码语言:javascript
复制
// 父组件
<template>
    <my-drawer :visible="visible" />
</template>
export default {
  data() {
    return {
      visible: false
    };
  }

3. 插槽 slot

用于分发组件的内容。这个官网文档讲解很清楚:https://cn.vuejs.org/v2/guide/components-slots.html

场景:比较多了。比如我们可能通过 slot做顶层的统一页面布局:

代码语言:javascript
复制
<template>
  <div>
    <head-top></head-top>
    <div class="container_wrap">
      <slot></slot>
    </div>
  </div>
</template>

<script>
import headTop from '@/components/headTop'

export default {
  name: 'layout',
  components: {
    headTop
  }
}

这样,父级内定义的内容,就会出现在组件对应的 slot 里,没有写名字的,就是默认的 slot。

代码语言:javascript
复制
// 使用 layout
<template>
  <layout>
    <el-button icon="el-icon-plus" size="small" type="primary" @click="onAdd">新建</el-button>
 </layout>
</template>

当需要多个插槽时,会用到具名 slot,比如:

代码语言:javascript
复制
// 子组件 my-button
<template>
  <button>
    <slot name="icon"></slot>
    <slot></slot>
  </button>
</template>
代码语言:javascript
复制
// 使用my-button
<my-button>
  <i slot="icon" class="el-icon-link" />
  按钮 1
</my-button>

小结

掌握了 Vue.js 组件的这三个 API 后,剩下的便是逻辑设计。 在组件开发中,最难的应当是解耦组件的交互逻辑,抽象业务能力,尽量把复杂的逻辑分发到不同的子组件中,然后彼此建立联系。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.组件属性定义 prop
  • 2. 组件间通信 event
    • $emit函数
      • ref引用
        • $parent 和 $children
        • 3. 插槽 slot
        • 小结
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档