<noframes id="bhrfl"><address id="bhrfl"></address>

    <address id="bhrfl"></address>

    <noframes id="bhrfl"><address id="bhrfl"><th id="bhrfl"></th></address>

    <form id="bhrfl"><th id="bhrfl"><progress id="bhrfl"></progress></th></form>

    <em id="bhrfl"><span id="bhrfl"></span></em>

    全部
    常見問題
    產品動態
    精選推薦

    UniApp 使用 RenderJS 在 App 端實現復雜交互

    管理 管理 編輯 刪除

    前言

    使用 UniApp 開發 Android / iOS App 時,經常會有地圖、可視化圖表、攝像頭、視頻播放、人臉采集、富文本編輯器等需求。而 UniApp 提供的相關組件或多或少存在各種影響使用的問題:如地圖層級過高難以覆蓋,UniApp 插件市場中的可視化圖表功能缺失、攝像頭相關 API 接口過于簡陋等,使得開發工作難以進行。

    RenderJS

    在這種情況下,RenderJS 的出現極大程度上緩解了這些難題。 RenderJS 是內置在 UniApp 框架中的一個組件,它運行在視圖層,可以在 App 端中對 DOM 直接進行操作,并運行 for web 的 JavaScript 庫,這意味著在 Web 支持的一切功能都可以在 App 端實現。

    與此同時, RenderJS 也存在著一定的限制:如不支持 Vue3 的 Setup 寫法,不能直接和邏輯層進行通信,需要借助一定的技巧才能實現雙向通信等。

    示例代碼

    下面是 RenderJS 實際使用的一點例子,這些代碼想說明的問題只有一個:視圖層和邏輯層如何互相調用方法和傳遞數據。示例比較長,可以翻到下面跟著說明來閱讀代碼。

    <template>
      <view id="container" style="padding-top: 20vh;" :viewLayerCallJson="viewLayerCallJson"
        :change:viewLayerCallJson="viewLayer.handleLogicLayerCall" :viewLayerData="viewLayerData" :change:viewLayerData="viewLayer.handleLogicLayerData">
        <button @click="handleTest">調用邏輯層方法</button>
        <button @click="viewLayer.handleViewLayerTest">調用視圖層方法</button>
        <button @click="handleTest2">從邏輯層調用視圖層方法</button>
        <button @click="viewLayer.handleViewLayerTest2">從視圖層調用邏輯層方法</button>
    
        <button @click="handleSendDataToViewLayer">視圖層傳遞數據給視圖層</button>
        <button @click="handleTest4">動態創建 VIDEO 元素</button>
      </view>
    </template>
    
    <script>
    export default {
      data() {
        return {
          viewLayerCallJson: null,
          viewLayerData: null
        };
      },
      methods: {
        handleViewLayerCall({ method, params }) {
          this[method]?.(params);
        },
        handleCallViewLayerFunc(method, params) {
          const randomNethodPrefix = Math.random().toString(36).substring(2, 15);
          this.viewLayerCallJson = JSON.stringify({
            method: `${randomNethodPrefix}-${method}`,
            params
          });
        },
        handleTest2() {
          this.handleCallViewLayerFunc("createEl", {
            tag: "input",
            value: "hahahaha"
          });
        },
        handleTest3({ content }) {
          uni.showModal({
            title: "提示",
            content,
            success: (res) => {
              console.log(res);
            }
          });
        },
        handleTest4() {
          this.handleCallViewLayerFunc("createEl", {
            tag: "video"
          });
        },
        handleTest() {
          uni.showModal({
            title: "提示",
            content: "邏輯層方法被調用啦",
            success: (res) => {
              console.log(res);
            }
          });
        },
        handleSendDataToViewLayer() {
          this.viewLayerData = Date.now();
        }
      }
    }
    </script>
    
    <script lang="renderjs" module="viewLayer">
    export default {
      data() {
          return {
              _data: {}
          };
      },
      methods: {
          handleViewLayerTest() {
              alert("視圖層方法被調用啦")
          },
          handleViewLayerTest2() {
              this.callLogicLayerFunc("handleTest3", { 
                  content: "從視圖層調用邏輯層方法"
              });
          },
          handleLogicLayerCall(json) {
              if (!json) return;
              const { method: randomMethod, params } = JSON.parse(json);
              const method = randomMethod.split("-")[1];
              this[method]?.(params);
          },
          createEl({ value, tag }) {
              const el = document.createElement(tag);
              if (value) {
                el.value = value;
              }
              document.querySelector("#container").append(el);
          },
          callLogicLayerFunc(method, params) {
              this.$ownerInstance.callMethod("handleViewLayerCall", {
                  method,
                  params
              });
          },
          handleLogicLayerData(newval, oldval, owner, instance) {
            console.log(newval, oldval, owner, instance);
          }
      }
    }
    </script>
    
    <style>
    button + button {
      margin-top: 20px;
    }
    </style>


    編寫 RenderJS 代碼

    寫在正常 script 標簽中的代碼運行在邏輯層,而 RenderJS 運行在視圖層。 RenderJS 的代碼在編寫時需要單獨寫在一個 script 標簽中,同時給 script 標簽添加 lang="renderjs" 和 module="xxx" 的屬性,前者是固定值不能修改,后者可以自行定義,用來在模板中引用 RenderJS 中定義的屬性和方法。

    df086202502201508222378.png

    邏輯層向視圖層傳遞數據

    傳遞數據需要關注以下幾點:

    1. 在邏輯層中定義數據,這個無需多言,正常定義要傳遞的數據即可。

    e1682202502201513267900.png

    2. 在模板內任意標簽中綁定要傳遞的數據,通過 :change:[屬性名] 來監聽綁定數據的變化,并綁定在視圖層中定義的回調函數,綁定視圖層中定義的回調函數時需要以視圖層 module 屬性為前綴來調用。

    02892202502201517406156.png

    3. 在視圖層內定義屬性變化的回調函數

    df5bb202502201519323447.png

    回調函數會傳遞四個參數,分別是綁定屬性新值,上一次的值,視圖層實例以及邏輯層實例,需要注意回調函數在組件創建之后無論數據是否變化都會被調用一次。


    視圖層向邏輯層傳遞數據

    視圖層并不能直接向邏輯層傳遞數據,只能借助調用邏輯層方法的形式來傳遞數據,具體可以參見下一小節。


    視圖層調用邏輯層方法

    視圖層可以通過 $ownerInstance.callMethod 方法來調用邏輯層內定義的方法,該方法接受兩個參數,邏輯層方法名和邏輯層方法接受的參數。

    e3cc1202502201523519483.png

    邏輯層調用視圖層的方法

    邏輯層并不能直接調用視圖層的方法,但通過一些技巧可以巧妙的實現調用。通過將要調用的方法和參數名序列化為 JSON 字符串,并作為數據傳遞給視圖層,視圖層在回調方法內反序列化 JSON 數據,然后執行相關方法。

    如果連續調用同一個方法并傳遞相同的參數,由于 JSON 字符串沒有發生變化,因為視圖層的回調函數并不會被執行,因此需要在 JSON 字符串內加入一些隨機因子,使得每次調用方法生成的 JSON 字符串總是不相同的。

    96c0e202502201532249511.png

    5baf2202502201533192306.png

    561b9202502201533429526.png

    需要注意傳遞的數據內容必須是兼容 JSON 的基本數據類型,如果傳遞 Set、Map、ArrayBuffer 等類型將會出現不可預知的錯誤,同時傳遞的數據也不宜過大過于頻繁,否則可能會造成性能問題。

    使用場景

    基于以上的例子,以往很多不能實現的操作都可以實現。如引入 ECharts 使用全功能圖表,通過動態創建 Video 元素來避免使用 App 內置的 Video 元素,使用 Web 端的各種地圖組件,不再為 App 端 Map 組件層級過高無法覆蓋而頭痛,使用 navigator.getUserMedia 操作攝像頭來實現自定義視頻錄制界面等等,一切在 Web 端可以實現的功能都可以在 App 中實現,不再受限于 UniApp App 的 API 限制。


    注意事項

    RenderJS 僅兼容 App 和 Web 兩端,雖然在微信小程序中存在著類 RenderJS 的組件 WXS,但其只是 RenderJS 的剪裁版,因此并不兼容。

    RenderJS 是一個用來在 App 中實現 Web 相關能力的產物,因此在 Web 中并不需要 RenderJS。 RenderJS 在 Web 端運行時,其會以 mixin 形式混入邏輯層,因此在編寫視圖層代碼時,應注意在屬性名和方法名上和邏輯層加以區分,以免出現在 Web 端運行時被覆蓋的問題。

    請登錄后查看

    plz 最后編輯于2025-02-20 16:16:52

    快捷回復
    回復
    回復
    回復({{post_count}}) {{!is_user ? '我的回復' :'全部回復'}}
    排序 默認正序 回復倒序 點贊倒序

    {{item.user_info.nickname ? item.user_info.nickname : item.user_name}} LV.{{ item.user_info.bbs_level }}

    作者 管理員 企業

    {{item.floor}}# 同步到gitee 已同步到gitee {{item.is_suggest == 1? '取消推薦': '推薦'}}
    {{item.is_suggest == 1? '取消推薦': '推薦'}}
    沙發 板凳 地板 {{item.floor}}#
    {{item.user_info.title || '暫無簡介'}}
    附件

    {{itemf.name}}

    {{item.created_at}}  {{item.ip_address}}
    {{item.like_count}}
    {{item.showReply ? '取消回復' : '回復'}}
    刪除
    回復
    回復

    {{itemc.user_info.nickname}}

    {{itemc.user_name}}

    回復 {{itemc.comment_user_info.nickname}}

    附件

    {{itemf.name}}

    {{itemc.created_at}}
    {{itemc.like_count}}
    {{itemc.showReply ? '取消回復' : '回復'}}
    刪除
    回復
    回復
    查看更多
    2599
    {{like_count}}
    {{collect_count}}
    添加回復 ({{post_count}})

    相關推薦

    快速安全登錄

    使用微信掃碼登錄
    {{item.label}} 加精
    {{item.label}} {{item.label}} 板塊推薦 常見問題 產品動態 精選推薦 首頁頭條 首頁動態 首頁推薦
    取 消 確 定
    回復
    回復
    問題:
    問題自動獲取的帖子內容,不準確時需要手動修改. [獲取答案]
    答案:
    提交
    bug 需求 取 消 確 定

    微信登錄/注冊

    切換手機號登錄

    {{ bind_phone ? '綁定手機' : '手機登錄'}}

    {{codeText}}
    切換微信登錄/注冊
    暫不綁定
    亚洲欧美字幕
    CRMEB客服

    CRMEB咨詢熱線 咨詢熱線

    400-8888-794

    微信掃碼咨詢

    CRMEB開源商城下載 源碼下載 CRMEB幫助文檔 幫助文檔
    返回頂部 返回頂部
    CRMEB客服