什麼時候封裝成元件? 什麼時候封裝成指令?
一般情況下, 如果要封裝的程式碼中, 包含大量的 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人後必須由群成員拉入)