什么时候封装成组件? 什么时候封装成指令?
一般情况下, 如果要封装的代码中, 包含大量的 HTML,就需要用组件,反之如果仅仅对某一个 DOM/组件的根 DOM 进行操作,那么可以选择封装成指令.
"拖拽"等手势的识别仅仅是对某一个元素/组件上的"mouse/touch"进行识别,并不涉及DOM操作, 所以这里封装就选择"指令".
最终目标(v-touch指令)
先看下目标, 然后分析.
// main.js
import VTouch from '@any-touch/vue3';
// ... 省略
const app = createApp(App);
app.mountd('#app');
app.use(VTouch);
<template>
<u-component
v-touch
@tap="onTap"
@swipe="onSwipe"
@press="onPress"
@pan="onPan"
@pinch="onPinch"
@rotate="onRotate"
>
</u-component>
</template>
- 既然要用
app.use
初始化, 那我们就需要封装成 vue 的插件, 插件是有固有格式的, 后面我们开发写. - 需要支持手势, 这里使用无依赖的js手势识别库any-touch.
知识点(🚀小技巧)
在vue中, 自定义的DOM事件,可以直接通过"@"语法接收, 所以实际我们可以自己实现任意"xx"事件, 最终都可以通过"@xx"来监听.
const event = new Event('xx');
this.$refs.xxEl.dispatchEvent(event);
'any-touch'内部触发的手势事件都是触发的原生DOM事件, 所以vue都可以直接监听到, 就好像"@tap"等.
写插件的格式
vue 的插件首先需要是一个对象, 其包含一个键值install
, 对应值为函数, 且参数为 vue 实例, 也就是这样:
export default {
install: (app) => {
// 逻辑
},
};
组件也是有生命周期的, 比如mounted
和unmounted
, 他们分别代表指令所在元素"加载完毕后执行"和"销毁时执行".
这里我们用这2个钩子,来执行any-touch
的初始化和销毁工作.
下面我们开始封装"v-touch"指令, 注意: 代码中我们给指令起名叫"touch", 但在组件中使用的时候要写"v-touch","v-"开头的属性, vue会知道他是指令.
import ATouch from 'any-touch';
const elAndAtMap = new WeakMap();
export default {
install: (app) => {
app.directive('touch', {
mounted(el) {
// 初始化
const at = new ATouch(el);
elAndAtMap.set(el, at);
},
unmounted(el: SupportElement) {
// 销毁
elAndAtMap.get(el).destroy();
},
});
},
};
这里用WeakMap
来存储每次使用使用"v-touch"时候生成的"any-touch"实例. 写到这功能实现完成了. 但是我们目标是用ts写, 所以请继续向下看.
typescript
首先我们要引入会用到的类型,用来对我们的代码进行标注.
import type { App, DirectiveBinding } from 'vue';
import type { Options, SupportElement } from 'any-touch';
App
表示vue实例类型.DirectiveBinding
表示指令的参数类型. 如果需要对指令的值进行约束, 比如限制v-touch=
的值只能是数字类型, 就需要写成DirectiveBinding<number>
.Options
是any-touch
的参数类型.SupportElement
是any-touch
支持的元素类型, 实际就是HTMLElement|SvgElement
.
指令的值怎么获取
下面的代码中给v-touch
指令增加了一个参数值, 通过指令的值给any-touch
传参, 值我们通过mounted
钩子函数的第二个参数获取, 参数是个对象, 其的"value"字段就是v-touch=
后面的值.
既然any-touch的参数就是指令的值, 那么我们标记第二个参数的类型为:DirectiveBinding<Options>
mounted(el: SupportElement, binding: DirectiveBinding<Options>) {}
完整代码
下面我们把类型都标记上.
import { App, DirectiveBinding } from 'vue';
import type { Options, SupportElement } from 'any-touch';
import ATouch from 'any-touch';
const elAndAtMap = new WeakMap();
export default {
install: (app: App) => {
app.directive('touch', {
mounted(el: SupportElement, { value }: DirectiveBinding<Options>) {
elAndAtMap.set(el, new ATouch(el, value));
},
unmounted(el: SupportElement) {
elAndAtMap.get(el).destroy();
},
});
},
};
到这里就都实现完毕了, 如果小伙伴对ts的知识不了解, 可以看看我的ts基础课程.
typescript入门基础
特别篇, 在vue3🔥源码中学会typescript🦕 - "is"
第六课, 什么是声明文件(declare)? 🦕 - 全局声明篇
第七课, 通过vue3实例说说declare module语法怎么用🦕模块声明篇
🍕学习互动
感谢大家的阅读, 如有疑问可以加我微信, 我拉你进入微信群(由于腾讯对微信群的100人限制, 超过100人后必须由群成员拉入)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。