定制小程序:全局修改tabbar,导航栏主题色,闪烁问题

背景

由于公司为合作伙伴推出定制小程序服务,小程序都是一套代码实现,而每个小程序都有自己的导航栏主题色,自己的tabBar。

实现过程

查看官方文档,发现可以通过api来更改导航栏颜色,tabBar颜色,图标(下面称为主题)。

导航栏

js 复制代码
wx.setNavigationBarColor({
  frontColor: '#ffffff',
  backgroundColor: '#ff0000'
})

tabBar

js 复制代码
wx.setTabBarStyle({
  color: '#FF0000',
  selectedColor: '#00FF00',
  backgroundColor: '#0000FF',
  borderStyle: 'white'
})

开始实现

想法与实现1

是在app初始化的时候根据appId调用以上api设置不同的主题,调式后发现只能更改当前页面主题,跳转到另一个页面还是保留了app.json的主题设置。只能抽离设置主题方法,每个页面都调用一次。

但是这种方式有个体验问题,进入小程序是会先展示app.json里面的配置主题,再变回需要设置的主题,中间存在闪烁过程,从一个页面跳转到另一个页面也存在这个闪烁过程。体验非常不好。

想法与实现2

通过自定义导航栏和tabBar实现;一顿倒腾时候发现也是存在闪烁问题,小程序从加载到读取到自定义配置需要时间。

社区

查看社区,发现已经有非常多开发者提出了(全局修改主题和闪烁问题),但从19年到现在,依然没用合适的解决方法,官方也没有解决。

建议setNavigationBarColor加一个参数,使其能全局生效?

官方来看看,提个需求,希望wx.setNavigationBarColor增加设置全局导航栏颜色?

wx.setNavigationBarColor为什么不能全局生效

自定义tabbar切换时会闪烁

转换实现思路

看起来在小程序运行时更改配置的实现方式都体验不佳(闪烁);既然小程序读取的是先读取的是app.json的配置,那么可以考虑先更改小程序的app.json再进行打包上传预览;把修改主题的时机从小程序加载提前到打包环节。

下面的实现以我前几天的文章为基础进行修改,可先阅读这个:

微信小程序自动化部署miniprogram-ci,一套代码一键上传多个小程序

主要目录结构

  • 新建appConfig目录,存放各个小程序的主题(app.json里的window和tabBar)
  • 新建setAppConfig.js文件, 用于打包前更改app.json文件。
  • ci.js文件:循环执行所有app的打包预览,上传脚本

appConfig下的json文件结构

取自app.json里的部分配置,根据自己的定制需求增减。

setAppConfig.js脚本

作用为更改app.json里的部分配置,打包上传前执行。

  • 获取所有小程序配置文件
  • 根据传入的appId找到当前的配置文件
  • 读取app.json文件和当前app的配置文件,将配置文件主题写入到当前app.json
js 复制代码
const fs = require('fs');
const path = require('path')
const projectPath = path.resolve(__dirname, '..')
// app.json路径
const appJsonFile = `${projectPath}/app.json`
// 获取所有小程序上传密钥文件
const appConfigList = fs.readdirSync(`${projectPath}/ci/appConfig`)
// setAppConfig 将app.json更改成当前app的主题配置
const setAppConfig = (appId) => {
  let err = null
  try {
    // 根据appId找到当前的配置文件
    let jsonFileName = appConfigList.find(item => item.includes(appId))
    const appConfigFile = `${projectPath}/ci/appConfig/${jsonFileName}`
    // 读取app.json文件和当前app的主题配置文件,将主题写入到当前app.json
    let appJsonData = JSON.parse(fs.readFileSync(appJsonFile, {encoding: 'utf-8'}))
    const {window, tabBar} = JSON.parse(fs.readFileSync(appConfigFile, {encoding: 'utf-8'}))
    appJsonData.window = window
    appJsonData.tabBar = tabBar
    fs.writeFileSync(appJsonFile, JSON.stringify(appJsonData, null, '\t'))
  } catch (err) {
    err = err
    console.log(err)
  }
  return err
}
module.exports = setAppConfig

调整ci.js 上传与预览脚本

文件由前面的批量预览和上传脚本更改而来;批量预览和上传参考:微信小程序自动化部署miniprogram-ci,一套代码一键上传多个小程序

  • 获取所有小程序上传密钥文件
  • 根据上传密钥文件列表,获取appId列表
  • app.json先更改为第一个app的配置,根据命令进行预览/上传
  • 待执行完成后,再执行appId列表下一个app,直到最后一个
js 复制代码
// 使用ci脚本上传,需在小程序后台:开发管理 =》 小程序代码上传,生成小程序代码密钥放到privateKey文件夹;配置上传机器的IP白名单;上传前先预览再上传 npm run preview => npm run upload
const fs = require('fs');
const ci = require('miniprogram-ci')
const path = require('path')
const projectPath = path.resolve(__dirname, '..')
// 更改app.json js
const setAppConfig = require(`${projectPath}/ci/setAppConfig.js`)
// 获取所有小程序上传密钥文件
const privateList = fs.readdirSync(`${projectPath}/ci/privateKey`)
// 处理命令
const command = process.argv.slice(2)[0]
// 当前处理的第几个小程序
let appIndex = 0
// appId列表
let appIdList = privateList.map(item => {
  return item.split('.')[1] // item: private.appId.key
})
const uploadInfo = {
  version: '5.0.9',
  desc: '功能优化与修复bug'
}
// 处理上传和预览
let commandHandler = async () => {
  let appId = appIdList[appIndex]
  // 创建项目对象
  const project = new ci.Project({
    appid: appId,    // 小程序appid
    type: 'miniProgram',  // 类型,小程序或小游戏
    projectPath: projectPath, // 项目路径
    privateKeyPath: `${projectPath}/ci/privateKey/${privateList[appIndex]}`,  // 密钥路径
    ignores: ['node_modules/**/*']  // 忽略的文件
  })
  // 预览和上传前设置app json文件
  setAppConfig(appIdList[appIndex])
  if (command === 'upload') {
    await ci.upload({
      project,
      ...uploadInfo,
      setting: {
        es6: true, // 对应小程序开发者工具的 "es6 转 es5"
        es7: true, // 对应小程序开发者工具的 "增强编译"
        minify: true  // 是否压缩代码
      },
      onProgressUpdate: getstate,
    })
  } else if (command === 'preview') {
    await ci.preview({
      project,
      desc: '功能优化', // 此备注将显示在"小程序助手"开发版列表中
      setting: {
        es6: true, // 对应小程序开发者工具的 "es6 转 es5"
        es7: true, // 对应小程序开发者工具的 "增强编译"
      },
      qrcodeFormat: 'image',
      qrcodeOutputDest: `${projectPath}/ci/qrcode/${appId}_qrcode.jpg`,
      onProgressUpdate: getstate,
    })
  }
  // 监听上传过程,如果上传完成延迟一下再上传下一个,避免配置串了
	function getstate(e) {
		if (e._status == "done" && e._msg == "upload") {
			console.log(`${appIdList[appIndex]}--上传完成`)
      // 继续下一个,直到最后一个
      if (appIndex < appIdList.length - 1) {
        setTimeout(() => {
          appIndex += 1
          commandHandler()
        }, 1000)
      }
		}
	}
}
commandHandler()
相关推荐
奈斯先生Vector5 小时前
告别工具碎片化:基于 Nano Banana 全模态 AI 聚合架构搭建“文本-图像-视频”自动化协同生产线
运维·数据库·人工智能·架构·自动化·aigc·音视频
猫猫不是喵喵.5 小时前
Vue3 Props 属性
前端·javascript·vue.js
jarvisuni10 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
小码哥哥11 小时前
如何评价“构建企业级 AI 知识库“这一趋势?从技术架构到落地实践的完整分析
人工智能·架构
破z晓12 小时前
javascript 导出excel表
开发语言·javascript·excel
小罗水13 小时前
第17章 后台管理端与演示支持
javascript·vue.js·ecmascript
Vince的修炼之路14 小时前
RAG 文档处理技术深度分析:PDF 解析、表格提取、OCR 识别全链路
人工智能·架构
万少14 小时前
DeepSeek-V4-Flash 正式版上线了,但这 3 个坑我帮你提前踩了
前端·javascript·后端
宁风NF15 小时前
JavaScript:网络请求与前端通信
开发语言·前端·javascript·网络·学习·ecmascript
吾儿良辰15 小时前
Flutter 迎来重大变革:Material 设计系统拆分,迈向更开放的 UI 框架
javascript·flutter·ui