Pixi.js 游戏开发教程 / Pixi.js 构建与发布(Vite / Webpack)
构建与发布(Vite / Webpack)
从项目搭建到生产部署的完整工程化指南。
一、Vite 项目搭建
1.1 初始化项目
npm create vite@latest my-pixi-game -- --template vanilla-ts
cd my-pixi-game
npm install pixi.js
npm run dev
1.2 推荐目录结构
my-pixi-game/
├── public/
│ └── image/ # 静态资源(不经过构建)
│ ├── spritesheet.png
│ └── bg.jpg
├── src/
│ ├── core/
│ │ ├── Game.ts # 主游戏类
│ │ ├── Scene.ts # 场景基类
│ │ └── Input.ts # 输入管理
│ ├── entities/
│ │ ├── Player.ts
│ │ └── Enemy.ts
│ ├── ui/
│ │ └── HUD.ts
│ ├── assets/
│ │ └── manifest.json # 资源清单
│ ├── main.ts # 入口
│ └── style.css
├── tests/
├── .env # 环境变量
├── .env.production
├── vite.config.ts
└── tsconfig.json
1.3 Vite 配置
// vite.config.ts
import { defineConfig } from 'vite';
export default defineConfig({
base: './', // 相对路径,方便部署到子目录
build: {
outDir: 'dist',
assetsDir: 'assets',
sourcemap: false, // 生产环境不生成 sourcemap
minify: 'terser', // 使用 terser 压缩
terserOptions: {
compress: {
drop_console: true, // 移除 console.log
},
},
rollupOptions: {
output: {
// 代码分割
manualChunks: {
pixi: ['pixi.js'],
},
},
},
},
server: {
port: 3000,
open: true,
},
preview: {
port: 4173,
},
});
1.4 入口文件
// src/main.ts
import { Application, Assets, Sprite } from 'pixi.js';
async function init() {
const app = new Application();
await app.init({
width: 800,
height: 600,
backgroundColor: 0x1a1a2e,
antialias: true,
resolution: window.devicePixelRatio || 1,
autoDensity: true,
});
document.getElementById('app')!.appendChild(app.canvas);
// 预加载资源
await Assets.load('/image/spritesheet.png');
const sprite = Sprite.from('/image/spritesheet.png');
app.stage.addChild(sprite);
app.ticker.add((ticker) => {
sprite.rotation += 0.01 * ticker.deltaTime;
});
}
init().catch(console.error);
二、Webpack 配置
2.1 安装依赖
npm install pixi.js
npm install -D webpack webpack-cli webpack-dev-server
npm install -D ts-loader css-loader html-webpack-plugin
npm install -D terser-webpack-plugin copy-webpack-plugin
2.2 Webpack 配置
// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
module.exports = (env, argv) => ({
entry: './src/main.ts',
mode: argv.mode || 'development',
devtool: argv.mode === 'production' ? false : 'source-map',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'assets/js/[name].[contenthash:8].js',
clean: true,
},
resolve: {
extensions: ['.ts', '.js'],
},
module: {
rules: [
{
test: /\.ts$/,
use: 'ts-loader',
exclude: /node_modules/,
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html',
minify: argv.mode === 'production' ? {
collapseWhitespace: true,
removeComments: true,
} : false,
}),
new CopyWebpackPlugin({
patterns: [
{ from: 'public', to: '' },
],
}),
],
optimization: {
minimize: argv.mode === 'production',
minimizer: [new TerserPlugin({
terserOptions: {
compress: { drop_console: true },
},
})],
splitChunks: {
chunks: 'all',
cacheGroups: {
pixi: {
test: /[\\/]node_modules[\\/]pixi\.js[\\/]/,
name: 'pixi',
priority: 10,
},
},
},
},
devServer: {
port: 3000,
open: true,
hot: true,
},
});
2.3 Vite vs Webpack
| 特性 | Vite | Webpack |
|---|---|---|
| 启动速度 | 极快(原生 ESM) | 较慢(全量打包) |
| HMR 热更新 | 毫秒级 | 秒级 |
| 配置复杂度 | 简单 | 较复杂 |
| 生态成熟度 | 快速增长 | 非常成熟 |
| 生产构建 | Rollup | Webpack 自身 |
| 推荐程度 | ✅ 新项目首选 | 旧项目维护 |
三、资源优化
3.1 图片压缩
# 安装压缩工具
npm install -D vite-plugin-imagemin
# 或使用命令行工具
npm install -D sharp-cli
sharp -i public/image/*.png -o dist/image/ --quality 80
// vite.config.ts - imagemin 插件
import imagemin from 'vite-plugin-imagemin';
export default defineConfig({
plugins: [
imagemin({
gifsicle: { optimizationLevel: 3 },
optipng: { optimizationLevel: 5 },
mozjpeg: { quality: 80 },
pngquant: { quality: [0.6, 0.8] },
webp: { quality: 80 },
}),
],
});
3.2 代码分割策略
// 动态导入实现懒加载
async function loadLevel(levelId: string) {
const module = await import(`./levels/${levelId}.ts`);
return module.default;
}
// 场景级代码分割
async function switchScene(sceneName: string) {
switch (sceneName) {
case 'menu':
const { MenuScene } = await import('./scenes/MenuScene');
return new MenuScene();
case 'game':
const { GameScene } = await import('./scenes/GameScene');
return new GameScene();
}
}
四、环境变量
4.1 配置文件
# .env(通用)
VITE_APP_TITLE=My Pixi Game
VITE_API_URL=http://localhost:3001
# .env.development(开发环境)
VITE_DEBUG=true
VITE_WS_URL=ws://localhost:8080
# .env.production(生产环境)
VITE_DEBUG=false
VITE_WS_URL=wss://api.example.com
VITE_SENTRY_DSN=https://[email protected]/xxx
4.2 TypeScript 类型声明
// src/env.d.ts
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_APP_TITLE: string;
readonly VITE_API_URL: string;
readonly VITE_DEBUG: string;
readonly VITE_WS_URL: string;
readonly VITE_SENTRY_DSN?: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
4.3 使用环境变量
// src/config.ts
export const config = {
title: import.meta.env.VITE_APP_TITLE,
apiUrl: import.meta.env.VITE_API_URL,
debug: import.meta.env.VITE_DEBUG === 'true',
wsUrl: import.meta.env.VITE_WS_URL,
sentryDsn: import.meta.env.VITE_SENTRY_DSN,
};
// 条件编译
if (config.debug) {
import('./debug').then(m => m.initDebug());
}
五、Tree Shaking
5.1 确保 Tree Shaking 生效
// ✅ 具名导入(Tree Shaking 友好)
import { Application, Sprite, Texture } from 'pixi.js';
// ❌ 全量导入(无法 Tree Shaking)
import * as PIXI from 'pixi.js';
PIXI.Sprite.from(...);
// ✅ 避免副作用
// 确保 package.json 中 "sideEffects": false
5.2 验证效果
# 构建后检查
npm run build
ls -la dist/assets/js/
# 使用 rollup-plugin-visualizer 分析
npm install -D rollup-plugin-visualizer
// vite.config.ts
import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig({
plugins: [
visualizer({
open: true,
filename: 'stats.html',
}),
],
});
六、构建产物分析
6.1 使用 rollup-plugin-visualizer
生成的 stats.html 可视化展示各模块占比:
┌────────────────────────────┐
│ pixi.js 45% │
│ ├─ core 15% │
│ ├─ rendering 12% │
│ └─ math 8% │
│ 游戏代码 20% │
│ assets 35% │
└────────────────────────────┘
6.2 Bundle 大小优化
| 目标 | 方法 |
|---|---|
| 减小 pixi.js 体积 | 只导入需要的模块 |
| 减小图片体积 | 使用 WebP/AVIF 格式 |
| 减小代码体积 | 移除 console.log、压缩 |
| 加快加载 | 代码分割、懒加载 |
七、CDN 部署
7.1 Cloudflare Pages
# 1. 构建
npm run build
# 2. 使用 Wrangler CLI 部署
npm install -g wrangler
wrangler pages deploy dist --project-name=my-pixi-game
或通过 Git 集成自动部署:
1. 登录 Cloudflare Dashboard → Pages
2. 连接 GitHub 仓库
3. 设置构建命令: npm run build
4. 设置输出目录: dist
5. 推送代码自动部署
7.2 Vercel
# 安装 Vercel CLI
npm install -g vercel
# 部署
vercel --prod
// vercel.json
{
"buildCommand": "npm run build",
"outputDirectory": "dist",
"framework": null,
"rewrites": [
{ "source": "/(.*)", "destination": "/index.html" }
]
}
7.3 部署平台对比
| 平台 | 免费额度 | 自定义域名 | HTTPS | CDN |
|---|---|---|---|---|
| Cloudflare Pages | 无限 | ✅ | ✅ | ✅ 全球 |
| Vercel | 100 GB/月 | ✅ | ✅ | ✅ 全球 |
| Netlify | 100 GB/月 | ✅ | ✅ | ✅ |
| GitHub Pages | 1 GB | ✅ | ✅ | 有限 |
八、GitHub Pages 发布
8.1 GitHub Actions 自动部署
# .github/workflows/deploy.yml
name: Deploy to GitHub Pages
on:
push:
branches: [main]
permissions:
contents: read
pages: write
id-token: write
jobs:
build-and-deploy:
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm run build
- uses: actions/upload-pages-artifact@v3
with:
path: dist
- id: deployment
uses: actions/deploy-pages@v4
8.2 配置 Vite base 路径
// vite.config.ts
export default defineConfig({
base: process.env.BASE_URL || '/my-pixi-game/',
// GitHub Pages 的 URL 格式: username.github.io/repo-name/
});
九、PWA 配置
9.1 使用 vite-plugin-pwa
npm install -D vite-plugin-pwa
// vite.config.ts
import { VitePWA } from 'vite-plugin-pwa';
export default defineConfig({
plugins: [
VitePWA({
registerType: 'autoUpdate',
includeAssets: ['image/*.png', 'image/*.jpg'],
manifest: {
name: 'My Pixi Game',
short_name: 'PixiGame',
description: '一个 Pixi.js 游戏',
theme_color: '#1a1a2e',
background_color: '#1a1a2e',
display: 'fullscreen',
orientation: 'landscape',
icons: [
{
src: 'icon-192.png',
sizes: '192x192',
type: 'image/png',
},
{
src: 'icon-512.png',
sizes: '512x512',
type: 'image/png',
},
],
},
workbox: {
globPatterns: ['**/*.{js,css,html,png,jpg,svg,woff2}'],
runtimeCaching: [
{
urlPattern: /^https:\/\/fonts\.googleapis\.com\/.*/i,
handler: 'CacheFirst',
options: {
cacheName: 'google-fonts-cache',
expiration: { maxEntries: 10, maxAgeSeconds: 60 * 60 * 24 * 365 },
},
},
],
},
}),
],
});
9.2 离线状态检测
// 检测网络状态
function initNetworkDetection() {
const indicator = document.getElementById('offline-indicator')!;
function updateStatus() {
if (navigator.onLine) {
indicator.style.display = 'none';
} else {
indicator.style.display = 'block';
indicator.textContent = '📡 离线模式';
}
}
window.addEventListener('online', updateStatus);
window.addEventListener('offline', updateStatus);
updateStatus();
}
十、版本管理与更新策略
10.1 版本号规范(SemVer)
主版本.次版本.修订号
MAJOR.MINOR.PATCH
1.0.0 → 初始发布
1.1.0 → 新增关卡
1.1.1 → Bug 修复
2.0.0 → 大版本更新(可能不兼容旧存档)
10.2 热更新提示
// 检测新版本并提示用户
class UpdateManager {
private currentVersion: string;
constructor(version: string) {
this.currentVersion = version;
}
async checkForUpdate() {
try {
const response = await fetch('/version.json', {
cache: 'no-cache',
});
const { version } = await response.json();
if (version !== this.currentVersion) {
this.showUpdatePrompt();
}
} catch {
// 忽略网络错误
}
}
private showUpdatePrompt() {
const prompt = document.createElement('div');
prompt.innerHTML = `
<div style="position:fixed; bottom:20px; right:20px;
background:#333; color:#fff; padding:16px;
border-radius:8px; z-index:9999;">
<p>有新版本可用!</p>
<button onclick="location.reload()">刷新更新</button>
<button onclick="this.parentElement.remove()">稍后</button>
</div>
`;
document.body.appendChild(prompt);
}
}
// 页面加载时检查
const updateManager = new UpdateManager('1.0.0');
updateManager.checkForUpdate();
10.3 资源版本化
// 确保浏览器不使用旧缓存资源
// Vite 默认会给文件名加 hash,自动解决此问题
// dist/assets/js/index.a1b2c3d4.js
// 对于 public/ 下的资源,手动添加版本号
const ASSET_VERSION = '1.0.0';
const imageUrl = `/image/spritesheet.png?v=${ASSET_VERSION}`;
完整构建流程清单
□ 开发环境
├─ Vite dev server (HMR)
├─ TypeScript 严格模式
└─ ESLint + Prettier
□ 测试
├─ Vitest 单元测试
├─ Playwright E2E 测试
└─ 截图对比测试
□ 构建
├─ TypeScript 编译检查
├─ Tree Shaking
├─ 代码分割 (pixi.js 单独 chunk)
├─ 图片压缩
└─ 移除 console.log
□ 部署
├─ 生成 version.json
├─ 部署到 CDN
├─ 配置缓存策略
└─ PWA Service Worker
□ 监控
├─ Sentry 错误监控
├─ 性能指标 (LCP, FID, CLS)
└─ 版本更新检测