什么时候封装成组件? 什么时候封装成指令?
个别状况下, 如果要封装的代码中, 蕴含大量的 HTML,就须要用组件,反之如果仅仅对某一个 DOM/组件的根 DOM 进行操作,那么能够抉择封装成指令.
"拖拽"等手势的辨认仅仅是对某一个元素/组件上的"mouse/touch"进行辨认,并不波及DOM操作, 所以这里封装就抉择"指令".
最终目标(v-touch指令)
先看下指标, 而后剖析.
// main.jsimport 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入门根底
第一课, 体验typescript
第二课, 根底类型和入门高级类型
第三课, 泛型
第四课, 解读高级类型
第五课, 命名空间(namespace)是什么
特别篇, 在vue3源码中学会typescript - "is"
第六课, 什么是申明文件(declare)? - 全局申明篇
第七课, 通过vue3实例说说declare module语法怎么用模块申明篇
学习互动
感激大家的浏览, 如有疑难能够加我微信, 我拉你进入微信群(因为腾讯对微信群的100人限度, 超过100人后必须由群成员拉入)