在打包程式碼中呼叫 bundle()
排解 Remotion 中「Calling bundle() in bundled code」錯誤,了解為何不能在瀏覽器端或已打包的程式碼中呼叫 bundle() 函式。
在打包程式碼中呼叫 bundle()
錯誤訊息
Error: Calling bundle() in bundled code is not supported.
或類似的變體:
bundle() cannot be called from within a Webpack bundle.
@remotion/bundler cannot be used in a browser environment.
錯誤原因
bundle() 是 @remotion/bundler 提供的 Node.js API,它的功能是呼叫 Webpack 將你的 Remotion 專案打包成一個可供渲染的 bundle。這個操作需要:
- 存取檔案系統(讀取原始碼、設定檔等)
- 執行 Webpack 進程
- 使用 Node.js 原生模組
以上這些都只能在 Node.js 環境中執行,無法在瀏覽器環境或已被 Webpack 打包過的程式碼中使用。
當你嘗試在 React 元件、Next.js 的 Client Component 或其他前端程式碼中匯入並呼叫 bundle() 時,就會觸發此錯誤。
錯誤示範
以下是幾種常見的錯誤用法:
// ❌ 錯誤:在 React 元件中直接呼叫 bundle()
import { bundle } from '@remotion/bundler';
import { useEffect } from 'react';
export const RenderButton: React.FC = () => {
const handleRender = async () => {
// 這裡不能呼叫 bundle()!
const bundled = await bundle({
entryPoint: './src/remotion/index.ts',
});
// ...
};
return <button onClick={handleRender}>開始渲染</button>;
};// ❌ 錯誤:在 Next.js Client Component 中呼叫
'use client';
import { bundle } from '@remotion/bundler';
export default function Page() {
const render = async () => {
const bundled = await bundle({ ... }); // 錯誤!
};
// ...
}正確做法
方案一:在 Server Action 中執行(Next.js App Router)
若使用 Next.js App Router,可以在 Server Action 中執行 bundle() 和渲染邏輯:
// app/actions.ts
'use server';
import { bundle } from '@remotion/bundler';
import { renderMedia, selectComposition } from '@remotion/renderer';
import path from 'path';
export async function startRender(props: { title: string }) {
const bundled = await bundle({
entryPoint: path.resolve('./src/remotion/index.ts'),
});
const composition = await selectComposition({
serveUrl: bundled,
id: 'MyComposition',
inputProps: props,
});
await renderMedia({
composition,
serveUrl: bundled,
codec: 'h264',
outputLocation: `out/${Date.now()}.mp4`,
});
return { success: true };
}// app/page.tsx
'use client';
import { startRender } from './actions';
export default function Page() {
const handleRender = async () => {
await startRender({ title: '我的影片' });
};
return <button onClick={handleRender}>開始渲染</button>;
}方案二:建立 API 路由
在後端 API 路由中執行渲染邏輯,前端僅發送 HTTP 請求:
// pages/api/render.ts(Next.js Pages Router)
import type { NextApiRequest, NextApiResponse } from 'next';
import { bundle } from '@remotion/bundler';
import { renderMedia, selectComposition } from '@remotion/renderer';
import path from 'path';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
const { title } = req.body;
const bundled = await bundle({
entryPoint: path.resolve('./src/remotion/index.ts'),
});
const composition = await selectComposition({
serveUrl: bundled,
id: 'MyComposition',
inputProps: { title },
});
await renderMedia({
composition,
serveUrl: bundled,
codec: 'h264',
outputLocation: `out/${Date.now()}.mp4`,
});
res.status(200).json({ success: true });
}前端發起渲染請求:
// 前端元件
const handleRender = async () => {
const response = await fetch('/api/render', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: '我的影片' }),
});
const result = await response.json();
console.log(result);
};方案三:獨立的 Node.js 腳本
若渲染不需要透過 Web API 觸發,可以直接撰寫獨立的 Node.js 腳本:
// scripts/render.ts
import { bundle } from '@remotion/bundler';
import { renderMedia, selectComposition } from '@remotion/renderer';
import path from 'path';
async function main() {
const bundled = await bundle({
entryPoint: path.resolve('./src/remotion/index.ts'),
});
const composition = await selectComposition({
serveUrl: bundled,
id: 'MyComposition',
inputProps: { title: '我的影片' },
});
await renderMedia({
composition,
serveUrl: bundled,
codec: 'h264',
outputLocation: 'out/video.mp4',
});
console.log('渲染完成!');
}
main();以 tsx 或編譯後執行:
npx tsx scripts/render.ts方案四:使用 AWS Lambda 或 Cloud Run
若需要可擴展的雲端渲染,使用 @remotion/lambda 或 Cloud Run。這些服務在後端執行 bundle() 和渲染,前端透過 SDK 觸發:
// 這段程式碼在後端(Node.js)執行
import { renderMediaOnLambda } from '@remotion/lambda/client';
const result = await renderMediaOnLambda({
region: 'ap-northeast-1',
functionName: 'remotion-render',
serveUrl: 'https://your-site.s3.amazonaws.com',
composition: 'MyComposition',
inputProps: { title: '我的影片' },
codec: 'h264',
});效能提示:快取 Bundle
bundle() 每次執行需要花費數秒鐘。在同一個 Node.js 程序中,若需要多次渲染,建議快取 bundle 的結果:
let cachedBundleUrl: string | null = null;
async function getBundleUrl() {
if (!cachedBundleUrl) {
cachedBundleUrl = await bundle({
entryPoint: path.resolve('./src/remotion/index.ts'),
});
}
return cachedBundleUrl;
}
// 多次渲染重複使用同一個 bundle
const bundleUrl = await getBundleUrl();