利用renderjs在app端加载for web库

利用renderjs在app端加载for web库

需求背景

公司网页端地图实现使用的是天地图,且已经在其基础上开发了很多功能,现在要在 app 上也实现地图功能,但是 uni-app 官方的 Map 组件只支持 高德、谷歌、腾讯等。为了方便复用已有功能和避免重复购买,要想办法在 app 端也能使用 天地图

技术分析

已知 uni-app 打包出的 app 实际上是一个套壳的网页,在 webview 外层套了一层 app 的壳子,而在 webview 上肯定是支持使用 h5 的那一套东西的。那我们就得想办法操控 webview,实际上 uni-app 官方提供了一个 webview 组件:


那么利用它我们肯定可以实现需求,但本文重点说的不是它,而是另一个有趣得东西——renderjs:

我们主要关注它的第二个主要作用:在视图层操作dom,运行 for webjs库,好,听起来不错,动手试试吧:

技术实现

页面结构和样式

这一块就比较简单,搞个容器用来展示地图

<template>
  <view>
    <view id="tmap-box"></view>
  </view>
</template>

<style lang="scss" scoped>
  #tmap-box {
    width: 668rpx;
    height: 900rpx;
  }
</style>

页面逻辑

<script>
  export default {
    data() {
      return {
      
      }
    }
  }
</script>
<script module="TMap" lang="renderjs">
  export default {
    data() {
      return {
        tMapInstance: null, // 天地图
      }
    },
    mounted() {
      const tmapScript = document.createElement('script')
      tmapScript.src = `https://api.tianditu.gov.***/api?v=4.0&tk=${'天地图密钥'}`
      tmapScript.type = "text/javascript"
      document.head.appendChild(tmapScript)
      tmapScript.onLoad = this.initMap.bind(this)
    },
    methods: {
      initMap() {
        this.tMapInstance = new T.Map('tmap-box')
        const lnglat = new T.LngLat(116.40969,39.89945)
        this.tMapInstance.centerAndZoom(lnglat,12)
      }
    }
  }
</script>

可以看到,我们搞了两个 script 标签,第一个就是我们平时页面的 script,我们姑且称为 “原始模块”,第二个我们加了 module="TMap" lang="renderjs" 这两个属性,而这个 script 就是 renderjs 的模块,命名为 “TMap模块”,而我们加载天地图的逻辑主要在 TMap 模块中。

效果

两个模块间通信

renderjs模块向原始模块发送信息

renderjs 模块任意位置可以调用 this.$ownerInstance.callMethod('methodName', data) 来向原始模块发送信息。

而在原始模块中需要提前定义函数 methodName

methods: {
  methodName(data) {
    // 接收到 renderjs 模块传过来的信息 data
  }
}

那我们可以在 renderjs 模块定义一个统一发送的函数,在原始模块中定义一个统一接收的函数:

// renderjs
methods: {
  sendMsg(msg, data) {
    this.$ownerInstance.callMethod('reciveMessage', {
      msg,
      data
    })
  }
}

// 原始模块
methods: {
  reciveMessage(msgObj) {
    console.log(msg.data)
    switch (msgObj.msg) {
      case 'a':
        // ....
        break
      case 'b':
        // ....
        break
    }
  }
}

原始模块向renderjs模块发送消息(renderjs模块监听原始模块数据变化)

其实原始模块没法主动向 renderjs 模块发送消息,而是 renderjs 模块可以选择监听原始模块某些数据的变化

绑定监听

以我之前的例子来说,假如原始模块的 data 中有这些数据:

data() {
  return {
    flag: false,
    str: '',
    num: 0,
    arr: [],
    obj: {
      a: false,
      b: '111',
      c: 0,
      d: [],
      e: {},
      f: null
    }
  }
}

那么在原始模块需要这样绑定:

<template>
  <view 
    :flag="flag"
    :change:flag="TMap.handleFlagChange"
    :str="str"
    :change:str="TMap.handleStrChange"
    :num="num"
    :change:num="TMap.handleNumChange"
    :arr="arr"
    :change:arr="TMap.handleArrChange"
    :obj="obj"
    :change:obj="TMap.handleObjChange"
  >
    <view id="tmap-box"></view>
  </view>
</template>

而在 TMap 模块中就需要定义对应的处理函数:

methods: {
  handleFlagChange(nV, oV) {
    // 页面刚加载的时候会触发一次,nV 打印 false,oV 打印 undefined
    if(oV !== undefined) {
      // 处理逻辑
    }
  },
  handleStrChange(nV, oV) {
    // 页面刚加载的时候会触发一次,nV 打印 '111',oV 打印 undefined
  },
  handleNumChange(nV, oV) {
    // 页面刚加载的时候会触发一次,nV 打印 0,oV 打印 undefined  
  },
  handleArrChange(nV, oV) {
    // 页面刚加载的时候会触发一次,nV 打印 [],oV 打印 undefined  
  },
  handleObjChange(nV, oV) {
    // 页面刚加载的时候会触发一次,nV 打印 { a: false, b: '111',  c: 0, d: [], e: {}, f: null },oV 打印 undefined  
  },
}

是不是有点向平时 vue 中使用的 watch ?,要注意的是,这些监听函数在页面刚加载的时候就会执行一次,打印的 nV 的值就是原始模块中绑定的初始值,oV 的值则都是 undefined,所以要判断后再处理。

注意

renderjs 模块和原始模块不能互相访问对方 data 中绑定的属性的,即:

<template>
  <view>
    <view id="tmap-box"></view>
  </view>
</template>

<script>
  export default {
    data() {
      return {
        flagA: false
      }
    },
    mounted() {
      console.log(this.flagB) // 报错
    }
  }
</script>
<script module="TMap" lang="renderjs">
  export default {
    data() {
      return {
        flagB: false
      }
    },
   mounted() {
      console.log(this.flagA) // 报错
   }
  }
</script>

而如果想在 renderjs 模块中直接访问原始模块中的数据,则需要像之前说的一样绑定属性和处理函数:

<template>
  <!-- 不能只绑定属性,一定要同时绑定处理函数 -->
  <view :flagA="flagA" :change:flagA="TMap.handleChangeFlagA">
    <view id="tmap-box"></view>
  </view>
</template>

<script>
  export default {
    data() {
      return {
        flagA: false
      }
    }
  }
</script>
<script module="TMap" lang="renderjs">
  export default {
   mounted() {
      console.log(this.flagA) // false
   },
   methods: {
     handleChangeFlagA(nV, oV) {
       // 不用做任何处理,但这个函数一定要有
     }
   }
  }
</script>

至于想在原始模块中访问 renderjs 模块中的数据,目前好像没有办法,也可能是我不知道。

总结

renderjs 使用起来还是很麻烦的,但是它可以使你的 app 可以使用 for web 的库,这个作用还是很大的,而它的语法和使用方式,我也了解的不太全面,大家文章中也可能有错误的和不全面的地方,大家有懂得欢迎评论区指正,谢谢。

转载请说明出处内容投诉
CSS教程_站长资源网 » 利用renderjs在app端加载for web库

发表评论

欢迎 访客 发表评论

一个令你着迷的主题!

查看演示 官网购买