强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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

特性ViteWebpack
启动速度极快(原生 ESM)较慢(全量打包)
HMR 热更新毫秒级秒级
配置复杂度简单较复杂
生态成熟度快速增长非常成熟
生产构建RollupWebpack 自身
推荐程度✅ 新项目首选旧项目维护

三、资源优化

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 部署平台对比

平台免费额度自定义域名HTTPSCDN
Cloudflare Pages无限✅ 全球
Vercel100 GB/月✅ 全球
Netlify100 GB/月
GitHub Pages1 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)
  └─ 版本更新检测

扩展阅读