【经验分享】如何实现小程序代码热更新| 江海计划

简介: 【经验分享】如何实现小程序代码热更新| 江海计划

江海入海,知识涌动,这是我参与江海计划的第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代码,及万能跳转配置的动态化,仅供外部开发者参考。

目录
相关文章
|
2月前
|
API Nacos
【想进大厂还不会阅读源码】ShenYu源码-重构同步数据服务
ShenYu源码阅读📚。我们看下PR的标题和Concersation的头一句,大概意思就是重构注册中心数据同步到ShenYu网关的方式。大家看看重构了有没好处呢?不仅获得了知识,还获得了一次开源贡献,何乐而不为呢
52 3
|
5月前
|
测试技术 Linux Go
后端实践--单元测试与性能优化 青训营
后端实践--单元测试与性能优化 青训营
|
8月前
|
前端开发
前端学习笔记202305学习笔记第二十三天-重构和代码迁移1
前端学习笔记202305学习笔记第二十三天-重构和代码迁移1
30 0
|
8月前
|
前端开发
前端学习笔记202305学习笔记第二十三天-重构项目依赖安装
前端学习笔记202305学习笔记第二十三天-重构项目依赖安装
40 0
|
移动开发 JSON 小程序
【小程序开篇】小程序架构和配置
【小程序开篇】小程序架构和配置
245 0
【小程序开篇】小程序架构和配置
|
JavaScript 算法 前端开发
建议收藏,使用WebAssembly保护前端JS核心代码实战
上个月我花了2天开发了一个全新的VSCode插件叫“我爱掘金”,让所有掘友可以化身为小蝌蚪,在VSCode里实时聊天。使用的是一个开源项目 workerman-todpole ,在原项目的基础上我做了大量的修改和优化。也曾试图添加一些房管功能,比如只有管理员可以使用大红色,只有管理员可以发光等等。
502 0
建议收藏,使用WebAssembly保护前端JS核心代码实战
|
Arthas 监控 IDE
手把手教你实现热更新功能,带你了解 Arthas 热更新背后的原理
一天下午正在摸鱼的时候,测试小姐姐走了过来求助,说是需要改动测试环境 mock 应用。但是这个应用一时半会又找不到源代码存在何处。但是测试小姐姐的活还是一定要帮,突然想起了 Arthas 可以热更新应用代码,按照网上的步骤,反编译应用代码,加上需要改动的逻辑,最后热更新成功。对此,测试小姐姐很满意,并表示下次会少提 Bug。 嘿嘿,以前一直对热更新背后原理很好奇,借着这个机会,研究一下热更新的原理。
手把手教你实现热更新功能,带你了解 Arthas 热更新背后的原理
|
Java 编译器 开发工具
程序的执行流程和开发工具介绍 - 第五课
程序的执行流程和开发工具介绍 - 第五课
137 0
程序的执行流程和开发工具介绍 - 第五课
比心源码如何开发,这个功能要会实现
说到比心源码,不如讲讲比心源码实现聊天室功能该怎么做?
比心源码如何开发,这个功能要会实现
|
开发框架 小程序 IDE
mPaaS 小程序架构解析 | 实操演示小程序如何实现多端开发
mPaaS 小程序开发框架已全面开放,免费接入,欢迎体验。
3220 0
mPaaS 小程序架构解析 | 实操演示小程序如何实现多端开发