vue3中什麼時候封裝成元件? 什麼時候封裝成指令? 通過例項告訴你(typescript)

鐵皮飯盒發表於2022-03-31

什麼時候封裝成元件? 什麼時候封裝成指令?

一般情況下, 如果要封裝的程式碼中, 包含大量的 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>
  1. 既然要用app.use初始化, 那我們就需要封裝成 vue 的外掛, 外掛是有固有格式的, 後面我們開發寫.
  2. 需要支援手勢, 這裡使用無依賴的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) => {
        // 邏輯
    },
};

元件也是有生命週期的, 比如mountedunmounted, 他們分別代表指令所在元素"載入完畢後執行"和"銷燬時執行".

這裡我們用這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';
  1. App表示vue例項型別.
  2. DirectiveBinding表示指令的引數型別. 如果需要對指令的進行約束, 比如限制v-touch=的值只能是數字型別, 就需要寫成DirectiveBinding<number>.
  3. Optionsany-touch的引數型別.
  4. SupportElementany-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人後必須由群成員拉入)

相關文章