Files
oops-plugin-framework/assets/libs/model-view/ViewModel.ts
dgflash f2fe9d47b6 1. 存储模块全面优化,修复跨平台兼容性问题,完美支持所有Unicode字符
2. 存储模块性能提升,添加LRU缓存、批量操作支持,优化内存使用
3. 多语言模块性能与内存管理优化,组件查询性能提升
4. 时间模块类型安全与性能优化,使用泛型替代any,对象池机制减少内存分配
5. 事件系统修复双重注册、重复注册等严重问题,实现EventData对象池减少GC压力
6. RandomManager修复4个逻辑BUG,包括边界问题和越界问题
7. 音频模块内存与性能优化,避免重复加载,优化数据结构,添加完整清理机制
8. CCView与CCViewVM合并,支持按需启用MVVM
9. Collection模块优化,AsyncQueue添加队列容量限制,Collection查询性能提升
10. ECS系统全面优化,对象池复用减少内存分配,循环性能提升
11. 优化MVVM组件性能
2026-01-09 21:54:05 +08:00

255 lines
7.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { director, log } from 'cc';
import { JsonOb } from './JsonOb';
const VM_EMIT_HEAD = 'VC:';
const DEBUG_SHOW_PATH = false;
/** 通过 . 路径设置值 */
function setValueFromPath(obj: any, path: string, value: any, tag: string | null = '') {
const props = path.split('.');
for (let i = 0; i < props.length; i++) {
const propName = props[i];
if (propName in obj === false) {
console.error('[' + propName + '] not find in ' + tag + '.' + path); break;
}
if (i == props.length - 1) {
obj[propName] = value;
}
else {
obj = obj[propName];
}
}
}
/** 通过 . 路径 获取值 */
function getValueFromPath(obj: any, path: string, def?: any, tag: string | null = ''): any {
const props = path.split('.');
for (let i = 0; i < props.length; i++) {
const propName = props[i];
if ((propName in obj === false)) {
console.error('[' + propName + '] not find in ' + tag + '.' + path); return def;
}
obj = obj[propName];
}
if (obj === null || typeof obj === 'undefined') obj = def;//如果g == null 则返回一个默认值
return obj;
}
/**
* ModelViewer 类
*/
class ViewModel<T> {
constructor(data: T, tag: string) {
this._jsonOb = new JsonOb(data, this._callback.bind(this));
this.$data = data;
this._tag = tag;
}
$data: T;
// 索引值用的标签
private _tag: string | null = null;
// JsonOb 实例引用,用于销毁时清理
private _jsonOb: JsonOb<T> | null = null;
/** 激活状态, 将会通过 director.emit 发送值变动的信号, 适合需要屏蔽的情况 */
active = true;
/** 是否激活根路径回调通知, 不激活的情况下 只能监听末端路径值来判断是否变化 */
emitToRootPath = false;
// 回调函数
private _callback(n: any, o: any, path: string[]): void {
if (this.active == true) {
const name = VM_EMIT_HEAD + this._tag + '.' + path.join('.');
if (DEBUG_SHOW_PATH) log('>>', n, o, path);
// 传递路径数组的副本,防止外部修改
director.emit(name, n, o, [this._tag, ...path]); // 通知末端路径
if (this.emitToRootPath) director.emit(VM_EMIT_HEAD + this._tag, n, o, path.slice()); // 通知主路径
}
}
// 通过路径设置数据的方法
setValue(path: string, value: any) {
setValueFromPath(this.$data, path, value, this._tag);
}
// 获取路径的值
getValue(path: string, def?: any): any {
return getValueFromPath(this.$data, path, def, this._tag);
}
// 销毁 ViewModel释放内存
destroy() {
if (this._jsonOb) {
this._jsonOb.destroy();
this._jsonOb = null;
}
// @ts-ignore
this.$data = null;
this._tag = null;
this.active = false;
}
}
/**
* VM 对象管理器(工厂)
*/
class VMManager {
private _mvs: Map<string, ViewModel<any>> = new Map<string, ViewModel<any>>();
/**
* 绑定一个数据并且可以由VM所管理绑定的数据只能是值类型
* @param data 需要绑定的数据
* @param tag 对应该数据的标签(用于识别为哪个VM不允许重复)
* @param activeRootObject 激活主路径通知,可能会有性能影响,一般不使用
*/
add<T>(data: T, tag = 'global', activeRootObject = false) {
const vm = new ViewModel<T>(data, tag);
const has = this._mvs.get(tag);
if (tag.includes('.')) {
console.error('cant write . in tag:', tag);
return;
}
if (has) {
console.error('already set VM tag:' + tag);
return;
}
vm.emitToRootPath = activeRootObject;
this._mvs.set(tag, vm);
}
/**
* 移除并且销毁 VM 对象
* @param tag
*/
remove(tag: string) {
const vm = this._mvs.get(tag);
if (vm) {
vm.destroy();
this._mvs.delete(tag);
}
}
/**
* 获取绑定的数据
* @param tag 数据tag
*/
get<T>(tag: string): ViewModel<T> | undefined {
const res = this._mvs.get(tag);
return res;
}
/**
* 通过全局路径,而不是 VM 对象来 设置值
* @param path - 全局取值路径
* @param value - 需要增加的值
*/
addValue(path: string, value: any) {
path = path.trim();//防止空格,自动剔除
const rs = path.split('.');
if (rs.length < 2) {
console.error('Cant find path:' + path);
};
const vm = this.get(rs[0]);
if (!vm) {
console.error('Cant Set VM:' + rs[0]); return;
};
const resPath = rs.slice(1).join('.');
vm.setValue(resPath, vm.getValue(resPath) + value);
}
/**
* 通过全局路径,而不是 VM 对象来 获取值
* @param path - 全局取值路径
* @param def - 如果取不到值的返回的默认值
*/
getValue(path: string, def?: any): any {
path = path.trim(); // 防止空格,自动剔除
if (path === '') return '';
const rs = path.split('.');
if (rs.length < 2) {
console.error('Get Value Cant find path:' + path); return;
};
const vm = this.get(rs[0]);
if (!vm) {
console.error('Cant Get VM:' + rs[0]); return;
};
return vm.getValue(rs.slice(1).join('.'), def);
}
/**
* 通过全局路径,而不是 VM 对象来 设置值
* @param path - 全局取值路径
* @param value - 需要设置的值
*/
setValue(path: string, value: any) {
path = path.trim(); // 防止空格,自动剔除
const rs = path.split('.');
if (rs.length < 2) {
console.error('Set Value Cant find path:' + path); return;
};
const vm = this.get(rs[0]);
if (!vm) {
console.error('Cant Set VM:' + rs[0]); return;
};
vm.setValue(rs.slice(1).join('.'), value);
}
setObjValue = setValueFromPath;
getObjValue = getValueFromPath;
/** 等同于 director.on */
bindPath(path: string, callback: Function, target?: any, useCapture?: boolean): void {
path = path.trim(); // 防止空格,自动剔除
if (path == '') {
console.error(target.node.name, '节点绑定的路径为空');
return;
}
if (path.split('.')[0] === '*') {
console.error(path, '路径不合法,可能错误覆盖了 VMParent 的onLoad 方法, 或者父节点并未挂载 VMParent 相关的组件脚本');
return;
}
// @ts-ignore
director.on(VM_EMIT_HEAD + path, callback, target, useCapture);
}
/** 等同于 director.off */
unbindPath(path: string, callback: Function, target?: any): void {
path = path.trim();//防止空格,自动剔除
if (path.split('.')[0] === '*') {
console.error(path, '路径不合法,可能错误覆盖了 VMParent 的onLoad 方法, 或者父节点并未挂载 VMParent 相关的组件脚本');
return;
}
// @ts-ignore
director.off(VM_EMIT_HEAD + path, callback, target);
}
/** 冻结所有标签的 VM视图将不会受到任何信息 */
inactive(): void {
this._mvs.forEach((mv) => {
mv.active = false;
});
}
/** 激活所有标签的 VM*/
active(): void {
this._mvs.forEach((mv) => {
mv.active = true; // 修复:应该设置为 true
});
}
}
/**
* VM管理对象,使用文档:
* https://gitee.com/dgflash/oops-framework/wikis/pages?sort_id=12037849&doc_id=2873565
*/
export const VM = new VMManager();