feat(ui): 真机端到端响应式链路验证
SceneStart.ts(assets/scripts/):真机验证脚本 - 构造 mock playerState(signal 形式) - bindTo(PlayerInfoView 实例) - 3 秒后改变 playerState.value 触发响应式更新 - console.log 双次:bindTo 触发 + 状态变化 PlayerInfoView.prefab + Main.scene: - PlayerInfoView_Runtime 容器 + 实例化 PlayerInfoView - SceneStart_Node 挂脚本 + playerInfoView 引用 - save 到 Main.scene(已落盘) 真机 Game View 截图证据: - Nickname Label 文本受控(从 (nickname) 占位等待 bindTo) - Bean Label 文本受控 - AvatarSprite 引用 ✓ - 控制台输出 [SceneStart] bindTo 已触发 + [SceneStart] 改变 playerState 端到端响应式链路完整跑通:playerState.value 改变 → View 自动刷新 Label.string Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -26,7 +26,7 @@
|
|||||||
"_active": true,
|
"_active": true,
|
||||||
"_components": [],
|
"_components": [],
|
||||||
"_prefab": {
|
"_prefab": {
|
||||||
"__id__": 25
|
"__id__": 38
|
||||||
},
|
},
|
||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
},
|
},
|
||||||
"autoReleaseAssets": false,
|
"autoReleaseAssets": false,
|
||||||
"_globals": {
|
"_globals": {
|
||||||
"__id__": 26
|
"__id__": 39
|
||||||
},
|
},
|
||||||
"_id": "39e47c22-7aa0-4b0b-98a5-c5006cf3766b"
|
"_id": "39e47c22-7aa0-4b0b-98a5-c5006cf3766b"
|
||||||
},
|
},
|
||||||
@@ -163,15 +163,21 @@
|
|||||||
"_children": [
|
"_children": [
|
||||||
{
|
{
|
||||||
"__id__": 5
|
"__id__": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__id__": 23
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__id__": 33
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_active": true,
|
"_active": true,
|
||||||
"_components": [
|
"_components": [
|
||||||
{
|
{
|
||||||
"__id__": 23
|
"__id__": 36
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"__id__": 24
|
"__id__": 37
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_prefab": null,
|
"_prefab": null,
|
||||||
@@ -475,6 +481,273 @@
|
|||||||
],
|
],
|
||||||
"value": null
|
"value": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.Node",
|
||||||
|
"_name": "PlayerInfoView_Runtime",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"_parent": {
|
||||||
|
"__id__": 4
|
||||||
|
},
|
||||||
|
"_children": [
|
||||||
|
{
|
||||||
|
"__id__": 24
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"_active": true,
|
||||||
|
"_components": [
|
||||||
|
{
|
||||||
|
"__id__": 32
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"_prefab": null,
|
||||||
|
"_lpos": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 0,
|
||||||
|
"y": -100,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"_lrot": {
|
||||||
|
"__type__": "cc.Quat",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"w": 1
|
||||||
|
},
|
||||||
|
"_lscale": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"z": 1
|
||||||
|
},
|
||||||
|
"_mobility": 0,
|
||||||
|
"_layer": 33554432,
|
||||||
|
"_euler": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"_id": "fcIV9QMDtLGpt3/3eCUF9w"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.Node",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"_parent": {
|
||||||
|
"__id__": 23
|
||||||
|
},
|
||||||
|
"_prefab": {
|
||||||
|
"__id__": 25
|
||||||
|
},
|
||||||
|
"__editorExtras__": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.PrefabInfo",
|
||||||
|
"root": {
|
||||||
|
"__id__": 24
|
||||||
|
},
|
||||||
|
"asset": {
|
||||||
|
"__uuid__": "9dd8661c-f764-4e33-b5be-47e02a7d6f41",
|
||||||
|
"__expectedType__": "cc.Prefab"
|
||||||
|
},
|
||||||
|
"fileId": "dflMFKQ9RAybmIkPF8a3Q5",
|
||||||
|
"instance": {
|
||||||
|
"__id__": 26
|
||||||
|
},
|
||||||
|
"targetOverrides": null,
|
||||||
|
"nestedPrefabInstanceRoots": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.PrefabInstance",
|
||||||
|
"fileId": "068gqTjUZHz4hmwVlaPi9D",
|
||||||
|
"prefabRootNode": null,
|
||||||
|
"mountedChildren": [],
|
||||||
|
"mountedComponents": [],
|
||||||
|
"propertyOverrides": [
|
||||||
|
{
|
||||||
|
"__id__": 27
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__id__": 29
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__id__": 30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__id__": 31
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"removedComponents": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "CCPropertyOverrideInfo",
|
||||||
|
"targetInfo": {
|
||||||
|
"__id__": 28
|
||||||
|
},
|
||||||
|
"propertyPath": [
|
||||||
|
"_name"
|
||||||
|
],
|
||||||
|
"value": "PlayerInfoView"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.TargetInfo",
|
||||||
|
"localID": [
|
||||||
|
"dflMFKQ9RAybmIkPF8a3Q5"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "CCPropertyOverrideInfo",
|
||||||
|
"targetInfo": {
|
||||||
|
"__id__": 28
|
||||||
|
},
|
||||||
|
"propertyPath": [
|
||||||
|
"_lpos"
|
||||||
|
],
|
||||||
|
"value": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 0,
|
||||||
|
"y": 100,
|
||||||
|
"z": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "CCPropertyOverrideInfo",
|
||||||
|
"targetInfo": {
|
||||||
|
"__id__": 28
|
||||||
|
},
|
||||||
|
"propertyPath": [
|
||||||
|
"_lrot"
|
||||||
|
],
|
||||||
|
"value": {
|
||||||
|
"__type__": "cc.Quat",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"w": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "CCPropertyOverrideInfo",
|
||||||
|
"targetInfo": {
|
||||||
|
"__id__": 28
|
||||||
|
},
|
||||||
|
"propertyPath": [
|
||||||
|
"_euler"
|
||||||
|
],
|
||||||
|
"value": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.UITransform",
|
||||||
|
"_name": "",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"node": {
|
||||||
|
"__id__": 23
|
||||||
|
},
|
||||||
|
"_enabled": true,
|
||||||
|
"__prefab": null,
|
||||||
|
"_contentSize": {
|
||||||
|
"__type__": "cc.Size",
|
||||||
|
"width": 100,
|
||||||
|
"height": 100
|
||||||
|
},
|
||||||
|
"_anchorPoint": {
|
||||||
|
"__type__": "cc.Vec2",
|
||||||
|
"x": 0.5,
|
||||||
|
"y": 0.5
|
||||||
|
},
|
||||||
|
"_id": "feGGV1vlZBXYEb9+YMs0YO"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.Node",
|
||||||
|
"_name": "SceneStart_Node",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"_parent": {
|
||||||
|
"__id__": 4
|
||||||
|
},
|
||||||
|
"_children": [],
|
||||||
|
"_active": true,
|
||||||
|
"_components": [
|
||||||
|
{
|
||||||
|
"__id__": 34
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__id__": 35
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"_prefab": null,
|
||||||
|
"_lpos": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 600,
|
||||||
|
"y": -300,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"_lrot": {
|
||||||
|
"__type__": "cc.Quat",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"w": 1
|
||||||
|
},
|
||||||
|
"_lscale": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"z": 1
|
||||||
|
},
|
||||||
|
"_mobility": 0,
|
||||||
|
"_layer": 33554432,
|
||||||
|
"_euler": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"_id": "6eeU3KOvxGQbS6UwtxOvAd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.UITransform",
|
||||||
|
"_name": "",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"node": {
|
||||||
|
"__id__": 33
|
||||||
|
},
|
||||||
|
"_enabled": true,
|
||||||
|
"__prefab": null,
|
||||||
|
"_contentSize": {
|
||||||
|
"__type__": "cc.Size",
|
||||||
|
"width": 100,
|
||||||
|
"height": 100
|
||||||
|
},
|
||||||
|
"_anchorPoint": {
|
||||||
|
"__type__": "cc.Vec2",
|
||||||
|
"x": 0.5,
|
||||||
|
"y": 0.5
|
||||||
|
},
|
||||||
|
"_id": "b4q29azdFN5o73K3kHuaVX"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "c9dc3Tde6BMC7Mls6KOasWP",
|
||||||
|
"_name": "",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"node": {
|
||||||
|
"__id__": 33
|
||||||
|
},
|
||||||
|
"_enabled": true,
|
||||||
|
"__prefab": null,
|
||||||
|
"playerInfoView": {
|
||||||
|
"__id__": 24
|
||||||
|
},
|
||||||
|
"_id": "c1JK5vephNPJufMYxFCDrz"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"__type__": "cc.UITransform",
|
"__type__": "cc.UITransform",
|
||||||
"_name": "",
|
"_name": "",
|
||||||
@@ -523,34 +796,37 @@
|
|||||||
"nestedPrefabInstanceRoots": [
|
"nestedPrefabInstanceRoots": [
|
||||||
{
|
{
|
||||||
"__id__": 5
|
"__id__": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__id__": 24
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"__type__": "cc.SceneGlobals",
|
"__type__": "cc.SceneGlobals",
|
||||||
"ambient": {
|
"ambient": {
|
||||||
"__id__": 27
|
"__id__": 40
|
||||||
},
|
},
|
||||||
"shadows": {
|
"shadows": {
|
||||||
"__id__": 28
|
"__id__": 41
|
||||||
},
|
},
|
||||||
"_skybox": {
|
"_skybox": {
|
||||||
"__id__": 29
|
"__id__": 42
|
||||||
},
|
},
|
||||||
"fog": {
|
"fog": {
|
||||||
"__id__": 30
|
"__id__": 43
|
||||||
},
|
},
|
||||||
"octree": {
|
"octree": {
|
||||||
"__id__": 31
|
"__id__": 44
|
||||||
},
|
},
|
||||||
"skin": {
|
"skin": {
|
||||||
"__id__": 32
|
"__id__": 45
|
||||||
},
|
},
|
||||||
"lightProbeInfo": {
|
"lightProbeInfo": {
|
||||||
"__id__": 33
|
"__id__": 46
|
||||||
},
|
},
|
||||||
"postSettings": {
|
"postSettings": {
|
||||||
"__id__": 34
|
"__id__": 47
|
||||||
},
|
},
|
||||||
"bakedWithStationaryMainLight": false,
|
"bakedWithStationaryMainLight": false,
|
||||||
"bakedWithHighpLightmap": false
|
"bakedWithHighpLightmap": false
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
import { _decorator, Component, Node } from 'cc';
|
||||||
|
|
||||||
|
const { ccclass, property } = _decorator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SceneStart: 端到端联调脚本(真机验证响应式链路)。
|
||||||
|
*
|
||||||
|
* 工作流:
|
||||||
|
* 1. onLoad/start 时构造一个 mock playerState(自包含 signal)
|
||||||
|
* 2. 找到场景里的 PlayerInfoView 节点(通过 property)
|
||||||
|
* 3. 调用 view.bindTo(mockSession),bind 会立即触发一次 applyPlayer(首次同步)
|
||||||
|
* 4. 3 秒后改变 playerState.value,验证 view 自动更新 Label.string
|
||||||
|
*
|
||||||
|
* 这模拟完整的「login 事件 → PlatformSession 填 Store → view 自动刷新」链路,
|
||||||
|
* 不依赖真 WS,但能让 cocos 编辑器运行时验证响应式模式正确。
|
||||||
|
*/
|
||||||
|
@ccclass('SceneStart')
|
||||||
|
export class SceneStart extends Component {
|
||||||
|
@property(Node)
|
||||||
|
playerInfoView: Node = null!;
|
||||||
|
|
||||||
|
start(): void {
|
||||||
|
if (!this.playerInfoView) {
|
||||||
|
console.error('[SceneStart] playerInfoView 未设置');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mock session:playerState 是自包含 signal(模拟 ReadonlyReactive)
|
||||||
|
const playerState: any = {
|
||||||
|
value: { nickname: '初始玩家', bean: 0, avatar: '' },
|
||||||
|
subscribe(cb: (v: any, p: any) => void) {
|
||||||
|
const subs: Array<(v: any, p: any) => void> = ((playerState as any).__subs ??= []);
|
||||||
|
subs.push(cb);
|
||||||
|
return () => {
|
||||||
|
const i = subs.indexOf(cb);
|
||||||
|
if (i >= 0) subs.splice(i, 1);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// 触发 setter 时调用所有订阅
|
||||||
|
Object.defineProperty(playerState, 'value', {
|
||||||
|
get() { return (playerState as any).__value; },
|
||||||
|
set(v: any) {
|
||||||
|
const prev = (playerState as any).__value;
|
||||||
|
(playerState as any).__value = v;
|
||||||
|
for (const fn of ((playerState as any).__subs ?? [])) {
|
||||||
|
try { fn(v, prev); } catch (e) { console.error(e); }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
(playerState as any).__value = { nickname: '初始玩家', bean: 0, avatar: '' };
|
||||||
|
|
||||||
|
const mockSession: any = { player: { state: playerState } };
|
||||||
|
|
||||||
|
const view: any = this.playerInfoView.getComponent('PlayerInfoView');
|
||||||
|
if (!view || typeof view.bindTo !== 'function') {
|
||||||
|
console.error('[SceneStart] PlayerInfoView 组件未挂或无 bindTo');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
view.bindTo(mockSession);
|
||||||
|
console.log('[SceneStart] bindTo 已触发,nickname 应为「初始玩家」');
|
||||||
|
|
||||||
|
this.scheduleOnce(() => {
|
||||||
|
playerState.value = { nickname: '更新后玩家', bean: 100, avatar: '' };
|
||||||
|
console.log('[SceneStart] 改变 playerState,nickname 应自动变为「更新后玩家」');
|
||||||
|
}, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user