背景
无论国内外,phaser相关的文章少之又少,custom build也只有一篇官方文章,即使跟着文章做也有许多问题,所以在这分享一下自己的实践操作,有不对的地方欢迎大家指正~
Phaser引擎不受Tree Shaking影响,无论全量导入,还是按需导入,Phaser都会全量地导入在你的项目中。因此,Phaser官方提供自定义构建的方式来优化Phaser的体积。
官方repo:
Custom build步骤
废话不多说,进入正题
- clone下官方repo的地址
- 修改package.json中使用的phaser版本为自己项目中的版本,保证版本的一致性
- (如果是新开的项目直接用最新的即可,跳过 2.)
- 首先拿到buildfull的体积作为对比。
此处说明我用的是比较老的版本,所以fullbuild可能会比大家小
shake Phaser模块
以下是本次优化手动"shake"的配置,注释掉的模块没有被打包进来
Events,Game,GameObjects,Geom,Input,Loader,Plugins,Renderer,Scene,Scenes,Tweens
以上所有是目前我需要用到的模块,
其中,GameObjects和Loader是必选项, 但是可以更细粒度地拆分这两个模块,
在node_modules/phaser/src/gameobjects/目录中,每个文件夹即是一个模块,Loader同理
在以下示例中,GameObject只保留了Sprite和Group,Loader只保留了SpriteSheetFile。 因为我只用到了这些,大家可以根据自己的项目来做更细粒度的拆分。此处的效果比较明显
require("polyfills");
var CONST = require("const");
var Extend = require("utils/object/Extend");
/**
* @namespace Phaser
*/
var Phaser = {
// Actions: require('actions'),
// Animations: require('animations'),
// BlendModes: require('renderer/BlendModes'),
// Cache: require('cache'),
Cameras: require("cameras"),
// Core: require('core'),
// Class: require('utils/Class'),
// Create: require('create'),
// Curves: require('curves'),
// Data: require('data'),
// Display: require('display'),
// DOM: require('dom'),
Events: require("events/index"),
// FX: require('fx'),
Game: require("core/Game"),
// GameObjects: require('gameobjects'),
GameObjects: {
DisplayList: require("gameobjects/DisplayList"),
UpdateList: require("gameobjects/UpdateList"),
Sprite: require("gameobjects/sprite/Sprite"),
Group: require("gameobjects/group/Group"),
Factories: {
Sprite: require("gameobjects/sprite/SpriteFactory"),
Group: require("gameobjects/group/GroupFactory"),
},
Creators: {
Sprite: require("gameobjects/sprite/SpriteCreator"),
Group: require("gameobjects/group/GroupCreator"),
},
},
Geom: require("geom"),
Input: require("input"),
// Loader: require("loader"),
Loader: {
FileTypes: {
SpriteSheetFile: require("loader/filetypes/SpriteSheetFile"),
},
LoaderPlugin: require("loader/LoaderPlugin"),
},
// Math: require('math'),
// Physics: require('physics'),
Plugins: require("plugins"),
Renderer: require("renderer"),
// Scale: require('scale'),
// ScaleModes: require('renderer/ScaleModes'),
Scene: require("scene/Scene"),
Scenes: require("scene"),
// Structs: require('structs'),
// Textures: require('textures'),
// Tilemaps: require('tilemaps'),
// Time: require('time'),
Tweens: require("tweens"),
// Utils: require('utils')
};
// Merge in the optional plugins and WebGL only features
if (typeof FEATURE_SOUND) {
Phaser.Sound = require("sound");
}
// Merge in the consts
Phaser = Extend(false, Phaser, CONST);
/**
* The root types namespace.
*
* @namespace Phaser.Types
* @since 3.17.0
*/
// Export it
module.exports = Phaser;
global.Phaser = Phaser;
Webpack配置
在phaser的custom build webpack配置中,已经使用了terser做代码压缩,所以压缩方面无需更多操作
在插件注入方面,仍然有体积优化可以操作。在楼主的项目中,我们仅使用Canvas方式渲染phaser游戏,所以取消全部其他插件。
tips: 如果你做的是FbInstantGame,保留FbInstant插件。
plugins: [
new CleanWebpackPlugin(),
new webpack.DefinePlugin({
"typeof CANVAS_RENDERER": JSON.stringify(true),
"typeof WEBGL_RENDERER": JSON.stringify(false), // 去掉webgl
"typeof WEBGL_DEBUG": JSON.stringify(false),
"typeof EXPERIMENTAL": JSON.stringify(false),
"typeof PLUGIN_3D": JSON.stringify(false),
"typeof PLUGIN_CAMERA3D": JSON.stringify(false), // 去掉3D
"typeof PLUGIN_FBINSTANT": JSON.stringify(true), // 保留FB plugin
"typeof FEATURE_SOUND": JSON.stringify(false),
}),
],
结果
节约了大概400K-(gzip前)的体积(~45%)
当然,越简单的项目可去掉的模块越多,效果越明显。
此处值得一提的是我用的是非常老的版本,3.16.1,在官方教程中使用更新的版本,效果更好。
踩坑
踩坑点:Phaser的自定义构建产物不支持各种形式的导入
import Phaser from './phaser-custom.js'; // ❌
import * as Phaser from './phaser-custom.js' // ❌
// vite 配置别名错误
resolve: {
alias: {
phaser: path.resolve(__dirname, '../lib/phaser-custom.js'),
},
},
原因是 Phaser 没有默认的导出 export default,也不支持使用 import * as 导入所有命名导出
仅支持以下方式
// it works
<script src="./phaser-custom.js"></script>
<script>
console.log(Phaser)
</script>
原理:在 HTML 文件中直接通过 文件时,UMD 文件通常会将库暴露在全局对象上,这样可以直接访问库
代码改造
由于Phaser引入方式的改变,import语句也失去了效果
import phaser,{Module1,Module2...} from "Phaser"
// 全部修改成 const Phaser = window.Phaser? ❌
所以我们做一个包装,并且利用vite/webpack的alias代替phaser。这样无需修改Phaser相关项目代码即可正常跑通
//phaserWarp.js
const Phaser = window.Phaser
export const { Game, CANVAS, Events, Scene, GameObjects, Geom } = Phaser
//vite config
// 修改phaser别名为phaserWrap的路径,这样你的import phaser语句都是从phaserWarp.js中引入
resolve: {
alias: {
phaser: path.resolve(__dirname, '../src/phaserWrap.js'),
},
},
至此,大功告成,项目正常运行