1.事件绑定和解绑
通常,我们会在mounted中给dom绑定事件,然后在beforeDestroy中解绑。不过,有的时候,绑定和解绑写在两个不同的函数里面会觉得有些麻烦,也不利于维护。其实,可以通过$once+hook将事件绑定和解绑写在一起。
原写法:
public mounted() {this.targetRef.addEventListener("click", this.add, false);}public beforeDestroy() {this.targetRef.removeEventListener("click", this.add);}
新写法
public mounted() {this.initEvent();}private initEvent() {this.targetRef.addEventListener("click", this.add, false);this.$once("hook:beforeDestroy", () => {this.targetRef.removeEventListener("click", this.add);});}
这样,同一个功能就内聚了。
类似的,清除定时器也可以这么写。
2.父组件传递事件监听和属性
在开发中,曾有一些这样的需求。我们要对某个组件做一个包装,比如给按钮包装一个指令,在不需要修改逻辑的情况下快速实现一些额外的功能。
那么,原组件可能有很多属性和事件监听,如果一个一个写是很麻烦的,那么有没有一个快速简单的方式传递所有prop和事件监听给子组件呢?
答案是有的。用v-on和v-bind实现。看下面的demo
包装的组件
<template><div class="proxy"><a-button v-bind="$attrs" v-on="$listeners" @click="testClick"></a-button></div>
</template>
使用:
<proxy @click="bbb"></proxy>
结果是testClick和bbb的方法都会被触发。
在本人做过的项目中,曾经要给已做好的项目的一些按钮添加埋点功能,就用了这种包装button的方式快速实现,只需要替换按钮组件即可,不需要修改其他的逻辑代码。
3.自定义v-model
曾经有这么一个需求,要给select组件添加自定义的搜索框,并且,样式和组件库自带的搜索不一样。那没办法,只能自己封装了。
用v-model绑定一个属性,那么怎么穿透到子组件呢。
默认组件的v-model,是传递一个value的props,触发子组件的input事件,就会修改绑定的这个属性。看下面的demo
子组件:
@Prop({ type: Number })public value!: number;public timer = -1;public mounted() {this.timer = window.setInterval(() => {console.log(1111);this.$emit("input", this.value + 1);}, 1e3);this.$once("hook:beforeDestroy", () => {window.clearInterval(this.timer);});}
父组件:
<proxy v-model="count"></proxy>
这样,子组件就能修改它v-model绑定的属性了
如果想指定属性名和方法呢?可以在子组件通过model选项来控制
@Model("myEvent")public myProp!: number;public timer = -1;public mounted() {this.timer = window.setInterval(() => {console.log(1111);this.$emit("myEvent", this.myProp + 1);}, 1e3);this.$once("hook:beforeDestroy", () => {window.clearInterval(this.timer);});}
非ts项目,写选项就行了
model:{prop:'myProp',event:'myEvent'}
4.纯组件
函数式编程有纯函数,react用来渲染只负责渲染的组件(通俗的说就是没有逻辑的组件),其实vue也有类似的,没有额外的hook,只用于渲染的组件
在template标签里加上functional属性即可。纯组件不能有自己的方法、选项等,只有一个context会被传递进来,里面有props
看一个demo
<template functional><div>{{props.count}}</div>
</template>
5.路由参数解耦
在路由上经常需要携带参数,不管是query还是params,都需要在组件内通过路由实例去获取。
其实,我们可以将这些参数作为props传递进组件,这样在获取的时候就可以将参数解耦。
下面这些是定义在路由文件上的:
{path: "/event",name: "event",component: () =>import(/* webpackChunkName: "event" */ "./components/event.vue"),props: (route: Route) => {const { id, name } = route.query;return {id,name,};},},
props的属性可以是布尔值、对象、函数。
如果props属性为true,那么所有params上的属性都会转换成prop属性传递给组件。如果是对象,那么只有为true的key对应的组件才会有params作为prop传递进来。
这里拿官网的例子:
{path: '/user/:id',components: { default: User, sidebar: Sidebar },props: { default: true, sidebar: false }}
props为函数的话,就是上面我们写的例子。
6.响应数组的变化
我们知道,在当前版本,由于defineProperty的限制,通过数组下标修改数组的值是没办法响应的。(年底vue3应该会正式发布,到时就没这个问题了)
public list = [1, 2, 3, 4];public editList() {this.list[1] = 3;}
在上面的方法中,editList方法的确修改了数组的值,但是页面上是不会变化的,。
有下面几种方式让页面响应
1:用$set
// this.list[1] = 3;this.$set(this.list, 1, 3);
$set实际上是让vue重新对数组进行一次数据监听,即初始化defineProperty。
2.借用数组的方法
const methodsToPatch = ['push','pop','shift','unshift','splice','sort','reverse'
]
上面是vue的源码,vue对数组的这些方法做了代理,使用这些方法会触发数组的更新,
所以,在修改数组的值之后,我们手动调用无副作用的方法,就能触发页面的更新。无副作用的方法包括push、unshift、splice。
this.list[1] = 3;this.list.push();this.list.unshift();this.list.splice();
7.监听子组件的生命周期
如果想监听子组件初始化或者挂载完成的事件,一般做法是给子组件绑定事件,然后子组件在对应的生命周期emit出去,触发父组件的handle。
子组件:
public created() {this.$emit("hasCreated");}
父组件:
<proxy @hasCreated="handler"></proxy>
这样其实很麻烦,有的时候子组件并没有额外的逻辑,是父组件需要,但是我们要在子组件一个个生命周期方法里去添加。
我们可以用更简单的方式去监听:
父组件:
<proxy @hook:created="handler"></proxy>
hook后面的生命周期名字,想监听什么生命周期就写对应的方法名,不需要在子组件写,非常的方便