Skip to content
目录

GIS地图-renderjs

renderjs

  • 在uniapp中如果我们要实现地图离线加载,那就不能像原生小程序使用web-view那样的写法,这里需要用到uniapp的 renderjs 来开发地图,这里面的内容一定要逐行去看,尤其是底下的注意事项,要看并且要理解,文档里面官方也给了一个例子。

  • APP 端可以使用 dom、bom API,不可直接访问逻辑层数据,不可以使用 uni 相关接口(如:uni.request)

  • APP 端视图层的页面引用资源的路径相对于根目录计算,例如:./static/test.js。

  • 在renderjs的script中,是无法获取到uni中script--data中的数据的,所以要通过一定的方式去传输。

  • 调用方法时也可以通过this.$ownerInstance.callMethod('函数名', 数据)向uni的script发送。

封装

js
/**
 * author: wangkaibing
 * time: 2025-01-10 10:21:30
 * 
 * 
 * renderjs通信封装
 * 
 * 设计模式解释:
 * 
 * 真实业务中可能会有多张地图调用这个通信连接方法,这样每个 renderjs 组件需要独立的通信连接,不同页面的通信不能混淆,并且需要统一的事件处理机制,所以我采用类来封装
 * 
 * 
 * 1. 单例模式:整个应用只需要一个通信实例,多个实例会导致混乱
 *    - 通过 export const bridge = new RenderBridge() 导出单例
 *    - 其他地方引入时都是同一个实例
 * 
 * 2. 观察者模式:一个对象(目标)维护一组依赖它的对象(观察者),当状态发生改变时通知它们
 *    - eventHandlers 存储所有的观察者(处理函数)
 *    - 数据变化时通知相应的观察者
 * 
 * 主要用于解决以下场景:
 * 1. 地图操作:在renderjs中处理地图事件后,需要通知逻辑层更新数据
 * 2. 数据同步:逻辑层数据更新后,需要在renderjs中响应变化
 * 3. 离线存储:地图瓦片下载后,需要通知逻辑层进行存储
 */

// 【单例模式】:通过class定义RenderBridge类
class RenderBridge {
  constructor() {
    // 用于调用逻辑层的方法,比如保存离线地图、更新轨迹等
    this.ownerInstance = null
    
    // 【观察者模式】:eventHandlers作为观察者集合
    // 使用 Map 结构存储,key 是数据名(如'message'),value 是观察者函数
    // 例如: this.eventHandlers.set('message', (newVal) => console.log(newVal))
    this.eventHandlers = new Map()
  }

  /**
   * 保存vue实例,后续用于调用逻辑层的方法
   * 比如:地图下载瓦片后,调用逻辑层的保存方法
   */
  init(ownerInstance) {
    // 【单例模式】:确保只初始化一次
    if (!this.ownerInstance && ownerInstance) {
      this.ownerInstance = ownerInstance
			console.log('🚀 ~ [RenderBridge:]', '初始化成功')
    }
  }

  /**
   * 调用逻辑层的方法
   * 比如:
   * 1. 地图点击后,通知逻辑层更新坐标
   * 2. 下载瓦片后,通知逻辑层保存到本地
   * 3. 轨迹绘制完成后,通知逻辑层保存轨迹
   */
  callMethod(method, data) {
    if (!this.ownerInstance) {
			console.log('🚀 ~ [RenderBridge:]', '请先初始化')
      return
    }
    try {
      this.ownerInstance.callMethod(method, data)
			console.log('🚀 ~ [RenderBridge:]', `${method}调用成功`, data)
    } catch (e) {
			console.log('🚀 ~ [RenderBridge:]', `${method}调用失败`, e)
    }
  }

  /**
   * 【观察者模式】:注册观察者,监听逻辑层数据变化
   * 比如:
   * 1. 监听地图配置变化,实时更新地图样式
   * 2. 监听坐标变化,更新地图标记位置
   * 3. 监听网络状态变化,切换在线/离线地图
   */
  onDataChange(key, handler) {
    // 将观察者函数存入Map
    this.eventHandlers.set(key, handler)
  }

  /**
   * 【观察者模式】:通知观察者,处理逻辑层数据变化
   * 比如:
   * 1. 配置更新:逻辑层切换地图类型,renderjs层切换地图图层
   * 2. 位置更新:逻辑层传入新坐标,renderjs层更新地图中心点
   * 3. 样式更新:逻辑层修改地图样式,renderjs层重新渲染地图
   */
  handleDataChange(key, newVal, oldVal, ownerInstance) {
    this.init(ownerInstance)

    // 获取对应的观察者函数并执行
    const handler = this.eventHandlers.get(key)
    if (handler) {
      handler(newVal, oldVal)
    }
  }
}

// 导出单例,这样其他地方引入时都是同一个实例
export const bridge = new RenderBridge() 

使用

js
<template>
  <view class="wrap">
    <view class="tips">点击空白区域Renderjs向逻辑层通信</view>
    <view
      class="container"
      :message="message"
      :change:message="renderModule.handleMessageChange"
      @click="renderModule.handleClick"
    ></view>
    <view class="result-area">
      <view class="result-item">
        <text class="label">Renderjs接收到消息:</text>
        <text class="content">{{ message }}</text>
      </view>
      <view class="result-item">
        <text class="label">逻辑层接收到消息:</text>
        <text class="content">{{ eventData }}</text>
      </view>
    </view>
    <view class="controls">
      <button @click="sendMessage">逻辑层向Renderjs通信</button>
    </view>
		<view class="tips">注意</view>
		<view>这套rendejs通信只是简单封装,以下2个问题需要在真实业务中验证:</view>
		<view>1.性能开销过大</view>
		<view>2.通信太频繁,消息堆积,数据更新可能会有延迟</view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      message: '暂无数据',
      eventData: '暂无数据',
    }
  },
  methods: {
    sendMessage() {
      this.message = '来自逻辑层的消息 time: ' + Date.now()
    },
    handleEvent(e) {
      console.log('🚀 ~ [逻辑层收到renderjs的数据:]', e)
      this.eventData = e
    },
  },
}
</script>

<script module="renderModule" lang="renderjs">
import { bridge } from '../../utils/renderBridge.js'

export default {
  mounted() {
    // 监听message变化
    bridge.onDataChange('message', (newVal, oldVal) => {
        console.log('🚀 ~ [message changed:]', newVal)
    })
  },
  
  methods: {
    // 监听父组件数据变化
    handleMessageChange(newVal, oldVal, ownerInstance) {
      bridge.handleDataChange('message', newVal, oldVal, ownerInstance)
    },

    // 点击事件处理
    handleClick() {
      console.log('🚀 ~ [点击事件触发]')
      bridge.callMethod('handleEvent', '来自Renderjs的消息 time: ' + Date.now())
    }
  }
}
</script>

Released under the MIT License.