微信原生小程序的一次gulp构建
目录
- 一、项目目录介绍
- 源码目录
- 构建目录
- 二、目录文件介绍
- 2.1 build/appConfig
- 2.2 build/pageUrlConfig
- 2.3 build/env.config.json
- 2.4 build/npm/package.json
- 2.5 .gitignore
- 2.6 package.json
- 三、构建文件Gulpfile介绍
- 注意事项:
- 四、其他
看到标题大家应该很奇怪,原生小程序,直接放在微信开发者工具里面,开发就行了呗,做啥还要再构建一下呢?其实主要原因是想配置通过命令行直接切换环境,根据环境量,配置不同的配置文件,不同的页面入口,不同的tabbar等等,而不是每次手动切换手动修改。这里介绍了如何用gulp一次构建,直接用微信开发者工具就可以预览,不需要再手动切换配置,不需要手动安装依赖、手动npm构建的方式。
一、项目目录介绍
源码目录
├── build
│ ├── appConfig
│ ├── npm
│ ├── pageUrlConfig
│ └── env.config.json
├── dist
├── miniprogram
├── node_modules
├── typings
├── .gitignore
├── Gulpfile.js
├── package-lock.json
├── package.json
├── private.wx.config160dd2fa…
├── project.config.json
├── project.private.config.json
└── tsconfig.json
- build:用于存放构建相关的配置文件,是前端工程化的重要部分,里面的子目录和文件会在项目构建过程中发挥作用,以生成符合小程序要求的代码或资源。
- appConfig:存放微信小程序app.json文件,这里可以根据环境量选择不同的app.json。
- npm:存放 微信小程序用到的package.json文件,里面不包含构建用的包,只包含小程序代码用到的文件。
- pageUrlConfig:这个目录用于存放小程序路由map文件
{key:路径}
,这个文件不是必须的,但有了它,便于管理页面跳转地址。 - env.config.json:环境配置文件,可根据不同的运行环境(如开发环境、生产环境)配置不同的参数,像接口地址、调试开关等,实现项目在不同环境下的灵活切换。
- dist:构建完成后生成的最终输出目录,里面包含了可直接用于微信小程序运行的代码、资源等文件,是项目发布或调试的目标产物存放地。
- miniprogram:微信小程序的核心源码目录,小程序的页面、组件、逻辑等主要代码都放置在此目录下,是小程序功能实现的关键所在。
- node_modules:通过 npm 安装的项目依赖包都存放在这里,这些依赖为项目提供了各种功能支持,比如第三方库、工具等。
- typings:用于存放 TypeScript 的类型定义文件,当项目使用 TypeScript 开发时,这些文件能为代码提供类型检查和智能提示,提升开发体验和代码质量。
- .gitignore:Git 版本控制的忽略文件配置,指定了哪些文件或目录不需要被 Git 跟踪和提交,通常包括编译生成的文件、依赖目录等,避免将不必要的文件纳入版本管理。
- Gulpfile.js:Gulp 任务配置文件,Gulp 是前端自动化构建工具,通过此文件可以定义一系列构建任务,如代码压缩、文件复制、资源编译等,实现项目的自动化构建流程。
- package.json:项目的配置文件,包含了项目的基本信息(如名称、版本)、依赖包列表、脚本命令等,是 npm 管理项目的核心文件。
- private.wx.config160dd2fa…:微信小程序的私钥文件,这个文件不一定要在项目里,可以存放在你觉得安全的地方,文件来自于微信公众平台–开发管理–小程序代码上传密钥, 这个miniprogram初始化的时候必须有。
- project.config.json:微信小程序项目的配置文件,用于配置小程序的项目名称、appid、编译设置等项目级别的信息,微信开发者工具会根据此文件来识别和管理项目。
- project.private.config.json:微信小程序的私有项目配置文件,与 project.config.json 类似,但更侧重于存放一些不希望公开的私有配置内容。
构建目录
二、目录文件介绍
2.1 build/appConfig
- index.json 配置索引文件
{"customer1": "customer1_app.json" // 环境:配置文件名
}
- default_app.json 默认的小程序app.json文件配置
- customer1_app.json 环境量customer1对应的app.json配置文件,当环境量是customer1时,选取此文件作为app.json文件
2.2 build/pageUrlConfig
- index.json配置索引文件
{"customer1": "customer1_pageUrl.ts" // 环境:配置文件名
}
- default_pageUrl.ts 默认的小程序pageUrl.ts文件配置
export default {HOME: '/pages/index/index',LOG: '/pages/logs/logs'
}
- customer1_pageUrl.ts环境量customer1对应的pageUrl.ts配置文件,当环境量是customer1时,选取此文件作为pageUrl.ts文件
2.3 build/env.config.json
环境量配置文件
{"PAGE_TITLE": "测试项目","env": {"customer1": {"dev": {"API_URL": "http://test.com.cn/customer1"},"prod": {"API_URL": "http://prod.com.cn/customer1"}},"customer2": {"dev": {"API_URL": "http://test.com.cn/customer2"},"prod": {"API_URL": "http://prod.com.cn/customer2"}}}
}
环境量customer1对应的dev环境量就是
{"API_URL": "http://test.com.cn/customer1"
}
2.4 build/npm/package.json
{"name": "miniprogram-ts-less-quickstart","version": "1.0.0","description": "","scripts": {},"keywords": [],"author": "","license": "","dependencies": {"dayjs": "^1.11.13" // 小程序需要的依赖},"devDependencies": {"miniprogram-api-typings": "^2.8.3-1"}
}
2.5 .gitignore
node_modules
dist
2.6 package.json
{"name": "miniprogram-ts-less-quickstart","version": "1.0.0","description": "","scripts": {"clean": "gulp clean --env=dev --name","dev": "gulp dev --env=dev --name", // 这里可以 npm run dev customer1"build": "gulp build --env=prod --name"},"keywords": [],"author": "","license": "","dependencies": {},"devDependencies": { // 构建需要内容"@babel/core": "^7.28.0","@babel/preset-env": "^7.28.0","chalk": "^4.1.2","del": "^6.1.1","eslint": "^8.57.1","gulp": "^4.0.0","gulp-babel": "^8.0.0","gulp-changed": "^4.0.1","gulp-clean-css": "^4.0.0","gulp-cli": "^2.3.0","gulp-data": "^1.3.1","gulp-debug": "^4.0.0","gulp-ignore": "^2.0.2","gulp-imagemin": "^7.1.0","gulp-rename": "^1.4.0","gulp-sass": "^6.0.1","gulp-strip-debug": "^4.0.0","gulp-tap": "^2.0.0","gulp-typescript": "^6.0.0-alpha.1","gulp-wechat-weapp-src-alisa": "^1.0.4","imagemin-mozjpeg": "^10.0.0","imagemin-optipng": "^8.0.0","imagemin-svgo": "^11.0.1","miniprogram-api-typings": "^2.8.3-1","miniprogram-ci": "^2.1.14","sass": "^1.90.0","typescript": "^5.9.2","yargs": "^17.7.2"}
}
三、构建文件Gulpfile介绍
// 从 gulp 中解构出常用的任务流控制和文件操作方法
const { series, parallel, src, dest, watch } = require('gulp');
// 用于删除文件或目录的库
const del = require('del');
// 文件系统操作模块
const fs = require('fs');
// 路径处理模块
const path = require('path');
// 用于美化控制台输出的库
const chalk = require('chalk');
// 用于解析命令行参数的库
const yargs = require('yargs/yargs');
const { hideBin } = require('yargs/helpers');
// sass 相关编译库
const sass = require('sass');
const gulpSass = require('gulp-sass')(sass);
// 用于压缩 CSS 的库
const cleanCss = require('gulp-clean-css');
// babel 相关,用于 JavaScript 代码转译
const babel = require('gulp-babel');
// 用于移除代码中的调试语句(如 console、debugger)
const stripDebug = require('gulp-strip-debug');
// 用于重命名文件
const rename = require('gulp-rename');
// 用于只处理变更文件,提高构建效率
const changed = require('gulp-changed');
// 微信小程序源码别名处理插件
const alias = require('gulp-wechat-weapp-src-alisa');
// 用于在流中对文件进行自定义操作
const tap = require('gulp-tap');
// 用于执行 shell 命令
const { execSync } = require('child_process');
// 用于图片压缩的库
const imagemin = require('gulp-imagemin');
// 声明图片压缩相关的 ES 模块变量,后续动态导入
let imageminMozjpeg;
let imageminOptipng;
let imageminSvgo;// 异步加载图片压缩相关的 ES 模块
async function loadImageMinPlugins() {imageminMozjpeg = (await import('imagemin-mozjpeg')).default;imageminOptipng = (await import('imagemin-optipng')).default;imageminSvgo = (await import('imagemin-svgo')).default;
}// ============================ 1. 配置区(集中管理路径、别名等) ============================
const paths = {src: './miniprogram', // 小程序源码目录dist: './dist', // 构建输出根目录npmDist: './dist/miniprogram_npm', // 小程序 npm 构建输出目录distSrc: './dist/miniprogram', // 构建后小程序源码输出目录envConfig: './build/env.config.json', // 环境配置文件路径appJsonConfig: './build/appConfig', // 小程序 app.json 相关配置目录pageUrlConfig: './build/pageUrlConfig', // 页面 URL 配置目录configOutput: './dist/miniprogram/utils', // 配置文件输出目录imageIndexOutput: './dist/miniprogram/images/index.ts', // 图片映射文件输出路径buildNpmDir: './build/npm', // build/npm 目录,用于 npm 相关构建patterns: {npmPackage: './build/npm/package.json', // npm 包配置文件路径images: ['./miniprogram/**/*.{png,jpg,jpeg,gif,ico,svg}'], // 图片文件匹配模式wxml: ['./miniprogram/**/*.wxml'], // wxml 文件匹配模式wxss: ['./miniprogram/**/*.wxss'], // wxss 文件匹配模式scss: ['./miniprogram/**/*.scss'], // scss 文件匹配模式ts: ['./miniprogram/**/*.ts'], // TypeScript 文件匹配模式wxs: ['./miniprogram/**/*.wxs'], // wxs 文件匹配模式json: ['./miniprogram/**/*.json'], // json 文件匹配模式typings: ['./typings/', './typings/**'], // 类型定义文件匹配模式projectConfig: ['project.config.json', 'project.private.config.json', 'tsconfig.json'], // 项目配置文件列表},
};// 小程序源码别名配置,用于简化模块引用路径
const aliasConfig = {'@Utils': path.join(paths.src, 'utils'),'@pages': path.join(paths.src, 'pages'),'@images': path.join(paths.src, 'images'),'@typings': './typings',
};// ============================ 2. 工具函数区(可复用逻辑) ============================
/** 解析命令行参数(env、name),获取环境类型和环境标识 */
const getParams = () => {return yargs(hideBin(process.argv)).option('env', { alias: 'e', default: 'dev', describe: '环境类型(dev/prod)' }).option('name', { alias: 'n', required: true, describe: '环境标识(如customer1)' }).parse();
};/** 日志工具(美化输出),封装不同类型的日志输出方法 */
const logger = {info: (msg) => console.log(chalk.blue(`[INFO] ${msg}`)),success: (msg) => console.log(chalk.green(`[SUCCESS] ${msg}`)),error: (msg) => console.log(chalk.red(`[ERROR] ${msg}`)),warn: (msg) => console.log(chalk.yellow(`[WARN] ${msg}`)),task: (name) => console.log(chalk.cyan(`[TASK] 开始执行:${name}`)),
};/** 创建带别名+缓存的公共流,返回一个包含 src 方法的对象,方便后续文件处理流的创建 */
const createCommonPipe = (destPath, ext = '') => {return {src: (globPath, options) => {return src(globPath, options).pipe(alias(aliasConfig)) // 应用别名配置.pipe(changed(destPath, ext ? { extension: ext } : {})); // 只处理变更过的文件}};
};/** 检查文件是否存在(封装错误处理),返回文件是否存在的布尔值 */
const fileExists = (filePath) => {try {return fs.existsSync(filePath);} catch (err) {logger.error(`检查文件失败:${filePath},错误:${err.message}`);return false;}
};// ============================ 3. 环境配置处理(独立逻辑块) ============================
/** 解析环境配置(支持多环境多客户),将配置转换为便于使用的格式并返回 */
const parseConfig = (config) => {const { env, name } = getParams();if (!config.env?.[name]?.[env]) {logger.error(`环境配置缺失:env.${name}.${env}`);process.exit(1);}return Object.entries(config).reduce((prev, [key, value]) => {if (key === 'env') {Object.entries(config[key][name][env]).forEach(([k, v]) => {prev[k.toUpperCase()] = v;});} else {prev[key.toUpperCase()] = value;}return prev;}, { ENV: env, CUSTOMER_ENV: name });
};/** 生成环境配置文件(如 config.ts),从环境配置文件读取并处理后生成对应的 TypeScript 配置文件 */
const generateEnvConfig = () => {logger.task('generateEnvConfig');if (!fileExists(paths.envConfig)) {logger.error(`环境配置文件不存在:${paths.envConfig}`);process.exit(1);}return src(paths.envConfig).pipe(tap((file) => {try {const conf = JSON.parse(file.contents.toString());const config = parseConfig(conf);file.contents = Buffer.from('// 自动生成,请勿修改\n' +`export default ${JSON.stringify(config, null, 2)};`);} catch (err) {logger.error(`解析环境配置失败:${err.message}`);process.exit(1);}})).pipe(rename({ basename: 'config', extname: '.ts' })).pipe(dest(paths.configOutput)).on('end', () => logger.success('环境配置文件生成完成'));
};/** 从配置文件提取目标路径(通用逻辑),异步获取指定配置对应的文件路径 */
const getFilePathByConfig = async (defaultFile, basePath, sourceFile) => {return new Promise((resolve, reject) => {const { name } = getParams();fs.readFile(`${basePath}/${sourceFile}`, 'utf8', (err, data) => {if (err) reject(new Error(`读取文件失败: ${err.message}`));else {const config = JSON.parse(data);const filePath = config[name] || defaultFile;resolve(`${basePath}/${filePath}`);}});});
};// 生成 app.json(独立任务),根据配置生成小程序的 app.json 文件
const generateAppJson = async () => {logger.task('generateAppJson');if (!fileExists(`${paths.appJsonConfig}/index.json`)) {logger.error(`配置文件不存在:${paths.appJsonConfig}`);process.exit(1);}return src(await getFilePathByConfig('default_app.json', paths.appJsonConfig, 'index.json')).pipe(rename({ basename: 'app', extname: '.json' })).pipe(dest(paths.distSrc)).on('end', () => logger.success('app.json生成完成'));
};// 生成 pageUrl.ts(独立任务),根据配置生成页面 URL 相关的 TypeScript 文件
const generatePageUrl = async () => {logger.task('generatePageUrl');if (!fileExists(`${paths.pageUrlConfig}/index.json`)) {logger.error(`配置文件不存在:${paths.pageUrlConfig}`);process.exit(1);}return src(await getFilePathByConfig('default_pageUrl.js', paths.pageUrlConfig, 'index.json')).pipe(rename({ basename: 'pageUrl', extname: '.ts' })).pipe(dest(paths.configOutput)).on('end', () => logger.success('pageUrl.ts生成完成'));
};// ============================ 4. 构建任务区(按类型拆分) ============================
/** 清理构建目录,删除之前的构建产物 */
const clean = () => {logger.task('clean');return del([`${paths.dist}/**/*`, `!${paths.dist}`], { force: true }).then(() => logger.success('清理完成'));
};// WXML 编译,将小程序的 WXML 文件复制到构建目录
const compileWxml = () => {logger.task('compileWxml');return src(paths.patterns.wxml, { base: paths.src }).pipe(changed(paths.distSrc)).pipe(dest(paths.distSrc)).on('end', () => logger.success('WXML编译完成'));
};// SCSS 编译(含错误处理),将 SCSS 文件编译为 WXSS 并压缩
const compileScss = () => {logger.task('compileScss');return createCommonPipe(paths.distSrc, '.wxss').src(paths.patterns.scss, { base: paths.src }).pipe(gulpSass()).pipe(cleanCss()).pipe(rename({ extname: '.wxss' })).pipe(dest(paths.distSrc)).on('end', () => logger.success('SCSS编译完成'));
};// WXSS 编译,将 WXSS 文件复制到构建目录
const compileWxss = () => {logger.task('compileWxss');return src(paths.patterns.wxss, { base: paths.src }).pipe(changed(paths.distSrc)).pipe(dest(paths.distSrc)).on('end', () => logger.success('WXSS编译完成'));
};// TS/JS 编译(区分环境),根据环境决定是否转译和移除调试语句
const compileTs = () => {logger.task('compileTs');const { env } = getParams();let stream = createCommonPipe(paths.distSrc).src(paths.patterns.ts, {});if (env === 'prod') {stream = stream.pipe(babel({ presets: ['@babel/preset-env'] })).pipe(stripDebug());}return stream.pipe(dest(paths.distSrc)).on('end', () => logger.success('JS编译完成'));
};// WXS 编译,将 WXS 文件转译后复制到构建目录
const compileWxs = () => {logger.task('compileWxs');return createCommonPipe(paths.distSrc).src(paths.patterns.wxs, { base: paths.src }).pipe(babel({ presets: ['@babel/preset-env'] })).pipe(rename({ extname: '.wxs' })).pipe(dest(paths.distSrc)).on('end', () => logger.success('WXS编译完成'));
};// JSON 编译,将 JSON 文件复制到构建目录
const compileJson = () => {logger.task('compileJson');return src(paths.patterns.json).pipe(changed(paths.distSrc)).pipe(dest(paths.distSrc)).on('end', () => logger.success('JSON编译完成'));
};// 图片处理(含压缩和映射生成),压缩图片并生成图片映射对象
let imagesMap = {};
const compileImage = async () => {logger.task('compileImage');imagesMap = {};// 确保插件已加载if (!imageminMozjpeg) {await loadImageMinPlugins();}return src(paths.patterns.images).pipe(tap((file) => {const relativePath = file.relative.replace(/\\/g, '/');const pathParts = relativePath.split('/');const fileName = pathParts.pop();const nameMatch = fileName.match(/^([\w-]+)(@\d+x)?\.\w+$/);if (!nameMatch) return;const imageName = nameMatch[1];pathParts.reduce((obj, part, index) => {if (index === pathParts.length - 1) {obj[part] = obj[part] || {};obj[part][imageName] = `/${relativePath}`;} else {obj[part] = obj[part] || {};}return obj[part];}, imagesMap);})).pipe(imagemin([imageminMozjpeg({ quality: 80, progressive: true }),imageminOptipng({ optimizationLevel: 5 }),imageminSvgo({plugins: [{ name: 'removeViewBox', active: false },{ name: 'cleanupIDs', active: false }]})])).pipe(dest(paths.distSrc)).on('end', () => logger.success('图片压缩和处理完成'));
};// 生成图片映射文件,将图片映射对象写入到指定的 TypeScript 文件中
const generateImageIndex = (cb) => {logger.task('generateImageIndex');try {fs.writeFileSync(paths.imageIndexOutput, '// 自动生成,请勿修改\n' +`export default ${JSON.stringify(imagesMap, null, 2)};`);logger.success('图片映射生成完成');cb();} catch (err) {logger.error(`生成映射失败:${err.message}`);cb(err);}
};// 复制项目配置,将项目配置文件复制到构建目录
const copyProjectConfig = () => {logger.task('copyProjectConfig');return src(paths.patterns.projectConfig).pipe(changed(paths.dist)).pipe(dest(paths.dist)).on('end', () => logger.success('项目配置复制完成'));
};// 复制类型定义,将类型定义文件复制到构建目录
const copyTypings = () => {logger.task('copyTypings');return src(paths.patterns.typings, { base: './' }).pipe(changed(paths.dist)).pipe(dest(paths.dist)).on('end', () => logger.success('类型定义复制完成'));
};// 复制 npm 依赖,将 npm 包配置文件复制到构建目录
const copyNpmPackage = () => {logger.task('copyNpmPackage');return src(paths.patterns.npmPackage).pipe(changed(paths.distSrc)).pipe(dest(paths.distSrc)).on('end', () => logger.success('package.json复制完成'));
};
// 安装依赖的任务函数,用于在构建目录中安装npm依赖
// cb是Gulp的回调函数,用于通知任务完成或失败
const installNpm = (cb) => {// 记录任务开始日志logger.task('installNpm');// 根据操作系统生成不同的命令:// Windows系统使用cd /d切换目录(/d参数用于跨盘符切换),然后执行npm install// 其他系统(如macOS、Linux)直接使用cd切换目录,然后执行npm installconst cmd = process.platform === 'win32' ? `cd /d "${paths.distSrc}" && npm install` : `cd ${paths.distSrc} && npm install`try {// 同步执行shell命令,stdio: 'pipe'表示捕获输出流const stdout = execSync(cmd, { stdio: 'pipe' });// 将输出流转为字符串(npm的警告信息通常输出到stdout)const stderr = stdout.toString('utf-8');// 如果有警告信息,输出错误日志if (stderr) {logger.error(`npm警告:${stderr}`);}// 安装成功,输出成功日志并调用回调logger.success('依赖安装完成');cb();} catch (error) {// 安装失败,输出错误信息并将错误传递给回调logger.error(`npm安装失败:${error.message}`);cb(error);}
};// 构建npm的任务函数,使用miniprogram-ci工具处理小程序npm依赖
const buildNpm = async (cb) => {// 记录任务开始日志logger.task('buildNpm');try {// 引入微信小程序CI工具(用于构建npm、上传代码等)const ci = require('miniprogram-ci');// 尝试从project.config.json读取小程序appidlet appid = 'wxsomeappid'; // 默认appid(占位符)try {const projectConfig = require('./project.config.json');if (projectConfig.appid) {appid = projectConfig.appid;logger.info(`从project.config.json读取appid: ${appid}`);}} catch (err) {// 读取失败时使用默认值并警告logger.warn(`读取project.config.json失败,使用默认appid: ${appid}`);}// 定义私钥文件路径(用于小程序代码上传等操作的安全验证)const privateKeyPath = './private.wx0f4160dd2fa7d379.key';// 检查私钥文件是否存在if (!fs.existsSync(privateKeyPath)) {logger.error(`私钥文件不存在: ${privateKeyPath}`);cb(new Error('私钥文件不存在'));return;}// 创建项目实例,配置小程序基本信息const project = new ci.Project({appid: appid, // 小程序appidtype: 'miniProgram', // 项目类型(小程序)projectPath: paths.distSrc, // 项目源码路径(构建后的目录)privateKeyPath: privateKeyPath, // 私钥文件路径ignores: ['node_modules/**/*'], // 构建时忽略的文件});// 使用miniprogram-ci的packNpm方法构建npm依赖logger.info('开始构建npm...');const warnings = await ci.packNpm(project, {ignores: ['pack_npm_ignore_list'], // 构建时忽略的列表outputPath: paths.npmDist, // 构建结果输出目录reporter: (infos) => { // 构建过程中的信息回调infos.forEach(info => {logger.info(`构建信息: ${info.msg}`);});}});// 处理构建过程中产生的警告信息if (warnings && warnings.length > 0) {logger.warn('npm构建存在警告:');// 格式化警告信息,显示序号、内容、错误码和位置const formattedWarnings = warnings.map((it, index) => {return `${index + 1}. ${it.msg}> code: ${it.code}@ ${it.jsPath}:${it.startLine}-${it.endLine}`;}).join('---------------\n'); // 用分隔符拼接多个警告logger.warn(formattedWarnings);}// 构建成功,输出日志并调用回调logger.success('npm构建完成');cb();} catch (error) {// 构建失败,输出错误信息并传递错误logger.error(`npm构建失败:${error.message}`);cb(error);}
};// 监控文件变化的任务函数,当指定文件修改时自动执行对应构建任务
const watchFiles = () => {logger.info('开始监控文件变化...');// 监控WXML文件变化,变化时执行compileWxml任务watch(paths.patterns.wxml, compileWxml);// 监控SCSS文件变化,变化时执行compileScss任务watch(paths.patterns.scss, compileScss);// 监控TS文件变化,变化时执行compileTs任务watch(paths.patterns.ts, compileTs);// 监控WXS文件变化,变化时执行compileWxs任务watch(paths.patterns.wxs, compileWxs);// 监控JSON文件变化,变化时执行compileJson任务watch(paths.patterns.json, compileJson);// 监控图片文件变化,变化时按顺序执行compileImage和generateImageIndex任务watch(paths.patterns.images, series(compileImage, generateImageIndex));logger.success('文件监控已启动,等待文件变化...');// 监控环境配置文件变化,变化时执行generateEnvConfig任务watch(paths.envConfig, generateEnvConfig);// 监控npm包配置文件变化,变化时按顺序执行复制、安装、构建npm任务watch(paths.patterns.npmPackage, series(copyNpmPackage, installNpm, buildNpm));// 监控项目配置文件变化,变化时执行copyProjectConfig任务watch(paths.patterns.projectConfig, copyProjectConfig);// 监控appJson配置目录变化,变化时执行generateAppJson任务watch(paths.appJsonConfig, generateAppJson);// 监控pageUrl配置目录变化,变化时执行generatePageUrl任务watch(paths.pageUrlConfig, generatePageUrl);
};// ============================ 5. 任务入口(清晰分层) ============================
/** 完整构建流程:按顺序执行一系列任务 */
const build = series(generateEnvConfig, // 第一步:生成环境配置文件// 第二步:并行执行多个构建任务(提高效率)parallel(compileWxml, // 编译WXML文件compileTs, // 编译TS文件compileWxs, // 编译WXS文件compileJson, // 处理JSON文件compileWxss, // 处理WXSS文件compileScss, // 编译SCSS文件compileImage, // 处理图片文件copyNpmPackage, // 复制npm配置文件copyProjectConfig,// 复制项目配置文件copyTypings // 复制类型定义文件),generateImageIndex, // 第三步:生成图片映射文件// 第四步:并行生成应用配置文件parallel(generateAppJson, // 生成app.jsongeneratePageUrl // 生成pageUrl.ts),installNpm, // 第五步:安装npm依赖buildNpm // 第六步:构建npm依赖
);/** 开发模式任务:包含清理、完整构建、计时和文件监控 */
const dev = series((cb) => { // 构建计时:记录开始时间startTime = process.hrtime(); // 使用高精度计时cb();},clean, // 第一步:清理构建目录build, // 第二步:执行完整构建流程(cb) => { // 输出构建耗时const [s, ns] = process.hrtime(startTime); // 计算耗时(秒+纳秒)const totalMs = (s * 1000 + ns / 1e6).toFixed(2); // 转换为毫秒并保留两位小数logger.success(`构建完成,耗时:${totalMs}ms`);cb();},watchFiles, // 第三步:启动文件监控
);// 导出Gulp任务,允许通过命令行调用
exports.clean = clean; // 导出clean任务:gulp clean
exports.build = series(clean, build); // 导出build任务(先清理再构建):gulp build
exports.watch = watchFiles; // 导出watch任务:gulp watch
exports.dev = dev; // 导出dev任务:gulp dev
exports.default = dev; // 默认任务(执行gulp时默认运行dev)
注意事项:
- privateKeyPath 这个路径可以是你自定义的,不一定是项目根目录。虽然我们npm构建用不到这个,但是miniprogram-cli初始化的时候不能省。
- 要确保package.json和node_modules和小程序的app.json同级目录才能构建成功,也就是正确生成miniprogram_npm目录。
- generateImageIndex任务,是生成了图片的映射map, 访问的时候可以使用key值访问资源地址,这样我们在修改的时候只需要修改这个文件,不需要修改其他代码,但是这个文件放在主包,会增加主包大小,所以看具体情况使用。
- 此示例支持配置多个不同的tabbar数组,别当心主包页面多,增加体积,小程序在上传的时候,会自动过滤掉没有使用的文件,比如home页面只有环境量customer2是否用了,那么使用customer1的时候,home页面相关文件会被过滤调。
四、其他
- 小程序开发主要只放tabbar页面,其他页面放在子包。
- 只有子包使用的静态文件,放在子包。
- 页面、静态资源 目录命名要具体并保持一致,在readme中做好目录简介
- 做好工具方法封装,但自由子包用到的可以放在子包
- 业务弱相关的页面,可以单独放在一个子包,其他如果一个子包特别大,可以按业务再细分,多分出一个包,避免请求子包时,由于体积过大,请求慢。
- 做好页面跳转接口的封装,便于后面添加些拦截或者其他请求。
- 做好项目中页面的映射map,页面上所有跳转都是用key,这样路径有修改只需要修改映射文件,不需要修改其他代码。
- 公共常量尽量抽取,多处引入使用,便于修改。
- 图片可使用
https://tinypng.com/
压缩后使用,也可放置cdn - request\api拦截可借助wxapp-api-intercepters插件