Cocos Creator 3.x对于开放数据域的二次开发问题

cocos creator升级到 3.x以后,开放数据域的使用方式发生了重大变化。在
官方开放数据域github示例项目 中,提到了最重大的变更:

Cocos Creator 3.x 无需依赖 Cocos Creator 2.x 导出子域工程,可以直接通过构建面板勾选 生成开放数据域工程模版 直接构建出开放数据域工程。

本人在使用时,感觉确实比2.x版本在生成开放数据域工程方面更加方便。
但是,在默认示例工程基础上进行二次开发时,仍然遇到问题。

开发环境:

  • PC win10
  • cocos creator 3.4.1
  • 微信开发者工具stable 1.05.2102010

问题描述:
对默认生成的开放数据域模板工程进行二次开发改造,
主要是在dataDemo.js文件中,将默认示例数据改为通过wx.getFriendCloudStorage(Object object)方法获取,并将获取到的数据添加到 dataDemo.data 对象数组中,用于在排行榜页面展示。
本人初步的实现如下:

wx.getFriendCloudStorage({
        keyList:['week_rank'],
        success:(res) => {
            let item = {};
            for (let i = 0; i < res.data.length; i++) {
                item.avatarUrl = res.data[i].avatarUrl;
                item.nickname = res.data[i].nickname;
                item.rankScore = Number(res.data[i].KVDataList[0].value);
                console.log("item is %o", item);
                dataDemo.data.push(item);
            }
            dataDemo.data.sort((a, b) => a.rankScore - b.rankScore);
            console.log("push over dataDemo.data : %o", dataDemo.data);
        },
        fail:(res) => {
            console.log("fail, res is %o ", res);
        }
    });

从控制台输出结果看,数据是获取并添加成功的

然而排行榜页面显示为空。
2空排行榜

但是,如果将前一步获取的数据硬编码添加到目标数组

let item = {};
item.avatarUrl = "https://wx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTLebniaI51eIuuFTwdDsc2Y0hk2888sQvhzgKulJGpkicOicRsl5TJnaOxeTPtftEsGIqp4E6So3RzNg/132";
item.nickname = "有微信没有威信1";
item.rankScore = "64.2";
dataDemo.data.push(item);
console.log("dataDemo.data : %o", dataDemo.data);    

则排行榜页面正常显示:
3有数据排行榜

综上,问题的关键在于,由于wx.getFriendCloudStorage方法是异步执行的,当wx.getFriendCloudStorage方法尚未执行完时,排行榜页面已经加载完成了,导致获取到的数据未显示在排行榜页面。因此,希望在方法执行完成、dataDemo.data的数据也添加完毕后,再加载排行榜页面。请问,如何能较好地实现这一需求?

另,本人对3.x版本开放数据域使用的doT.js模板引擎不够了解,官方文档及其他资料也比较缺失。实在不知如何入手,甚至不知道是否应该将wx.getFriendCloudStorage的方法直接放在dataDemo.js中。
首次发帖求助,期望得到解答!
最好希望官方能提供3.x版本关于开发数据域的相对完整的示例工程。
谢谢!

如果在 wx.getFriendCloudStorage 函数的 success 回调中,等数据填充完了之后再显示控件,是否能解决问题?

感谢回复!
确实如您所说,思路就是在wx.getFriendCloudStorage 的 success 回调中,等数据填充完再显示控件。但是由于以下原因,暂时没找到实现方法:

  1. 由于wx.getFriendCloudStorage只能在开放数据域使用,而主域与开放数据域是单向通信的,只能主域发送给开放数据域,反之则不行。因此主域无法获取开放数据域的执行状态,不能决定何时显示排行榜控件。(写到这,突然想到,如果主域先发送指令,再手动设置一个适当的延时,比如3秒之后,足够开放数据域填充完成,主域再显示排行榜,似乎可行?等有机会试试。但是这样实现肯定是不太合理。)
  2. 由于开放数据域运用了doT.js模板引擎,根据我有限的研究,执行顺序似乎应该是:
    入口index.js --> draw()方法调用模板template.js --> 模板template.js内加载dataDemo 的数据。
    但是此过程似乎只执行了一次,加载数据完成后,后续再向dataDemo添加数据,排行榜页面也不再刷新。不知是我使用的问题,还是doT.js引擎需要特殊配置才能支持动态刷新?

顺便吐槽一下doT.js对于不熟悉的人使用体验不够友好,官方文档太少(似乎就一页)、网上资料少,看起来用着挺容易,实际用起来好像坑挺多的。比如 template.js文件,模板内容经过编译后,生成的方法

export default anonymous(dataDemo);

一直没弄懂为什么方法名叫anonymous,也没发现是在哪里调用的(就算这不重要)

因此希望cocos官方最好能提供一版比较完整的3.x版本的开放数据域教程。
谢谢!

同求一个完整教程

找到了一个解决方案。

【教程】cocos creator 3.x 微信小游戏子域

(正好比我的问题晚了几天)
我看了一下,主要思路如下:

// index.js,调用getFriendRankData,传入两个参数
// 第二个参数drawRank是方法名,其实就是官方默认示例的draw()方法
else if (data.key == "RankData") {
    getFriendRankData("RankData", drawRank)
}
// dataDemo.js
// getFriendRankData是在dataDemo.js中定义的,
// 执行wx.getFriendCloudStorage的最后,调用callback方法。
export function getFriendRankData(key, callback) {
	wx.getFriendCloudStorage({
		keyList: [key],
		success: res => {
			// console.log(“getFriendData success--------”, res);
			friendRankData.data = res.data;
			friendRankData.data.sort((a, b) => b.KVDataList[0].value - a.KVDataList[0].value);
			friendRankData.itemBg = ‘openDataContext/render/itemBg.png’;
			for (let i = 0; i < friendRankData.data.length; i++) {
				friendRankData.data[i].rankImg = ‘openDataContext/render/Rank_’ + (i + 1) + ‘.png’;
			}
      callback && callback();
	 },
	 // ......
})

完整代码就不粘贴了,直接参见原贴 【教程】cocos creator 3.x 微信小游戏子域

楼主解决了吗?
我也遇到了同样的问题。
现在模板又有轻微改动。我在数据填充好以后调用draw(),也不会绘制。

还是画不出来。

可以试试:

  1. template.js里去掉对dataDemo的引用
    然后将exports改成:
module.exports ={

 getTemplate(data){

   return  anonymous(data);

 }

};
  1. 将dataDemo.js改成
let dataDemo = {

  data: [],

};

function initData(data){

  const arr = [];

  const maxCount = 30;

  for (let i = 0; i < data.length; ++i) {

    let item = {};

    const playerInfo=data[i];

    item.rankScore = parseInt(playerInfo.KVDataList[0].value);

    item.avatarUrl = playerInfo.avatarUrl;

    item.nickname = playerInfo.nickname;

    arr.push(item);

  }

  arr.sort((a, b) => b.rankScore - a.rankScore);

  dataDemo.data=arr.slice(0, maxCount);

  console.log(dataDemo.data);

}

module.exports = {

  data:dataDemo,

  initData

};
  1. 将index.js改成
const style = require('./render/style')

const template = require('./render/template')

const dataDemo = require('./render/dataDemo')

const Layout = require('./engine').default;

let __env = GameGlobal.wx || GameGlobal.tt || GameGlobal.swan;

let sharedCanvas  = __env.getSharedCanvas();

let sharedContext = sharedCanvas.getContext('2d');

function draw(data) {

    Layout.clear();

    Layout.init(template.getTemplate(data), style);

    Layout.layout(sharedContext);

}

function updateViewPort(data) {

    Layout.updateViewPort({

        x: data.x,

        y: data.y,

        width: data.width,

        height: data.height,

    });

}

function requestData(cb){

wx.getFriendCloudStorage({

  keyList: ["你的key名"],

  success(resp) {

    cb(resp.data);

  },

  fail(err) {

    console.error(err);

  },

});

}

__env.onMessage(data => {

    if ( data.type === 'engine' && data.event === 'viewport' ) {

        updateViewPort(data);

        requestData((data)=>{

          dataDemo.initData(data);

          draw(dataDemo.data);

        })

    }

});

我自己好使了。
image

1赞

每次都得申请一下插件 ,挺麻烦的

特意登录来感谢大神,真的好使!

确认好使的