uniapp renderjs实践

1,945 阅读2分钟

功能详解

  • 大幅降低逻辑层和视图层的通讯损耗,提供高性能视图交互能力 逻辑层和视图层分离有很多好处,但也有一个副作用是在造成了两层之间通信阻塞。尤其是小程序和App的Android端阻塞问题影响了高性能应用的制作。

  • renderjs运行在视图层,可以直接操作视图层的元素,避免通信折损。

  • 在视图层操作dom,运行for web的js库 官方不建议在uni-app里操作dom,但如果你不开发小程序,想使用一些操作了dom、window的库,其实可以使用renderjs来解决。 在app-vue环境下,视图层由webview渲染,而renderjs运行在视图层,自然可以操作dom和window。

注意事项

  • 可以使用 dom、bom API 不可直接访问逻辑层数据
  • 视图层和逻辑层通讯方式与 WXS 一致
  • 观测更新的数据在 view 层可以直接访问到
  • 不要直接引用大型类库,推荐通过动态创建 script 方式引用
  • view 层的页面引用资源的路径相对于根目录计算,例如:./static/test.js
  • 目前仅支持内联使用
  • prop不能传递函数,故如f2等用到函数传递的不能封装成公共组件
<view  :prop="serviceProp" :change:prop="echarts.updateEcharts" :id="id" class="echarts"></view>
/**
:prop  逻辑层传递给视图层数据,必须与 :change:prop 连用 
也可以:xx =""  :chang:xx="viewMethod"
主要作用是,当逻辑层数据xx发生改变后,视图层js做出响应 

*/

实践

app 选择本地文件,图片、视频、文档、等文件。

  • 通常解决方案都是下载收费的原生插件。而且有的还不能支持同时支持ios端。而有了renderjs就不用原生插件也能实现文件上传

android 有个吭

当运行环境targetSdkVersion版本设置过高时。华为低端手机上会获取不到文件路径。

<template>
	<view class="">
		<view :prop="option" :change:prop="inputfile.updateEcharts" @click="inputfile.onClick"><slot>文件上传</slot></view>
	</view>
</template>
<script>
export default {
	inheritAttrs: false,
	name: '',
	props: {},
	components: {},
	watch: {},
	created() {},
	computed: {},
	mounted() {},
	data() {
		return {
			option: {
				API: $globalConfig.baseUrl + `/upload/uploadFiles`,
				token: uni.getStorageSync('token') ? uni.getStorageSync('token') : ''
			}
		};
	},
	methods: {
		onViewClick(files) {
			this.$emit('files', files);
		},
		loading(status = true) {
			if (status) this.$utils.uni.showLoading('文件上传中……');
			else this.$utils.uni.hideLoading();
		},
		errorMsg(msg = 'msg') {
			this.$utils.uni.toast.error(msg);
		}
	}
};
</script>
<script module="inputfile" lang="renderjs">
import axiosConfig from './axiosConfig'
export default {
	mixins:[axiosConfig],
	data(){
		return {myOwnerInstance:null}
	},
	mounted() {
		this.createInputDom()
	},
	computed: {
		fileId() {
			return this.getUUID()
		}
	},
	methods: {
		createInputDom(){
			let _this=this
			let inputDom = document.createElement("input");
			 inputDom.setAttribute("type","file");
			 inputDom.setAttribute("id",this.fileId);
			 inputDom.setAttribute("hidden",true);
			 inputDom.setAttribute("multiple",'multiple');
			 // inputDom.setAttribute("capture",'capture');
			 inputDom.addEventListener('change',async function(e){
				 _this.myOwnerInstance.callMethod('loading')
				let files= await _this.uploadFile(inputDom.files)
				_this.myOwnerInstance.callMethod('loading',false)
				_this.myOwnerInstance.callMethod('onViewClick', files)
				//_this.excuteVmMethods('onViewClick',files)
			 })
			document.body.appendChild(inputDom);
		},
		async uploadFile(files){
			let _this=this
			let formData = new FormData()
			let keys=Object.keys(files)
			let fileList=keys.map(key=>{
				formData.append('files',files[key])
				return files[key]
			})
			try{
				return await axios({
					method: 'post',
					url:this.option.API,
					headers:{
						'Content-Type':'multipart/form-data'
					},
					data : formData
				})
			}catch(e){
				 _this.myOwnerInstance.callMethod('errorMsg',e.data?e.data.msg:'文件上传失败')
				return  []
			}
		},
		getUUID() {
			return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
				return (c === 'x' ? (Math.random() * 16) | 0 : 'r&0x3' | '0x8').toString(16);
			});
		},
		updateEcharts(newValue, oldValue, ownerInstance, instance) {
			console.log('updateEcharts');
		},
		onClick(event, ownerInstance) {
			//将服务层的实例控制对象
			this.myOwnerInstance=ownerInstance;
			document.getElementById(this.fileId).click()
		},
		//执行service 层中的方法 ,只适用与App
		excuteVmMethods(method,args){
			// #ifdef APP-VUE
			UniViewJSBridge.publishHandler('onWxsInvokeCallMethod', {
				cid: this._$id,
				//方法名
				method,
				//参数
				args
			  })
			// #endif
		}
	}
};
</script>
<style lang="scss" scoped></style>

直接用echarts 渲染图表

<template>
	<view class="content">
		<!-- #ifdef APP-PLUS || H5 -->
		<view :prop="viewProp" :change:prop="echarts.updateEcharts" :id="id" class="echarts" @click="echarts.onClick"></view>
		<!-- #endif -->
	</view>
</template>

<script>
export default {
	props: {
		option: {
			type: Object,
			required: true
		}
	},
	data() {
		return {
			id: this.getUUID()
		};
	},
	onLoad() {},
	computed: {
		viewProp() {
			return {
				option: this.option,
				id: this.id
			};
		}
	},
	methods: {
		getUUID() {
			return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
				return (c === 'x' ? (Math.random() * 16) | 0 : 'r&0x3' | '0x8').toString(16);
			});
		}
	}
};
</script>

<script module="echarts" lang="renderjs">
let myChart;
export default {
	mounted() {
		if (typeof window.echarts === 'function') {
			this.initEcharts();
		} else {
			// 动态引入较大类库避免影响页面展示
			const script = document.createElement('script');
			// view 层的页面运行在 www 根目录,其相对路径相对于 www 计算
			script.src = 'static/js/echarts.js';
			script.onload = this.initEcharts.bind(this);
			document.head.appendChild(script);
		}
	},
	methods: {
		initEcharts() {
			myChart = echarts.init(document.getElementById(this.viewProp.id));
			// 观测更新的数据在 view 层可以直接访问到
			myChart.setOption(this.viewProp.option);
		},
		updateEcharts(newValue, oldValue, ownerInstance, instance) {
			console.log(JSON.stringify('change'));
			// 监听 service 层数据变更
			myChart.setOption(newValue.option);
		},
		onClick(event, ownerInstance) {
				// 调用 service 层的方法
				ownerInstance.callMethod('onViewClick', {
					test: 'test'
				})
		},
		//执行service 层 中的方法
		excuteVmMethods(){
			UniViewJSBridge.publishHandler('onWxsInvokeCallMethod', {
			        cid: this._$id,
					//方法名
			        method:'onRenderJSEvent',
					//参数
			        args:{test:'test'}
			  })
		}
	}
};
</script>

<style scoped lang="scss">
.content {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	.echarts {
		width: 100%;
		height: 300px;
	}
}
</style>