Vue组件开发三板斧:prop、event、slot
组件,有些可以完全独立运行完全不依赖外部属性,比如层级较高的页面组件。但大多时候,组件还是需要使用方做一些定制操作,并可以在状态变化时通知给使用方,于是,一个组件最基本的API就是prop、event、slot,只要了解它们,那么再复杂的功能也能够做出来。
1.组件属性定义 prop
prop
定义了这个组件有哪些可配置的属性,可通过父组件传递进来。如果是对象类型,比如对象或数组,它的默认值必须从一个工厂函数获取。
格式如下:
// 子组件 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
监听组件属性,同时在组件内声明一个变量,当监听的属性发生变化时,同步修改该变量。
模板中,不要直接使用属性,而改为组件变量值。
props: {
show: {
type: Boolean,
default: true
}
},
data() {
return {
isVisible: true
};
},
watch: {
show: function(val) {
this.isVisible = val;
}
}
上面的例子比较简单,监听了一个简单类型属性。如果是对象类型,且该对象可能包含子对象,且属性发生变更就是内部子对象变化,那么,需要用到深度监听。
那么,改造一下props: data
的监听器。
watch: {
data: {
deep: true,
immediate: true,
handler: function(val) {
this.formData = { ...val };
}
}
},
这样看起来貌似完美了。但是,如果父组件定义了一个空对象属性数据,但真实值是复杂对象呢?
// 父组件
<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
监听数据变化,而此方法不能监听到对象新增或者删除操作。
解法也很简单,在初始化组件数据时,把可能用到的子对象都定义好。
export default {
data() {
return {
data: { list:[], size:0 }
};
},
然后再试试,OK了。
吐槽一下,相同功能,React不需要都这么大弯子。当组件属性变化,React会自动触发组件的
render
函数。这是因为Vue本身对组件更新渲染做了底层处理,而React把这些交给使用者,使用方可根据shouldComponentUpdate
做优化
2. 组件间通信 event
$emit
函数
这个比较简单,当组件状态发生变化或者完成某些操作后,需要通知父组件,调用$emit
函数。
// 子组件
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
引用
// 子组件:my-component
export default {
data () {
return {
title: 'my component'
}
},
methods: {
sayHello () {
window.alert('Hello');
}
}
}
// 父组件
<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
最常见的就是表单引用,可以在父组件里面做一些表单验证,或者表单清理的工作。
// form是组件的ref
// mainForm是组件内表单的ref
this.$refs.form.$refs.mainForm.clearValidate();
$parent
和 $children
基于当前上下文访问父组件或全部子组件。
场景:使用element-ui
的抽屉组件时,'visible'值很重要。如果把它设置为组件内部变量,同时监听属性'visible',那么,当关闭抽屉时,必须通知父组件更新属性变量(打开对话框的行为,是父组件发起的)。如果不更新父组件状态,那么关闭之后再次点击按钮不会触发属性变化,那么自然watch
事件不会被触发。
// 子组件 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;
},
}
// 父组件
<template>
<my-drawer :visible="visible" />
</template>
export default {
data() {
return {
visible: false
};
}
3. 插槽 slot
用于分发组件的内容。这个官网文档讲解很清楚:https://cn.vuejs.org/v2/guide/components-slots.html
场景:比较多了。比如我们可能通过 slot
做顶层的统一页面布局:
<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。
// 使用 layout
<template>
<layout>
<el-button icon="el-icon-plus" size="small" type="primary" @click="onAdd">新建</el-button>
</layout>
</template>
当需要多个插槽时,会用到具名 slot,比如:
// 子组件 my-button
<template>
<button>
<slot name="icon"></slot>
<slot></slot>
</button>
</template>
// 使用my-button
<my-button>
<i slot="icon" class="el-icon-link" />
按钮 1
</my-button>
小结
掌握了 Vue.js 组件的这三个 API 后,剩下的便是逻辑设计。 在组件开发中,最难的应当是解耦组件的交互逻辑,抽象业务能力,尽量把复杂的逻辑分发到不同的子组件中,然后彼此建立联系。
- Python内置函数property()使用实例
- 区块链技术的历史发展的不同阶段
- 微信力推搜一搜,培养用户微信搜索习惯,搜一搜直接给红包
- datetime
- [转自JeffreyZhao]在LINQ to SQL中使用Translate方法以及修改查询用SQL
- Flask-SQLAlchemy
- 2017全球存储大会:存储设备面临三大挑战
- cobbler自动安装系统(Centos7.X)
- Python接口自动化-7-unittest
- cobbler自动安装系统(Centos7.X)
- Linux NTP时间服务器
- 子查询的另一种方式——映射
- LNMP架构之搭建wordpress博客网站
- Nginx的各种报错总结
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- PHP中的类
- PHP获取表单textarea数据中的换行问题
- Numpy根据Bool值挑选数组中元素
- MySQL获取每个分类下面的前三条数据
- windows宿主机如何SSH连接VMware的Linux虚拟机
- np.random.rand均匀分布随机数和np.random.randn正态分布随机数函数使用方法
- Java FileReader使用相对路径读取文件
- MySQL批量插入数据库实现语句性能分析
- np.repeat用法
- MySQL优化INSERT的性能
- PHP在函数体中传递与接收参数
- 使用Arraylist将数组中元素随机均等乱序分为N个子数组
- PHP中abstract 和 interface的区别
- PHP5中的魔术方法
- 一起来学演化计算-matlab基本函数randn,rand, orth