renderjs有什么用?聊聊uniapp中用renderjs的一些细节

 3790

本篇文章带大家了解一下renderjs,通过在uniapp中使用better-scroll,聊聊renderjs的一些细节,希望对大家有所帮助!


renderjs有什么用?聊聊uniapp中用renderjs的一些细节


包含内容:

使用renderjs在app端获取dom

renderjs和service层之间的通信

renderjs中如何接收到service层中的自定义id(重点,官方文档没有的)

一、renderjs

1.1 renderjs的概念

官方文档

运行在视图层的js,只支持app-vue和h5(简单来说就是开了另外一条线程)

1.2 renderjs的作用

大幅降低逻辑层和视图层的通讯损耗,提供高性能视图交互能力(减少通讯损耗提升性能,例如一些手势或canvas动画的场景)

在视图层操作dom,运行for web的js库(可以操作dom,意味着拥有window、document等这些全局变量,在app-vue的service层没有这些)

1.3 renderjs的使用

官方文档给出的示例 (内容较为详细,也可以看我接下来的简要概括)

在原先的script标签的同级新增一个script,设置lang=renderjsmodule=(值任意,相当于命名空间,之后会根据这个名字调用其中的方法)

新script标签内的结构和之前的几乎一致,有几点不同的需要注意:

生命周期不和uniapp相同,而是和vue相同,onLoad应该写成原生vue的created

官方文档好像说了renderjs中无法使用uni这个全局变量,具体哪个地方忘了。实测结果是:部分可以。例如uni.upx2px是可以用的,uni.request不可以,所以使用uni全局变量之前先输出看一下有没有

在template中使用一开始给renderjs的命名加.的方式调用其中的方法

  1. <template>
  2.   <view>
  3.     <button @tap="test.handleClick">点击</button>
  4.   </view>
  5. </template>
  6.  
  7. <script>
  8.   export default {
  9.     // 原先的script,这里被称为service层
  10.   }
  11. </script>
  12.  
  13. <script module="test">
  14.   export default {
  15.     data() {
  16.       return {}
  17.     },
  18.     methods: {
  19.       handleClick(event, ownerInstance) {
  20.         // event是事件对象
  21.         // ownerInstance和this.$ownerInstance是一样的,用来调用service层的方法
  22.         console.log('点击了按钮')
  23.       }
  24.     },
  25.     created() {
  26.       console.log('renderjs初始化完毕')
  27.     }
  28.   }
  29. </script>


二、renderjs和service层的通信

具体分为三部分:

在template中通过用户手动操作触发事件

在service层中调用方法

在renderjs中调用方法

从renderjs到service层:通过this.$ownerInstance.callMethod()方法可以调用service中的方法,第一个参数是方法名,第二个参数是传过去的参数

  1. <template>
  2.   <view>
  3.     <button @tap="test.onClick">点击</button>
  4.   </view>
  5. </template>
  6.  
  7. <script>
  8.   export default {
  9.     methods: {
  10.       acceptDataFromRenderjs(data) {
  11.         console.log('从renderjs中接收到的数据', data)
  12.       }
  13.     }
  14.   }
  15. </script>
  16.  
  17. <script module="test">
  18.   export default {
  19.     data() {
  20.       return {}
  21.     },
  22.     methods: {
  23.       onClick(event, ownerInstance) {
  24.         ownerInstance.callMethod('acceptDataFromRenderjs', { content: '测试文字' })
  25.         // 或this.$ownerInstance.callMethod('acceptDataFromRenderjs', { content: '测试文字' })
  26.         // 需要注意的是:只有通过在template中用户手动操作触发renderjs的方法参数是这两个:event, ownerInstance;
  27.         // 通过其他方法触发的函数参数不一样,后面会说
  28.       }
  29.     }
  30.   }
  31. </script>

从service层到renderjs:

这里就需要template了,首先在template中绑定一个service中定义的值,然后在同样的位置增加:change:(属性名)=(触发的方法)来实现通信。

简单来说就是service负责数据的更改,通过template监听数据的变化来通知renderjs

  1. <template>
  2.   <view>
  3.     // prop是个名字,可以随意改,注意:change:[name]这两个名字需要相同就行了
  4.     <text :prop="options" :change:prop="test.onChange">无内容</text>
  5.     <button @tap="changeOptionFn">点击修改options</button>
  6.   </view>
  7. </template>
  8.  
  9. <script>
  10.   export default {
  11.     data() {
  12.       return {
  13.         options: {
  14.           // 这里存放准备传递给renderjs的数据
  15.           token: null,
  16.           num: 1
  17.         }
  18.       }
  19.     },
  20.     methods: {
  21.       changeOptionFn() {
  22.         this.options = {
  23.           // 这个地方我用时间戳来修改token,用于触发change,实际需要传递的数据是num
  24.           token: Date.now(),
  25.           num: Math.random()
  26.         }
  27.       }
  28.     }
  29.   }
  30. </script>
  31.  
  32. <script module="test">
  33.   export default {
  34.     methods: {
  35.       onChange(newValue, oldValue, ownerInstance, instance) {
  36.         console.log('service层中的options发生变化')
  37.         console.log('新值', newValue)
  38.         console.log('旧值', oldValue)
  39.         // ownerInstance和this.$ownerInstance一样,可用来向service层通信
  40.         // instance和ownerInstance的区别是:
  41.         // instance.$el指向的是触发事件的那个节点;ownerInstance.$el指向当前vue文件中的根节点;
  42.         // instance的作用目前尚不明确,官方没有给出用法
  43.       }
  44.     }
  45.   }
  46. </script>

在上面的例子中,prop初次绑定到节点时,事件不会触发。

用户首先通过点击按钮触发changeOptionFn事件,函数中修改了this.options的值。

而在text节点监听到绑定值发生了改变就会触发test.onChange,从而实现service到renderjs的通信

上面的例子中有一点需要注意:在this.options中我定义了一个token属性,每次修改时都将最新的时间戳赋值给他,这样就保证了我每一次的点击都会使options发生改变。

如果没有这个token的话就会出现明明修改了options但是并未触发onChange的情况。

了解js基础的都知道,修改options是我是直接重新赋值的,改变了索引,所以即使我num值和原先的相同,他也应该做出改变(例如vue中的watch),但是事实并不是

所以可以推测出这个监听数据改变监听的是内部的属性值,只有属性的增删改才能触发回调。(如果一开始绑定的就是基础数据类型的话,直接修改就好了)

故,当绑定值使用对象的时候,在对象中增加一个每次都一定会变的值,即可保证事件的触发(如上例中的token

补充一点:进行过prop绑定的值,触发过一次监听事件之后,在renderjs中可以直接使用this.属性名的方式获取到

例如上面的代码中,options改变导致test.onChange触发一次之后,在renderjs中可以直接通过this.options获取到值


三、renderjs中如何接收到service层中的自定义id

3.1 在renderjs中使用better-scroll

做过app-vue开发的话应该知道在service层中没有document对象,无法获取dom节点。

所以引用一些外部js的时候,如果初始化的时候需要传入一个选择器的,那基本就断定用到了document对象获取节点。

这时候就需要用到renderjs了,首先看一个better-scroll的示例。根据官方给出的示例做一些修改,我们可以得到以下代码

  1. <template>
  2.   <view id="my-scroll">
  3.     <view><slot /></view>
  4.   </view>
  5. </template>
  6.  
  7. <script>
  8.   export default {}
  9. </script>
  10.  
  11. <script module="BScroll">
  12.   export default {
  13.     mounted() {
  14.       // 如果这个插件支持ESModule的话就不用这么写,直接import导入就好了
  15.       if (typeof window.BScroll == 'function') return this.initBScroll()
  16.       const script = document.createElement('script')
  17.       script.src = 'static/better-scroll.core.min.js'
  18.       script.onload = this.initBScroll
  19.       document.head.appendChild(script)
  20.     },
  21.     methods: {
  22.       initBScroll() {
  23.         this.bs = new BScroll(document.querySelector('#my-scroll'))
  24.       }
  25.     }
  26.   }
  27. </script>

3.2 better-scroll自定义id

重点来了,上面的例子中虽然实现了效果,但是也出现了一个问题:id是固定的。

如果我在同一页面中多次使用该组件,就会导致出现多个重复id,导致无法预料的错误。

在官方给出的示例中,包括我研究过的插件市场中的很多项目,都是使用固定的id。

解决的方法就是由外部传入自定义id或是由内部生成随机id。那么应该如何在renderjs中如何接收到service层中的自定义id呢

下面我给出的方法算是我自己测试过最有效的方法了,直接看代码

  1. <template>
  2.   <view :id="bsId" :prop="bsId" :change:prop="BScroll.initBScroll">
  3.     <view><slot /></view>
  4.   </view>
  5. </template>
  6.  
  7. <script>
  8.   export default {
  9.     props: {
  10.       bsId: {
  11.         type: String,
  12.         default: 'bs-container'
  13.       }
  14.     }
  15.   }
  16. </script>
  17.  
  18. <script module="BScroll">
  19.   export default {
  20.     mounted() {
  21.       if (typeof window.BScroll == 'function') return this.initBScroll()
  22.       const script = document.createElement('script')
  23.       script.src = 'static/better-scroll.core.min.js'
  24.       script.onload = this.initBScroll
  25.       document.head.appendChild(script)
  26.     },
  27.     methods: {
  28.       initBScroll() {
  29.         this.bs?.destroy()
  30.         this.bs = new BScroll(document.querySelector(`#${this.$ownerInstance.$vm.bsId}`))
  31.       }
  32.     }
  33.   }
  34. </script>

在父级中传入自定义的bsId,组件接收到之后将其作为元素id。

执行顺序和之前一样:在renderjs的mounted中加载外部js,加载完成后进行初始化操作,通过this.$ownerInstance.$vm.bsId获取到service层中的bsId完成操作

同时,bsId也绑定了prop,监听到改变时会重新进行初始化操作,所以在初始化的方法第一行加入了this.bs?.destroy(),如果实例已存在就先销毁。

还记得一开始就说过的renderjs只支持app-vue和h5吗,这里主要说的是app端,因为如果是h5端的话,是可以在service中直接使用document的,压根不用这么麻烦。

这里还有一点需要注意的::prop="bsId" :change:prop="BScroll.initBScroll"

实测,如果不写这行代码,也就是不进行绑定prop的操作的话,是无法获取到this.$ownerInstance.$vm.bsId。(app端是这样,h5端不写这个也可以,但是h5端压根也用不着这种方法)


TAG标签:
本文网址:https://www.zztuku.com/detail-10678.html
站长图库 - renderjs有什么用?聊聊uniapp中用renderjs的一些细节
申明:本文转载于《掘金社区》,如有侵犯,请 联系我们 删除。

评论(0)条

您还没有登录,请 登录 后发表评论!

提示:请勿发布广告垃圾评论,否则封号处理!!

    编辑推荐