一种曲折的uniapp接入第三方sdk方式
标签搜索

一种曲折的uniapp接入第三方sdk方式

指针原来是套娃的
2026-06-01 / 0 评论 / 2 阅读 / 正在检测是否收录...

uniapp当前痛点

采用uniapp框架开发时,很多情况下会收到第三方原生生态的制约,一些第三方的服务可能只有安卓、ios、web的sdk,并不会单独对uniapp开发适配的sdk。如果想要用uts的方式进行接入,需要每个端都写一套适配的代码(安卓、ios或鸿蒙),这样带来的开发成本会很高。而且引入过多的sdk会提高app的内存大小,在云打包时安卓如果超过150MB,每打包一次都要收费。

我们的项目就遇到了这样的问题,使用到的第三方服务没有提供uniapp版本的插件,需要手动的接入原生的sdk,非常复杂,而且sdk还有很多类型声明和官方文档上的不一致,成了开发的卡点。

通过翻阅uniapp文档,我们发现了一种叫做renderjs的方式,可以在视图层运行的JS。

renderjs介绍与使用

官方文档:https://uniapp.dcloud.net.cn/tutorial/renderjs.html#renderjs

uniapp打包的app,其实是一种桥接技术,将原生能力和webview桥接起来,所以在官方文档的划分中,有逻辑层和视图层两个概念。我们编写的代码都会运行在逻辑层中,通过一种特定的通信来控制视图层,所以这样会有不少的性能损耗,也是代码里不能频繁的更新视图的原因。在逻辑层中,也没有dom相关的元素给我们使用,而视图层不一样。

视图层由webview渲染,拥有dom和window,renderjs天然的处于webview环境中,可以直接操作视图dom,毫无通讯损耗。最关键的是它相当于在浏览器环境运行的代码,可以接入第三方的web端sdk,正常的获取dom和window元素,不会像逻辑层的代码一样,抛出各种不兼容。

使用renderjs,需要先在template中挂载view标签,并单独写一个script。代码如下:

<template>
    <!-- renderjs数据绑定 -->
    <view :prop="rtmConfig" :change:prop="agora.handleConfig" style="display: none"></view>
</template>

<script lang="ts" setup>
    // 通知renderjs使用RTM加入频道
    rtmConfig.value = {
        userId: 'xxxxxxxx'
    };
    
     /**
     * 处理renderjs消息(通过事件总线)
     */
    const handleRenderjsMessage = (message: any) => {
        console.log('收到renderjs消息:', message);
    };
    
    onBeforeMount(async () => {
        // 监听renderjs消息事件(使用事件总线)
        uni.$on('renderjs-message', handleRenderjsMessage);
    });
</script>

<!-- Vue 2 桥梁:用于 renderjs 调用,可以使用 uni.$emit -->
<script lang="ts">
    export default {
        methods: {
            // renderjs 调用此方法发送消息给 Vue 3
            handleRenderjsBridge(data: any) {
                try {
                    console.log('Vue2桥梁收到消息:', JSON.stringify(data));
                    // 使用 uni.$emit 发送事件给 Vue 3
                    uni.$emit('renderjs-message', data);
                } catch (error) {
                    console.error('Vue2桥梁发送消息失败:', error);
                }
            }
        }
    };
</script>

<script module="agora" lang="renderjs">
    export default {
        data() {
            return {
                // #ifdef APP
                sdkUrl: 'static/js/xxx.js',
                // #endif
                // #ifdef H5
                sdkUrl: '/static/js/xxx.js',
                // #endif
                ownerInstance: null
            }
        },
        mounted() {
            console.log('Agora RenderJS mounted - 等待配置');
        },
        methods: {
            // 初始化Agora
            async initAgora() {
                try {
                    console.log('正在初始化...');
                    // 动态加载Agora RTM SDK
                    await this.loadAgoraSDK();
                    console.log('初始化完成');
                } catch (error) {
                    console.error('初始化失败:', error);
                }
            },

            // 动态加载Agora SDK
            loadAgoraSDK() {
                return new Promise((resolve, reject) => {
                    console.log('准备加载SDK,路径:', this.sdkUrl);
                    const script = document.createElement('script');
                    script.src = this.sdkUrl;
                    script.onload = () => {
                        console.log('ASDK加载成功');
                        resolve();
                    };
                    script.onerror = (error) => {
                        console.error('SDK加载失败,错误:', error);
                        console.error('尝试加载的URL:', this.sdkUrl);
                    };
                    document.head.appendChild(script);
                });
            },

            // 处理配置 - 使用uni-app renderjs规范
            handleConfig(newValue, oldValue, ownerInstance, instance) {
                console.log('收到配置信息:', JSON.stringify(newValue));
                // 保存ownerInstance供后续使用
                this.ownerInstance = ownerInstance;
                // 配置接收后初始化sdk
                this.initAgora();
            },

            // 发送消息给逻辑层
            sendMessage(data) {
                try {
                    console.log('正在发送消息给逻辑层:', JSON.stringify(data));
                    // 通过 ownerInstance.callMethod 调用 Vue 2 的方法
                    // Vue 2 方法中可以使用 uni.$emit
                    if (this.ownerInstance && this.ownerInstance.callMethod) { this.ownerInstance.callMethod('handleRenderjsBridge', data);
                    } else {
                        console.warn('ownerInstance.callMethod 不可用');
                    }
                } catch (error) {
                    console.error('发送消息失败:', error);
                }
            }
        }
    }
</script>

agora可以自定义命名,rtmConfig是正常的逻辑层代码的变量,当rtmConfig绑定的数据发生改变后,会立即通知到agora的handleConfig中,这样我们就实现了逻辑层传递内容到renderjs中,函数有四个参数:

newValue: 变化后的新值
oldValue: 变化前的旧值
ownerInstance: 组件实例对象,可以获取逻辑层的函数,ownerInstance.callMethod('handleRenderjsBridge', data);
instance: 当前 renderjs 模块所在视图层的实例对象,通过 `instance.$el` 可以直接获取当前绑定的 DOM 元素

通过document.createElement('script')挂载js,即可实现加载第三方sdk,当sdk处于本地时,h5与app上路径有些区别,app中路径为'static/js/xxx.js',h5中使用'/static/js/xxx.js'。

逻辑层通知renderjs,修改绑定的变量即可,但是renderjs通知给逻辑层就有一些曲折。

因为renderjs当前只能支持vue2的代码,不支持调用setup形式中的函数,我们的处理方案是写一层vue2的script作为桥接,vue2与setup的业务逻辑中通过uni.$emit全局通讯的方式传递。

这里有一个踩坑点需要注意:renderJs需要写在页面级别文件中,不能放在组件里嵌套,绑定的dom元素也需要在页面级别,不能嵌套,不然会报错Mt.setAttribute is not a function。当时排查了很久,发现从组件改到页面级文件就可以了。

使用效果

我们使用这套方案接入了声网RTM的sdk和国外的Stripe支付sdk,减少了很多的开发工作。在后续处理一个视频需求时,发现插件市场也有同样思路处理的视频组件,使用体验非常好,避免了很多原生video的问题:https://ext.dcloud.net.cn/plugin?id=19654

以上。

0

评论

博主关闭了所有页面的评论