钉钉工作台组件间通讯优化

459 阅读3分钟

背景

在钉钉工作台组件中,我们需要实现一个公共的JWT组件,其他组件在获取数据之前需要通过该组件获取JWT,并使用JWT来调用接口返回接口数据。这样可以减少每个请求中用户数据转换的次数,同时在多个钉钉组件之间实现身份验证信息的传递。

存在问题

在组件初始化阶段,我们使用以下方法去获取JWT组件的数据:

getSdk().triggerCustomEvent('dingdingworkbench/request', data);

然而,有时候JWT组件尚未完全加载,导致上述方法失效,进而导致其他组件无法正确加载数据。

问题分析

目前工作台组件的加载顺序依赖于网络请求的完成情况,即先完成加载的组件资源会先执行。官方并未提供监听组件加载状态的方法。

解决方案

  1. 等待JWT组件加载完成后,触发其他组件的初始化,并设定一个超时时间。
  2. 如果在限定时间内未收到消息通知,组件可以自行发起一次请求。

通过以上优化方案,我们可以解决组件间数据传递的同步问题,确保在合适的时机获取到JWT并进行后续操作,从而提升组件间交互的稳定性和可靠性。

//JWT组件

Component({
  data: {
    ddJWT:'',
    refreshData:'',
    componentInitSate:{},// 组件初始化状态,记录各组件是否已初始化
    hasReceivedNotification:false,// 是否收到所有组件的通知
  },
  props: {
    componentName: '',
    componentProps: {
      gateWayJWT: {},
      componentNames:"LatestNews,Document,Column",// 需要发送消息通知的组件列表
    },
  },

  didMount() {
    let _this = this
    getLifecycleSdk().didMount(this.props.componentName);
    this.iniJWT()
    this.requestParams = (returnData) => {
      console.log('returnData',returnData)
      if(returnData){
       // 通知返回,监听 componentName/request ,触发说明 componentName 组件初始化完成
       let componentName = returnData.pluginName.split('/')[0];
        let componentInitState = this.data.componentInitState;
        componentInitState[componentName] = true;

        let hasReceivedNotification = Object.values(componentInitState).every(state => state);
        
        this.setData({
          hasReceivedNotification: hasReceivedNotification,
          componentInitState: componentInitState
        });
      }
      _this.getData(returnData)//调取接口

    };

    this.refreshData = this.iniJWT.bind(this);
    getSdk().listenCustomEvent('onShow', this.refreshData);
    getSdk().listenCustomEvent('dingdingworkbench/request', this.requestParams);
  },
  didUpdate() {
    getLifecycleSdk().didUpdate(this.props.componentName);
  },
  didUnmount() {
    getLifecycleSdk().didUnmount(this.props.componentName);
    getSdk().removeCustomEvent('dingdingworkbench/request', this.requestParams);
    getSdk().removeCustomEvent('onShow', this.refreshData);
  },
  methods: {
    checkDataReceived() {
      if (!this.data.hasReceivedNotification) {
        console.log('No notification received, fetching data...');
        let componentInitSate = this.data.componentInitSate;
        for (let key in componentInitSate) {
          if (componentInitSate.hasOwnProperty(key) && !componentInitSate[key]) {
            getSdk().triggerCustomEvent(`${key}/init`, true);
          }
        }
        this.timeoutId = setTimeout(this.checkDataReceived, 200); // 每200毫秒检查一次
      } else {
        clearTimeout(this.timeoutId); // 停止定时器
      }
    },
    async iniJWT(){
      let _this = this


      // 初始化相关组件接口
      let componentInitState = {};
      const componentNames = this.props.componentProps.componentNames.split(',');

      componentNames.forEach(name => {
        componentInitState[name] = false;
      });

      this.setData({
        componentInitState: componentInitState
      });


      const joinData = JSON.stringify({ apiPath: 'login' }); 
      try {
       // 获取JWT
        const res = await getSdk().request(this.props.componentProps.gateWayJWT, { joinData }, getSdkParam);
        
        if (res.code === '200') {
          this.setData({ ddJWT: res.data });

          this.checkDataReceived = this.checkDataReceived.bind(this);
          this.checkDataReceived(); // 开始数据请求循环


          return Promise.resolve(res.data);
        } else {
          console.error('Failed to initialize JWT:', res);
        }
      } catch (error) {
        console.error('Error initializing JWT:', error);
      }
    },
    async getData(data){
    
  }
});




// Document组件

Component({
  data: {
    isInit:false,//是否初始化
    requestResultName:'Document/requestResult',// <namespace>/<eventType> 事件名用 / 命名,以防和其他服务商的组件冲突。namespace建议用公司的英文名。 namespace = 组件名称
  },
  props: { 
    componentName: '',
    componentProps: {},
  },
  async didMount() {
    getLifecycleSdk().didMount(this.props.componentName);

    // 统一的初始化方法,监听 Document/init ,触发说明JWT组件初始化完成
    this.init = (res) => {
      // res 返回值为布尔值,默认为false,
      if(res){
        //加载页面的相关方法
        if(!this.isInit){//防止重复调用初始化
          this.isInit = true
          this.getListData(0)
        }
        
      }
    };
    // 触发方法 组件名+/init
    getSdk().listenCustomEvent('Document/init', this.init);
    
  },
  didUpdate() {
    getLifecycleSdk().didUpdate(this.props.componentName);
    // 业务代码写到下方
  },

  didUnmount() {
    getLifecycleSdk().didUnmount(this.props.componentName);
    // 业务代码写到下方
    getSdk().removeCustomEvent(this.data.requestResultName, this.requestResult);
    getSdk().removeCustomEvent('Document/init', this.init);
  },
  methods: {
    requestJWT(apiPath,param,pluginName){
     // 触发 dingdingworkbench/request 方法
  let params = {
   apiPath,
   param,
   pluginName
  }
  getSdk().triggerCustomEvent('dingdingworkbench/request', params);
    },
    getListData(index){

      this.requestJWT(apiPath,params,this.data.requestResultName)
      this.requestResult = (res) => {
       //返回数据的处理
      };
      getSdk().listenCustomEvent(this.data.requestResultName, this.requestResult);
    },
    
  }
});