uniapp 小程序自定义编译及自动化构建

897 阅读4分钟

uniapp支持编译到多端,如微信、抖音、支付宝等等,在日常开发中针对小程序关于域名、appid和一些各端自有的文件配置,往往要手动修改代码,或者是创建文件。

还有,要想生成一个小程序码,要打开编辑器切换分支,运行对应编译命令,然后打开模拟器生成预览码,颇为不便。

为了优化开发体验,增加开发效率,对项目进行自定义编译改造,和开发自动化构建平台。

自定义编译

脚本配置

在 package.json 设置自定义脚本

  "uni-app": {
    "scripts": {
      "wx-dev": {
        "title": "微信小程序-开发环境",
        "env": {
          "UNI_PLATFORM": "mp-weixin",
          "ENV_TYPE": "dev"
        } 
      }
    }
  }

会在编辑器生成对应的脚本,点击即可运行

image.png

设置config目录存储数据及脚本

  • config
    • env.js
    • script.js

config\env.js 根据不同端口和环境存储数据

export default {
    dev: {
        wxUrl: 'https://xxxxxx.dev.xxxxxx.com',
        ttUrl: 'https://xxxxxx.dev.xxxxxx.com/tt',
        ttAppid: 'xxxxxxxxx',
    },
    test: {
        wxUrl: 'https://xxxxxx.dev.xxxxxx.com',
        ttUrl: 'https://xxxxxx.dev.xxxxxx.com/tt',
        ttAppid: 'xxxxxxxxx',
    },
    ready: {
        wxUrl: 'https://xxxxxx.dev.xxxxxx.com',
        ttUrl: 'https://xxxxxx.dev.xxxxxx.com/tt',
        ttAppid: 'xxxxxxxxx',
    },
    prod: {
        wxUrl: 'https://xxxxxx.dev.xxxxxx.com',
        ttUrl: 'https://xxxxxx.dev.xxxxxx.com/tt',
        ttAppid: 'xxxxxxxxx',
    },
}

config\script.js 对不能通过环境变量处理及要手动生成的文件做处理 如抖音小程序的配置文件

// 生成抖音的配置文件,目前并不能在uniapp项目里面配置,需要手动生成
const fs = require('fs')
const path = require('path')

export function createPackage() {
  const dirPath = path.join(__dirname, '../dist/dev/mp-toutiao/app.js')
  const filePath = path.join(__dirname, '../dist/dev/mp-toutiao/package.json')

  function writeFile() {
    fs.writeFileSync(
      filePath,
      `{ "industrySDK": true, "ttPlugins": { "dependencies": { "microapp-trade-plugin": { "version": "1.1.2", "isDynamic": true } } } }`
    )
    console.error('--------------------生成package.json文件成功--------------------')
  }

  try {
    // 这里使用循环检测文件存在来判断是否编译完成
    const key = setInterval(() => {
      if (fs.existsSync(dirPath, fs.constants.F_OK)) {
        writeFile()
        clearInterval(key)
      }
    }, 1000)
  } catch (error) {
    console.error('生成package.json文件失败', error)
  }
}

vite.config.ts 文件注入全局变量和执行脚本

// 根据执行命令的类型获取变量,以及执行脚本
const ENV_TYPE = process.env?.UNI_CUSTOM_DEFINE ? (JSON.parse(process.env.UNI_CUSTOM_DEFINE!)).ENV_TYPE'prod'

//  生成抖音文件
if (process.env.UNI_PLATFORM === 'mp-toutiao') {
    createPackage()
}

// 注入全局变量
export default defineConfig(({ mode }) => ({
    define: {
        'process.env': process.env,
        'process.ENV_CONFIG': ENV_CONFIG,
        'process.ENV_TYPE': {type:ENV_TYPE},
    },
}));

全局变量使用

以接口域名为例

option.url = process.ENV_CONFIG[process.ENV_TYPE.type].wxUrl + option.url

自动化构建平台

目标设计:支持代码分支自由选择,支持环境变量选择(依赖上面的自定义构建)

要完成上述目标,有几个关键点

  1. 分支切换:使用 simple-git 库来执行命令
  2. 支持根据环境变量,进行自动化构建:依赖上述的自定义构建,和微信小程序的CLI包
  3. 前端页面及服务器开发:使用 nuxt 进行开发,有 VUE 的原生优势,也可以调用 node 能力

代码分支控制–git

引入simple-git 库,针对于各种场景的分支操作做封装,包括分支更新、切换

import simpleGit from "simple-git";
const projectPath = "../cd-mini-program";

export function gitFetch() {
  return new Promise((resolve, reject) => {
    try {
      const git = simpleGit(projectPath);
      git.fetch((err: any) => {
        if (err) {
          console.error(err);
          reject(err);
          return;
        }
        console.log("------更新分支成功-----");
        resolve("200");
      });
    } catch (error) {
      reject(error);
    }
  });
}

自动化构建

uniapp项目编译

对于 HBuilderX 创建的项目可以引入cli支持,确保可以执行命令来编译项目 预计设计引入 shelljs 库执行 npm 命令来打包 uniapp,运行时切换目录到小程序项目根目录下,执行命令。

微信小程序预览

引入 miniprogram-ci 库,这是微信小程序官方的库,支持编译、预览和上传等代码操作。 这里主要用到预览功能,以此为例。

 const project = new ci.Project({
    appid: "xxxx", // 小程序项目的 appid
    type: "miniProgram",
    projectPath: `${PATH}/dist/${buildType}/mp-weixin`, //  项目路径
    privateKeyPath: `xxxxx.key`, // 项目私钥
    ignores: [`node_modules/**/*`],
  });
  const previewResult = await ci.preview({
    project,
    setting: { // 支持的配置参数,与模拟器同理
      minifyJS: true,
      minifyWXML: true,
      minifyWXSS: true,
      minify: true,
    },
    bigPackageSizeSupport: true, // 非官方文档用法
    qrcodeFormat: "image",
    qrcodeOutputDest: `./${envType}.png`,
    useCOS: true, // 非官方文档用法
    onProgressUpdate: (data: any) => {
      // console.log('onProgressUpdate',data);
    },
  });

vite 插件支持环境化处理

使用 vite 处理 uniapp 环境化

// vite-plugin-uni-script.js
const fs = require('fs')
const path = require('path')

let env_config
const UNI_PLATFORM = process.env.UNI_PLATFORM // 运行平台
const ENV_TYPE = process.env.UNI_CUSTOM_DEFINE
    ? JSON.parse(process.env.UNI_CUSTOM_DEFINE).ENV_TYPE
    : 'prod' // 环境类型
const PLATFORM_MAP = {
    'mp-toutiao': '抖音',
    'mp-alipay': '支付宝',
    'mp-weixin': '微信',
}

// 修改 appid
function modifyAppId(platform, appId) {
    const filePath = path.join(__dirname, '../src/manifest.json')
    try {
        const fileContent = fs.readFileSync(filePath, 'utf-8')
        const obj = JSON.parse(fileContent)
        obj[platform]['appid'] = appId
        fs.writeFileSync(filePath, JSON.stringify(obj, null, 2)) // 格式化 JSON 输出
        console.info(
            `--------------------修改${PLATFORM_MAP[platform]} appid成功--------------------`
        )
    } catch (error) {
        console.error(`修改${PLATFORM_MAP[platform]} appid失败`, error)
    }
}

export default function uniScriptPlugin(resolvedConfig) {
    return {
        name: 'vite-plugin-uni-script',
        configResolved(config) {
            env_config = config.env_config
        },
        buildStart() {
            if (UNI_PLATFORM === 'mp-toutiao') {
                modifyAppId('mp-toutiao', env_config[ENV_TYPE].ttAppid)
            } else if (UNI_PLATFORM === 'mp-alipay') {
                modifyAppId('mp-alipay', env_config[ENV_TYPE].alAppId)
            }
        },
        writeBundle() {
            if (UNI_PLATFORM === 'mp-toutiao') {
                const filePath = path.join(__dirname, '../dist/dev/mp-toutiao/package.json')
                try {
                    const txt = JSON.stringify(
                        {
                            industrySDK: true,
                            ttPlugins: {
                                dependencies: {
                                    'microapp-trade-plugin': {
                                        version: '1.1.2',
                                        isDynamic: true,
                                    },
                                },
                            },
                        },
                        null,
                        2
                    ) // 格式化 JSON 输出
                    fs.writeFileSync(filePath, txt)
                    console.info('--------------------生成package.json文件成功--------------------')
                } catch (error) {
                    console.error('生成package.json文件失败', error)
                }
            } else if (UNI_PLATFORM === 'mp-alipay') {
                // ...
            }
        },
    }
}

// vite.config.ts
import uniScriptPlugin from './vite-plugin-uni-script'
export default defineConfig(({ mode }) => ({
	plugins: [uni(),vitePluginUniScript(ENV_CONFIG)],
	env_config: ENV_CONFIG,
}));

// ENV_CONFIG 定义
const ENV_CONFIG = {
    dev: {
        ttAppid: 'ttAppid',
        alUrl: 'alUrl',
    },
    prod: {
        ttAppid: 'ttAppid',
        alUrl: 'alUrl',
    }
}

求内推

广州/深圳-前端求内推