renderjs解读 您所在的位置:网站首页 echarts引用方式 renderjs解读

renderjs解读

2024-01-10 12:51| 来源: 网络整理| 查看: 265

1.前言

对于UNI APP端的开发而言,由于上并没有document,所以我们不能进行相关的DOM操作,同时有关DOM渲染的第三方库(echart、openlayer等)也无法有效的使用,因此官方推出了renderjs方案,来解决上述问题。

renderjs是一个运行在视图层的js,仅支持APP端和H5端,实际上H5端本身就支持DOM等,所以renderjs方案基本上只用于APP端的开发。

2.renderjs的细节解析

在这里我们以echarts为例子,进行组件的封装

根据官方提供的renderjs版echart例子,我们发现其示例的页面,有两个,其中一个设置了module和lang属性

... ... ...

然后在中通过动态引入js文件的方式,实现echarts.js的注入。

export default { mounted() { if (typeof window.echarts === 'function') { this.initEcharts() } else { // 动态引入较大类库避免影响页面展示 const script = document.createElement('script') // view 层的页面运行在 www 根目录,其相对路径相对于 www 计算 script.src = 'static/echarts.js' script.onload = this.initEcharts.bind(this) document.head.appendChild(script) } }, methods: { initEcharts() { myChart = echarts.init(document.getElementById('echarts')) // 观测更新的数据在 view 层可以直接访问到 myChart.setOption(this.option) }, updateEcharts(newValue, oldValue, ownerInstance, instance) { // 监听 service 层数据变更 myChart.setOption(newValue) }, onClick(event, ownerInstance) { // 调用 service 层的方法 ownerInstance.callMethod('onViewClick', { test: 'test' }) } } }

image.png

2.1 路径

官方的示例项目中写道:

view 层的页面运行在 www 根目录,其相对路径相对于 www 计算

我们来看下www目录的文件结构是什么样的,我们打开手机的文件资源管理器找到如下路径 Android/data/io.dcloud.HBuilder/apps/HBubilder,后会发现有如下两个文件夹

doc www

image.png

我们打开www文件夹,会发现如下文件

image.png

这些文件是不是很熟悉,其实在我们保存代码编译APP的时候,在/unpackage/dev/app-plus文件也是和我们在手机中的文件相互对应的

image.png

因此renderjs实际上是运行在app-view.js中的,因此在官方示例中的路径可以写为如下的类型

static/echarts.js ./static/echarts.js 但是不能写为/static/echarts.js 2.2 直接引用库文件

官方文档中提到

目前仅支持内联使用。 不要直接引用大型类库,推荐通过动态创建 script 方式引用。 那么我们尝试下如何直接引用类库

在项目的根目录创建一个文件名为libs,并将static里的echarts.js复制一份到该目录。

image.png

然后修改示例文档的js代码

let myChart import * as echarts from '@/libs/echarts.js' export default { mounted() { this.initEcharts() }, methods: { initEcharts() { myChart = echarts.init(document.getElementById('echarts')) // 观测更新的数据在 view 层可以直接访问到 myChart.setOption(this.option) }, updateEcharts(newValue, oldValue, ownerInstance, instance) { // 监听 service 层数据变更 myChart.setOption(newValue) }, onClick(event, ownerInstance) { // 调用 service 层的方法 ownerInstance.callMethod('onViewClick', { test: 'test' }) } } }

image.png

可以看到直接引入库文件也是支持的

2.3 生命周期及执行顺序

官方文档中提到

可以使用 vue 组件的生命周期不可以使用 App、Page 的生命周期

但是根据本人测试,renderjs不支持beforeCreate钩子,调用会报错,因此总结如下

钩子逻辑层视图层(renderjs)beforeCreate✔❌created✔✔beforeMount✔✔mounted✔✔beforeUpdate✔✔updated✔✔beforeDestroy✔✔destroyed✔✔

执行顺序如下

image.png

2.4 数据访问

官方文档提到

APP 端可以使用 dom、bom API,不可直接访问逻辑层数据,不可以使用 uni 相关接口(如:uni.request) 观测更新的数据在视图层可以直接访问到 H5 端逻辑层和视图层实际运行在同一个环境中,相当于使用 mixin 方式,可以直接访问逻辑层数据。

代码测试如下

export default { data() { return { name:"张三" }; } } export default { created() { console.log(this.name); }, mounted() { console.log(this.name); } }

结果如下

image.png

export default { name:"k-eChart", data() { return { name:"张三", age:12 }; } } export default { created() { console.log(this.name); }, mounted() { console.log(this.name); }, }

image.png

export default { name:"k-eChart", data() { return { name:"张三", age:12 }; } } export default { created() { console.log(this.name); }, mounted() { console.log(this.name); }, }

image.png

export default { name:"k-eChart", data() { return { name:"张三", age:12 }; } } export default { mounted() { console.log(this.name); console.log(this.age); }, }

image.png

因此我们可以得出结论

renderjs无法直接访问逻辑层的任何数据,只能访问通过显式传递给视图层的数据 renderjs不能直接在模板上直接绑定字符串,必须绑定逻辑层的数据,否则无法监听 通过绑定到视图的数据可以被renderjs访问,但是必须和change:参数名称成对出现才有效 script的module的名称可以随便取,但是change:参数名称必须和module保持一致,虽然不会阻断renderjs的运行,但是会报错,也会导致无法捕获数据的变化 模板传递的数据只能在视图层的mounted钩子之后访问 2.4.1 逻辑层传递的数据中包含函数对象

有同学肯定在renderjs的使用过程中遇到过对象的某个值是方法的时候,传递给逻辑层就变成了{}了,如下

export default { data() { return { option: { xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] }, yAxis: { type: 'value' }, series: [{ data: [150, 230, 224, 218, 135, 147, 260], type: 'line' }], tooltip: { formatter: function() { return "1" } } } }; }, } export default { mounted() { console.log(this.option); } }

image.png

这是因为逻辑层给视图层传递数据的时候,函数对象会自动进行一层处理(处理原理和原因未知),直接成为空对象。

解决办法

在逻辑层把方法写成字符串的形式,传递给视图层再用new Function()或者eval()的方式将其还原为方法对象

export default { data() { return { option: { xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] }, yAxis: { type: 'value' }, series: [{ data: [150, 230, 224, 218, 135, 147, 260], type: 'line' }], tooltip: { formatter: `function() { return "1" }` } } }; }, } export default { mounted() { this.option.formatter = new Function(this.option.formatter) //或者 this.option.formatter = eval(this.option.formatter) console.log(this.option); } }

image.png

3.实战:基于renderjs的echarts组件封装(部分) export default { name: "k-eChart", props:{ option:{ type:Object, required:true }, chartStyle:{ type:Object } }, data() { return { id:this.getGUID() }; }, methods:{ /** * @description 生成唯一的一个id */ getGUID() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); } } } import * as echarts from "echarts" let chartInstance export default { mounted() { chartInstance = echarts.init(document.getElementById(this.id)) this.sttringToFuncPrototype(this.option) chartInstance.setOption(this.option) }, methods:{ /** * @description 将字符串函数转换为函数 * @param {Object} option */ sttringToFuncPrototype(option){ for (const key in option) { let prototype = option[key] if(typeof prototype === "object") { //如果属性值是数组 //遍历数组 if(Array.isArray(prototype)) { prototype.forEach(item=>{ if(typeof item === "object") this.sttringToFuncPrototype(item) }) } //遍历此属性值 else this.sttringToFuncPrototype(prototype) } //转换string为function if(typeof prototype === "string" && prototype.includes("function")) prototype = eval(`(${prototype})`) } }, } } .chart { height: 400px; width: 100%; }

调用

const colors = ['#5470C6', '#91CC75', '#EE6666']; export default { data() { return { option: { xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] }, yAxis: { type: 'value' }, series: [{ data: [150, 230, 224, 218, 135, 147, 260], type: 'line' }], tooltip: { formatter: `function() { return "1" }` } }, option2: { color: colors, tooltip: { trigger: 'axis', axisPointer: { type: 'cross' } }, grid: { right: '20%' }, toolbox: { feature: { dataView: { show: true, readOnly: false }, restore: { show: true }, saveAsImage: { show: true } } }, legend: { data: ['Evaporation', 'Precipitation', 'Temperature'] }, xAxis: [{ type: 'category', axisTick: { alignWithLabel: true }, // prettier-ignore data: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] }], yAxis: [{ type: 'value', name: 'Evaporation', position: 'right', alignTicks: true, axisLine: { show: true, lineStyle: { color: colors[0] } }, axisLabel: { formatter: '{value} ml' } }, { type: 'value', name: 'Precipitation', position: 'right', alignTicks: true, offset: 80, axisLine: { show: true, lineStyle: { color: colors[1] } }, axisLabel: { formatter: '{value} ml' } }, { type: 'value', name: '温度', position: 'left', alignTicks: true, axisLine: { show: true, lineStyle: { color: colors[2] } }, axisLabel: { formatter: '{value} °C' } } ], series: [{ name: 'Evaporation', type: 'bar', data: [ 2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3 ] }, { name: 'Precipitation', type: 'bar', yAxisIndex: 1, data: [ 2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0, 2.3 ] }, { name: 'Temperature', type: 'line', yAxisIndex: 2, data: [2.0, 2.2, 3.3, 4.5, 6.3, 10.2, 20.3, 23.4, 23.0, 16.5, 12.0, 6.2] } ] } } }, }

效果如下

image.png

参考文献 renderjs有什么用?聊聊uniapp中用renderjs的一些细节 renderjs


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有