uniapp如何使用renderjs通信
说明:本文介绍uniapp如何使用renderjs通信:逻辑层和视图层通信,html模板中的组件如何通信,子组件更新数据父组件如何触发renderjs方法。
renderjs介绍
先介绍一下renderjs: renderjs
是一个运行在视图层的js。
官方介绍renderjs
的主要作用有2个:
大幅降低逻辑层和视图层的通讯损耗,提供高性能视图交互能力
在视图层操作dom,运行 for web 的 js库。
仅支持在
APP(vue页面)
、H5页面
使用。nvue无法使用。可以在renderjs使用mars3d或者openlayers库
解释一下:因为uniapp的逻辑层和视图层是分离的,所以我们无法在vue代码中获取到这个视图层的document。这种机制就造成了逻辑层和视图层的通信阻塞。renderjs
运行在视图层,直接就可以操作dom元素了。
renderjs基础使用方法
需要在script标签上命名一个module,直接在标签上使用。
lang=renderjs是必须要写的
renderjs里面是可以直接获取到document的
<template> <view> <button @click='test.fn'>按钮</button> </view> </template> <script module="test"> export default { methods: { fn() { console.log('我是fn'); console.log('doc', document) } }, } </script>
视图层和逻辑层
视图层:视图层是我们标记上renderjs的script标签
<script module="test"></script>
逻辑层: 是我们正常的script标签
<script></script>
视图层发消息给逻辑层
通过this.$ownerInstance.callMethod方法可以调用我们逻辑层里面的方法,也可以传参。
<template> <view> <button @click='test.fn'>按钮</button> </view> </template> // 逻辑层 <script> export default { methods: { receiveRenderjsFn(bool) { console.log('receiveRenderjsFn参数', bool); } }, } </script> // 视图层 <script module="test"> export default { methods: { fn() { this.$ownerInstance.callMethod('receiveRenderjsFn', true) } }, } </script>
逻辑层发消息给视图层
逻辑层如何发消息给视图层呢?答案是发不了。
那咋办,需求不做了是吧,容俺在想想。
答案是: 通过html模板通过绑定prop和change:prop,我们的prop可以绑定在逻辑层的data数据里面,change:prop绑定renderjs里面的方法,当prop发生变化后就会自动触发change:prop的方法,而这个方法就绑定在renderjs里面的函数,参数则是prop。
(emm,上面的说法确实有点绕,举个栗子吧)
我们在view这个标签绑定了prop和:change:prop, prop绑定在逻辑层,:change:prop的函数绑定在视图层。:prop='num'相当于监听了num(可以比作watch,而:change:prop可以比作watch里面执行的方法)
当我们点击按钮,触发add添加方法之后,num就+1,然后就会触发test里面的onChange方法
onChange方法里面有四个参数,新值、旧值、ownerInstance、instance
新值就是这个逻辑层的num
<template> <view> <view :prop='num' :change:prop='test.onChange'> {{num}} </view> <button @click='add'>按钮</button> </view> </template> // 逻辑层 <script> export default { data() { return { num: 1, } }, methods: { add() { this.num++ } } } </script> // 视图层 <script module="test"> export default { methods: { onChange(newValue, oldValue, ownerInstance, instance) { // ownerInstance和this.$ownerInstance一样,可用来向service层通信 // instance和ownerInstance的区别是 // instance.$el指向的是触发事件的那个节点 // ownerInstance.$el指向当前vue文件中的根节点 console.log('newValue',newValue); console.log('oldValue',oldValue); console.log('ownerInstance',ownerInstance); console.log('instance',instance); } }, } </script>
补充:不一定是写:prop和:change:prop,也可以是:test和:change:test, 只要名字相同就行了。
<view :test='obj' :change:test='test.onChange'> <childVue :prop.sync='obj'></childVue> </view>
某些情况下可能不会触发onChange,下面来介绍如何处理
当传输给子组件,参数是对象数组的时候,嵌套多层的结构时,无法监听到深层发生变化,就不会触发onChange。
index.vue
<template> <view> <childVue :prop.sync='obj' :change:prop='test.onChange'></childVue> </view> </template> <script> import childVue from './child.vue'; export default { components:{childVue}, data() { return { obj: { list: [ { name: '11', }, { name: '22', } ] } } }, } </script> <script module="test"> export default { methods: { onChange(newValue, oldValue, ownerInstance, instance) { console.log('newValue',newValue); console.log('oldValue',oldValue); console.log('ownerInstance',ownerInstance); console.log('instance',instance); } }, } </script>
child.vue
<template> <view> 子组件 <button @click='changeObj'>子组件按钮</button> </view> </template> <script> export default { props: { prop: { type: Object, } }, methods: { changeObj() { this.$emit('update:prop', { list: [ { name: '333', }, { name: '2442', } ] }) } } } </script> } } } </script>
上面的例子我们传入参数给子组件,然后为了在子组件更新参数给父组件,父组件接受到新的参数来触发onChange事件,但是确没有触发,因为嵌套了太多层,无法监听到,所以我们用另外的方法解决:
需要改变的部分
在父组件外面嵌套一层view标签,作为一个容器来让renderjs监听到变化,至于原因:可能是避免:prop这种写法,让子组件以为是传参数,而不是renderjs监听语法,所以我们嵌套一个外部容器view来作为监听
在child.vue组件更新时候,给对象添加一个当前时间,为了让renderjs监听到内容发生变化
index.vue
<template> <view :prop='obj' :change:prop='test.onChange'> <childVue :prop.sync='obj'></childVue> </view> </template>
child.vue
methods: { changeObj() { this.$emit('update:prop', { // 添加内容 now: Date.now(), list: [ { name: '333', }, { name: '2442', } ] }) } }
总体代码
index.vue
<template> <view :prop='obj' :change:prop='test.onChange'> <childVue :prop.sync='obj'></childVue> </view> </template> <script> import childVue from './child.vue'; export default { components:{childVue}, data() { return { obj: { list: [ { name: '11', }, { name: '22', } ] } } }, } </script> <script module="test"> export default { methods: { onChange(newValue, oldValue, ownerInstance, instance) { console.log('newValue',newValue); console.log('oldValue',oldValue); console.log('ownerInstance',ownerInstance); console.log('instance',instance); } }, } </script>
child.vue
<template> <view> 子组件 <button @click='changeObj'>子组件按钮</button> </view> </template> <script> export default { props: { prop: { type: Object, } }, methods: { changeObj() { this.$emit('update:prop', { now: Date.now(), list: [ { name: '333', }, { name: '2442', } ] }) } } } </script>