江海入海,知识涌动,这是我参与江海计划的第1篇。
前言:
对于开发小程序的同学来说,能不能实现这样的一个小需求:如何动态替换小程序的axml、js的代码?很多人的第一反应是使用富文本/eval语法等方式实现,但是富文本只能做页面代码的显示,对于js的逻辑处理是做不到的,而官方已经严禁使用eval语法了。
那么我们导致能不能实现动态生成小程序axml代码及动态js逻辑呢?既然今天分享了这篇文章,说明是有办法实现的,别方,接着往下看。
知识点:
- template模板
- 万能跳转
方向:
小程序开发者应该都知道目前各个平台的小程序都是支持template模板的,那么我们今天就通过template模板实现该功能,众所周知,前端代码里常用的都是div、ul、li、span、img、表单等等标签,那么我们的方案就是:使用模板循环嵌套遍历出匹配的标签,然后动态添加到axml上。以下图片是青团社内部的配置平台(外部有比较多的低代码搭建平台可供大家使用),当保存后会生成一套前后端约定好的类HTMLString 节点树。
通过拖动配置生成如下的类HTML String的配置。
{ wxAppendData: [{ "node": "config", "pageName": "page003", "topBarTitle": "青团RPO" }, { "node": "element", "tag": "img", "src": "https://qiniu-image.qtshe.com/1554357586839_24.png" }, { "node": "element", "tag": "img", "src": "https://qiniu-image.qtshe.com/1554357609062_218.png" }, { "node": "element", "tag": "btn", "content": "按钮1", "style": "position: absolute; top: 551px; left: 49px; width: 74px; height: 20px; " }, { "node": "element", "tag": "btn", "content": "按钮2", "style": "position: absolute; top: 551px; left: 154px; width: 74px; height: 21px; " }, { "node": "element", "tag": "btn", "content": "按钮3", "style": "position: absolute; top: 551px; left: 257px; width: 74px; height: 21px; " }, { "node": "element", "tag": "link", "style": "position: absolute; top: 65px; left: 31px; width: 316px; height: 70px; ", "url": "http://www.baidu.com" }] }
上述是配置平台后生成的节点树代码,然后下一步就是小程序里需要实现模版的遍历出小程序中对应的节点标签。
// 简单举个例子,可扩展 div => view span => text img => image input type="button" => button
而对应小程序里模板内可一直嵌套,这样就可实现动态生成axml的代码了,以下代码只是简单的给大家提供思路:
<template name="Tmpl"> <block a:for="{{wxAppendData}}" a:key=""> <template is="Tmpl1" data="{{item}}" /> </block> </template> <template name="Tmpl1"> <block a:if="{{item.node == 'element'}}"> <block a:if="{{item.tag == 'div'}}"> <view style="{{item.style}}" data-param="{{item.param || '{}'}}" onTap="{{item.jump1 ? 'universalJump' : ''}}" data-ptpid="e895-1e11-9652-507e" class="ptp_exposure"> {{item.content}} <block a:for="{{item.child}}" a:key=""> <template is="Tmpl2" data="{{item}}" /> </block> </view> </block> <!-- img标签 --> <block a:if="{{item.tag == 'img'}}"> <image mode="widthFix" style="{{item.style}}" src="{{item.src}}" data-param="{{item.param || '{}'}}" onTap="{{item.jump1 ? 'universalJump' : ''}}" data-ptpid="bd91-1cee-bdf5-7a5f" class="ptp_exposure" /> </block> <!-- text标签 --> <block a:if="{{item.tag == 'span'}}"> <text style="{{item.style}}" data-param="{{item.param || '{}'}}" onTap="{{item.jump1 ? 'universalJump' : ''}}" data-ptpid="b0cd-1dd5-80a9-910d" class="ptp_exposure"> {{item.content}} <block a:for="{{item.child}}" a:key=""> <template is="Tmpl2" data="{{item}}" /> </block> </text> </block> <!-- button标签 --> <block a:if="{{item.tag == 'btn'}}"> <button hover-class="none" style="{{item.style}}" data-param="{{item.param || '{}'}}" onTap="{{item.jump1 ? 'universalJump' : ''}}" data-ptpid="385a-15a5-b4af-803e" class="ptp_exposure">{{item.content}}</button> </block> </block> </template> <template name="Tmpl2"> <block a:if="{{item.node == 'element'}}"> <block a:if="{{item.tag == 'div'}}"> <view style="{{item.style}}" data-param="{{item.param || '{}'}}" onTap="{{item.jump1 ? 'universalJump' : ''}}" data-ptpid="370d-1885-b971-b861" class="ptp_exposure"> {{item.content}} <block a:for="{{item.child}}" a:key=""> <template is="Tmpl3" data="{{item}}" /> </block> </view> </block> <!-- img标签 --> <block a:if="{{item.tag == 'img'}}"> <image mode="widthFix" style="{{item.style}}" src="{{item.src}}" data-param="{{item.param || '{}'}}" onTap="{{item.jump1 ? 'universalJump' : ''}}" data-ptpid="1ac7-1d7c-bc86-360c" class="ptp_exposure" /> </block> <!-- text标签 --> <block a:if="{{item.tag == 'span'}}"> <text style="{{item.style}}" data-param="{{item.param || '{}'}}" onTap="{{item.jump1 ? 'universalJump' : ''}}" data-ptpid="2d35-12ec-ab8b-8a6b" class="ptp_exposure"> {{item.content}} <block a:for="{{item.child}}" a:key=""> <template is="Tmpl3" data="{{item}}" /> </block> </text> </block> <!-- button标签 --> <block a:if="{{item.tag == 'btn'}}"> <button hover-class="none" style="{{item.style}}" data-param="{{item.param || '{}'}}" onTap="{{item.jump1 ? 'universalJump' : ''}}" data-ptpid="9b24-1589-9178-e140" class="ptp_exposure">{{item.content}}</button> </block> </block> </template> <template name="Tmpl3"> <block a:if="{{item.node == 'element'}}"> <block a:if="{{item.tag == 'div'}}"> <view style="{{item.style}}" data-param="{{item.param || '{}'}}" onTap="{{item.jump1 ? 'universalJump' : ''}}" data-ptpid="c059-14a4-a1e9-1f17" class="ptp_exposure"> {{item.content}} <block a:for="{{item.child}}" a:key=""> <template is="Tmpl4" data="{{item}}" /> </block> </view> </block> <!-- img标签 --> <block a:if="{{item.tag == 'img'}}"> <image mode="widthFix" style="{{item.style}}" src="{{item.src}}" data-param="{{item.param || '{}'}}" onTap="{{item.jump1 ? 'universalJump' : ''}}" data-ptpid="29f1-15b9-8713-07d5" class="ptp_exposure" /> </block> <!-- text标签 --> <block a:if="{{item.tag == 'span'}}"> <text style="{{item.style}}" data-param="{{item.param || '{}'}}" onTap="{{item.jump1 ? 'universalJump' : ''}}" data-ptpid="884b-17ac-83bd-b0c3" class="ptp_exposure"> {{item.content}} <block a:for="{{item.child}}" a:key=""> <template is="Tmpl4" data="{{item}}" /> </block> </text> </block> <!-- button标签 --> <block a:if="{{item.tag == 'btn'}}"> <button hover-class="none" style="{{item.style}}" data-param="{{item.param || '{}'}}" onTap="{{item.jump1 ? 'universalJump' : ''}}" data-ptpid="7c3e-199e-8a0a-8458" class="ptp_exposure">{{item.content}}</button> </block> </block> </template> <template name="Tmpl4"> <block a:if="{{item.node == 'element'}}"> <block a:if="{{item.tag == 'div'}}"> <view style="{{item.style}}" data-param="{{item.param || '{}'}}" onTap="{{item.jump1 ? 'universalJump' : ''}}" data-ptpid="c073-173b-87cb-b609" class="ptp_exposure"> {{item.content}} </view> </block> <!-- img标签 --> <block a:if="{{item.tag == 'img'}}"> <image mode="widthFix" style="{{item.style}}" src="{{item.src}}" data-param="{{item.param || '{}'}}" onTap="{{item.jump1 ? 'universalJump' : ''}}" data-ptpid="d79c-1cd9-a0e7-573c" class="ptp_exposure" /> </block> <!-- text标签 --> <block a:if="{{item.tag == 'span'}}"> <text style="{{item.style}}" data-param="{{item.param || '{}'}}" onTap="{{item.jump1 ? 'universalJump' : ''}}" data-ptpid="a239-19a2-8534-a489" class="ptp_exposure">{{item.content}}</text> </block> <!-- button标签 --> <block a:if="{{item.tag == 'btn'}}"> <button hover-class="none" style="{{item.style}}" data-param="{{item.param || '{}'}}" onTap="{{item.jump1 ? 'universalJump' : ''}}" data-ptpid="86a4-1e71-8c4e-fcf0" class="ptp_exposure">{{item.content}}</button> </block> </block> </template>
那么对应的小程序js是如何动态的呢?前面说了个功能叫万能跳转,从字面意思可以理解为点击后可跳转至任意地方。如何实现万能跳转?
前端可以写死在小程序本地(当然配置也可以服务端返回),跟服务端同学约定好jumpKey的值,封装个universalJump方法。
// 万能跳转解析,这里列举几个例子,大家可按需加配置 universalJump(config) { let { title = '', jumpKey = '', param = '', subTitle = '' } = config try { if (param !== '') { param = JSON.parse(param) } } catch (error) { console.log('param参数非json字符串') } // 从万能跳转参数中寻找相关的字段值 let findParamValue = (arr = [], key = 'targetUrl') => { let value = '' arr.map(item => { if (item.key === key) { value = item.value } }) return value } // jumpKey为服务端约定的跳转方法名 switch (jumpKey) { case "USER_BENEFIT_LIST": // 举个例子:跳转到公益列表 my.navigateTo({ url: '/sundry/volunteerTravelList/index' }) break // 静态页面 case "USER_STATIC_PAGE": my.navigateTo({ url: `/pages/webview/webview?targetUrl=${encodeURIComponent(targetUrl)}&shareTitle=${encodeURIComponent(title)}` }) break // 跳转三方小程序 case 'USER_THIRD_ZFB_APP_JUMP_PAGE': var appId = findParamValue(param, 'appId') var path = findParamValue(param, 'path') || '' my.navigateToMiniProgram({ appId, path: decodeURIComponent(path) }) break } }
总结:
以上为青团社目前实现的动态生成axml代码,及万能跳转配置的动态化,仅供外部开发者参考。