Skip to content

Vấn đề Build Production

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

  • Hiểu root cause của production bug
  • Nắm debug process để identify issue
  • Biết tại sao chỉ xảy ra với MediaPipe
  • Học cách so sánh dev vs production

Triệu chứng

Error Message

Uncaught TypeError: FaceMesh is not a constructor
    at MediaPipeFaceMeshWrapper.ts:45
    at setup (EkycSettingEnhanced.vue:123)

Khi nào xảy ra?

  • Chế độ Development: Hoạt động bình thường
  • Build Production: Lỗi ngay khi component mount
  • Preview build production: Lỗi tương tự

Stack Trace

javascript
// Production build error
Uncaught TypeError: FaceMesh is not a constructor
    at new MediaPipeFaceMeshWrapper (MediaPipeFaceMeshWrapper.ts:45:15)
    at setup (EkycSettingEnhanced.vue:123:25)
    at callWithErrorHandling (runtime-core.esm-bundler.js:157:24)

Root Cause Analysis

Vấn đề: ES Module vs CommonJS

MediaPipe FaceMesh được build dưới dạng ES Module, nhưng Vite production build có thể bundle nó thành CommonJS hoặc IIFE, gây ra xung đột.

Code gây lỗi

typescript
// MediaPipeFaceMeshWrapper.ts
import { FaceMesh } from "@mediapipe/face_mesh";

export class MediaPipeFaceMeshWrapper {
  private faceMesh: FaceMesh;

  constructor() {
    // ❌ Production: FaceMesh is undefined hoặc không phải constructor
    this.faceMesh = new FaceMesh({
      locateFile: (file) => {
        return `https://cdn.jsdelivr.net/npm/@mediapipe/face_mesh/${file}`;
      },
    });
  }
}

Tại sao chỉ xảy ra với MediaPipe?

  1. Phụ thuộc WASM: MediaPipe sử dụng WebAssembly, cần xử lý đặc biệt
  2. Import động: MediaPipe tải file từ CDN tại thời gian chạy
  3. Cấu trúc ES Module: MediaPipe export default, nhưng Vite có thể chuyển đổi sai
  4. Tối ưu hóa build: Vite production build tối ưu quá mức, làm hỏng cấu trúc MediaPipe

So sánh Dev vs Production

Development Mode

typescript
// Vite dev server
import { FaceMesh } from "@mediapipe/face_mesh";

// ✅ FaceMesh is a class constructor
console.log(typeof FaceMesh); // "function"
console.log(FaceMesh.prototype); // Has methods

Production Build

typescript
// Vite production build
// After bundling/optimization
const FaceMesh = /* some transformed code */;

// ❌ FaceMesh might be:
// - undefined
// - An object instead of constructor
// - A function but not a constructor
console.log(typeof FaceMesh); // "undefined" or "object"

Debug Process

Step 1: Check Import

typescript
import { FaceMesh } from "@mediapipe/face_mesh";

console.log("FaceMesh:", FaceMesh);
console.log("Type:", typeof FaceMesh);
console.log("Is constructor?", typeof FaceMesh === "function");

Step 2: Check Build Output

bash
# Build production
npm run build

# Check dist folder
ls -la dist/assets/

# Look for MediaPipe bundle
# Check if it's properly included

Step 3: Check Browser Console

javascript
// In browser console (production build)
import("@mediapipe/face_mesh").then((module) => {
  console.log("Module:", module);
  console.log("FaceMesh:", module.FaceMesh);
  console.log("Default:", module.default);
});

Bước 4: Kiểm tra tab Network

  • ✅ Kiểm tra xem file WASM có được tải không
  • ✅ Kiểm tra xem MediaPipe JS có được tải không
  • ❌ Kiểm tra lỗi 404 trên tài sản MediaPipe

Common Causes

1. Vite Build Optimization

Vite production build có thể:

  • Tree-shake các export của MediaPipe
  • Chuyển đổi ES modules không đúng
  • Bundle MediaPipe thành định dạng sai

2. Rollup Plugin Issues

Vite dùng Rollup nội bộ, và Rollup có thể:

  • Làm rối tên class
  • Chuyển đổi default exports không đúng
  • Làm hỏng phụ thuộc WASM

3. Manual Chunks Configuration

Nếu có build.rollupOptions.output.manualChunks:

  • MediaPipe có thể bị bundle vào chunk sai
  • Thứ tự tải chunk có thể sai

4. External Dependencies

Nếu MediaPipe được đánh dấu là external:

  • Runtime sẽ không tìm thấy
  • Tải từ CDN có thể thất bại

Verification Steps

Test Development

bash
npm run dev
# ✅ Should work fine

Test Production Build

bash
npm run build
npm run preview
# ❌ Should fail with "FaceMesh is not a constructor"

Check Bundle Size

bash
# MediaPipe should be ~7MB
# If much smaller → might be tree-shaken incorrectly

Impact

Trải nghiệm người dùng

  • ❌ eKYC không thể khởi động
  • ❌ Phát hiện liveness khuôn mặt không hoạt động
  • ❌ Người dùng không thể hoàn tất xác thực

Tác động kinh doanh

  • ❌ Chặn đăng ký/xác thực người dùng
  • ❌ Lỗi ưu tiên cao
  • ❌ Cần sửa ngay lập tức

Các bước tiếp theo

Sau khi xác định nguyên nhân gốc rễ, cần triển khai giải pháp:

Giải pháp Workaround: Vite Plugin

Tổng kết

Nguyên nhân gốc rễ: Vite production build chuyển đổi MediaPipe ES Module không đúng, làm FaceMesh không còn là constructor.

Điểm chính:

  • ✅ Chế độ dev hoạt động (không tối ưu hóa)
  • ❌ Production thất bại (có tối ưu hóa)
  • ⚠️ Vấn đề đặc thù MediaPipe (WASM + ES Module)
  • 🔍 Debug bằng cách kiểm tra import và đầu ra build

Giải pháp: Cần plugin Vite tùy chỉnh để xử lý MediaPipe đúng cách.

Bài học tiếp theo

Giải pháp Workaround: Vite Plugin

Internal documentation for iNET Portal