Skip to content

MediaPipe với Vue: Ưu và Nhược điểm

Mục tiêu bài học

  • Hiểu ưu điểm khi dùng MediaPipe trong Vue
  • Nắm rõ nhược điểm và cách giải quyết
  • So sánh với React và frameworks khác
  • Đưa ra recommendations cho dự án

Ưu điểm (Pros)

1. Reactivity System mạnh mẽ

Vue 3 Reactivity giúp quản lý state dễ dàng:

typescript
// Tự động update UI khi state thay đổi
const faceDetected = ref(false);
const landmarksCount = ref(0);

wrapper.onResults((results) => {
  // Update state → UI tự động re-render
  faceDetected.value = !!results.multiFaceLandmarks?.length;
  landmarksCount.value = results.multiFaceLandmarks?.[0]?.length ?? 0;
});

Lợi ích:

  • Không cần thao tác DOM thủ công
  • Đồng bộ trạng thái tự động
  • Code sạch hơn

2. Composition API linh hoạt

Dễ dàng tách logic thành composables:

typescript
// composables/useFaceDetection.ts
export function useFaceDetection() {
  const isReady = ref(false);
  const faceDetected = ref(false);
  const wrapper = shallowRef<MediaPipeFaceMeshWrapper | null>(null);

  async function initialize() {
    wrapper.value = new MediaPipeFaceMeshWrapper();
    await wrapper.value.initialize();
    isReady.value = true;
  }

  function cleanup() {
    wrapper.value?.destroy();
    wrapper.value = null;
  }

  return {
    isReady,
    faceDetected,
    initialize,
    cleanup,
  };
}

// Sử dụng trong component
const { isReady, faceDetected, initialize, cleanup } = useFaceDetection();

Lợi ích:

  • Code có thể tái sử dụng
  • Dễ kiểm thử
  • Tách biệt các mối quan tâm

3. Template syntax trực quan

vue
<template>
  <!-- Conditional rendering dễ đọc -->
  <div v-if="!isReady">
    <Lucide icon="Loader" class="animate-spin" />
    <p>{{ t("locale.loading_model") }}</p>
  </div>

  <div v-else-if="!faceDetected">
    <Lucide icon="AlertCircle" />
    <p>{{ t("locale.no_face_detected") }}</p>
  </div>

  <div v-else class="success">
    <Lucide icon="CheckCircle" />
    <p>{{ t("locale.face_detected") }}</p>
    <BtnBase @click="capture">{{ t("locale.capture") }}</BtnBase>
  </div>
</template>

Lợi ích:

  • Template rõ ràng và dễ bảo trì
  • Logic UI tách biệt khỏi logic nghiệp vụ
  • Dễ hiểu

4. TypeScript Integration tốt

typescript
// Type-safe refs
const videoRef = ref<HTMLVideoElement>();
const wrapper = shallowRef<MediaPipeFaceMeshWrapper | null>(null);

// Type-safe props
interface Props {
  onCapture: (image: string) => void;
  minConfidence?: number;
}

const props = withDefaults(defineProps<Props>(), {
  minConfidence: 0.5,
});

5. DevTools mạnh mẽ

Vue DevTools giúp debug dễ dàng:

  • Kiểm tra trạng thái component
  • Theo dõi reactivity
  • Dòng thời gian của lifecycle hooks

Nhược điểm (Cons)

1. ⚠️ Proxy Reactivity Issues

Vấn đề lớn nhất: Vue Proxy không tương thích với WASM.

typescript
// ❌ KHÔNG hoạt động
const wrapper = ref(new MediaPipeFaceMeshWrapper());
const video = ref<HTMLVideoElement>();

// Lỗi: Vue wrap trong Proxy → WASM crash
await wrapper.value.faceMesh.send({ image: video.value });

Giải pháp: Dùng shallowRef hoặc không dùng ref:

typescript
// ✅ Solution 1: shallowRef (shallow reactivity)
const wrapper = shallowRef<MediaPipeFaceMeshWrapper | null>(null);

// ✅ Solution 2: Plain variable (recommended)
let wrapper: MediaPipeFaceMeshWrapper | null = null;

Tác động:

  • Cần hiểu sâu về Vue reactivity
  • Dễ mắc lỗi cho người mới
  • Phải tài liệu hóa rõ ràng

2. ⚠️ Vite Build Complexity

MediaPipe export không consistent giữa dev và production:

typescript
// Dev mode: OK
import { FaceMesh } from '@mediapipe/face_mesh';
const faceMesh = new FaceMesh(...);

// Production build: Lỗi!
// TypeError: FaceMesh is not a constructor

Giải pháp: Custom Vite plugin + wrapper:

typescript
// vite.config.ts
import mediaPipePlugin from "./mediaPipePlugin";

export default defineConfig({
  build: {
    rollupOptions: {
      plugins: [mediaPipePlugin()],
    },
    commonjsOptions: {
      transformMixedEsModules: true,
    },
  },
  optimizeDeps: {
    esbuildOptions: {
      keepNames: true,
    },
  },
});

Tác động:

  • Thiết lập phức tạp hơn React
  • Cần duy trì plugin tùy chỉnh
  • Debug khó khăn

3. ⚠️ Bundle Size

MediaPipe + Vue = large bundle:

dist/
├── mediapipe-face-mesh.js  (~7MB)  ← MediaPipe WASM
├── vendor.js               (~500KB) ← Vue + deps
├── index.js                (~200KB) ← App code
└── Total: ~7.7MB

Giải pháp: Code splitting + lazy loading:

typescript
// Lazy load MediaPipe khi cần
const { MediaPipeFaceMeshWrapper } = await import(
  "@/utils/ekyc/MediaPipeFaceMeshWrapper"
);

Tác động:

  • Tải ban đầu chậm (lần đầu)
  • Cần CDN hoặc chiến lược cache

4. ⚠️ toRaw() Confusion

Developer thường nhầm lẫn khi nào cần toRaw():

typescript
// ❓ Khi nào cần toRaw()?

// KHÔNG cần: DOM element từ ref đã là raw
const video = ref<HTMLVideoElement>();
wrapper.setVideoElement(video.value); // ✅ OK

// CẦN: Nếu object được wrap trong reactive()
const config = reactive({ threshold: 0.5 });
sendToWasm(toRaw(config)); // ✅ Cần toRaw

Tác động:

  • Đường cong học tập cao hơn
  • Dễ gây nhầm lẫn cho team mới

5. ⚠️ Lifecycle Management

Vue có nhiều lifecycle hooks → dễ nhầm lẫn:

typescript
// ❓ Initialize ở đâu?
onMounted(); // ✅ Tốt: DOM đã ready
onBeforeMount(); // ❌ Sớm quá: video chưa có
created(); // ❌ Options API (không dùng)

// ❓ Cleanup ở đâu?
onBeforeUnmount(); // ✅ Tốt: trước khi unmount
onUnmounted(); // ⚠️ Muộn: DOM đã mất

Tác động:

  • Phải hiểu rõ lifecycle
  • Dễ rò rỉ bộ nhớ nếu không dọn dẹp đúng cách

So sánh với React

AspectVue 3React
ReactivityAutomatic (Proxy)Manual (setState)
WASM Integration⚠️ Cần wrapper✅ Direct
Build Setup⚠️ Complex (Vite plugin)✅ Simple
TypeScript✅ Excellent✅ Excellent
Learning Curve⚠️ Higher (proxy issues)✅ Lower
Performance✅ Fast✅ Fast
Bundle SizeSimilarSimilar

React Example (for comparison)

typescript
// React: Không có proxy issues
import { FaceMesh } from '@mediapipe/face_mesh';

function FaceDetection() {
  const [faceDetected, setFaceDetected] = useState(false);
  const videoRef = useRef<HTMLVideoElement>(null);
  const faceMeshRef = useRef<FaceMesh | null>(null);

  useEffect(() => {
    const faceMesh = new FaceMesh({...});
    faceMeshRef.current = faceMesh;

    faceMesh.onResults((results) => {
      // Trực tiếp, không có Proxy
      setFaceDetected(!!results.multiFaceLandmarks?.length);
    });

    // Send trực tiếp
    faceMesh.send({ image: videoRef.current });

    return () => faceMesh.close();
  }, []);

  return <video ref={videoRef} />;
}

React pros: Không cần wrapper phức tạp

Vue pros: Template và reactivity tốt hơn

Khuyến nghị

Khi nào NÊN dùng Vue + MediaPipe

Dùng khi:

  1. Team đã quen thuộc với Vue
  2. Có thời gian thiết lập Vite plugin đúng cách
  3. Cần UI/UX phức tạp (Vue template tốt hơn)
  4. Cần i18n tích hợp sẵn (vue-i18n)

Khi nào KHÔNG NÊN dùng Vue

Không dùng khi:

  1. Cần tích hợp các thư viện WASM khác (các vấn đề tương tự)
  2. Team mới, chưa hiểu Vue reactivity
  3. Cần triển khai nhanh, không có thời gian debug build
  4. Prototype nhanh (React đơn giản hơn)

Thực hành tốt nhất cho Vue + MediaPipe

  1. Luôn dùng wrapper class
typescript
// ✅ Tách biệt WASM trong wrapper
let wrapper: MediaPipeFaceMeshWrapper | null = null;

// ❌ Không dùng trực tiếp trong Vue component
const faceMesh = ref(new FaceMesh(...));  // Lỗi!
  1. Tài liệu hóa rõ ràng
typescript
/**
 * QUAN TRỌNG: wrapper KHÔNG được bọc trong ref/reactive!
 * MediaPipe WASM không tương thích với Vue Proxy.
 */
let wrapper: MediaPipeFaceMeshWrapper | null = null;
  1. Kiểm thử kỹ build production
bash
# Luôn kiểm thử build production
npm run build-production
npm run preview:proxy

# Kiểm tra:
# - FaceMesh có tải được không
# - Camera có hoạt động không
# - Hiệu suất có ổn định không
  1. Giám sát kích thước bundle
bash
# Phân tích bundle
npm run build-production -- --analyze

# Kiểm tra kích thước chunk
ls -lh dist/assets/

Tổng kết

Khía cạnhRatingNote
Development Experience⭐⭐⭐⭐Vue template và reactivity tuyệt vời
Integration Complexity⭐⭐Cần wrapper + Vite plugin
Build Reliability⭐⭐⭐Ổn định sau khi setup đúng
Performance⭐⭐⭐⭐Tương đương React
Maintainability⭐⭐⭐Cần document kỹ về wrapper
Team Onboarding⭐⭐Learning curve cao

Tổng thể: Vue + MediaPipe khả thi nhưng cần:

  • ✅ Setup cẩn thận
  • ✅ Document đầy đủ
  • ✅ Team hiểu Vue reactivity
  • ✅ Test kỹ production builds

Bài học tiếp theo

Vấn đề Build Production với MediaPipe

Internal documentation for iNET Portal