3

开发小程序时,最麻烦的事情莫过于在上线前需要反复切换测试和正式环境接口地址。

本文介绍一种小程序工程化改造的思路,基于这个我们能够实现小程序自动根据多环境打包。

参考项目

https://github.com/mecoepcoo/miniprogram-boilerplate

准备工作

阅读本文,你需要有对以下内容的基本认知:

  • gulp
  • 环境变量

工程化改造

原生的小程序只有一个简单的启动脚手架,不支持less、sass等样式预处理器,对npm的支持也不是太好,我们自己做一个简单的构建工具,来增强小程序的功能。本节起一个抛砖引玉的作用,基于这个思路,可以改造出更多更强大的功能。

目录结构

  • dist 输出/发布目录,在微信开发者工具中打开这个目录

    • project.config.json 配置文件,这个文件与src中的配置无关
  • src

    • miniprogram

      • assets 静态资源目录,放图片之类的东西
      • components 组件目录
      • pages 小程序页面目录
      • app.js 小程序入口
      • app.json 小程序全局配置
      • app.less
      • sitemap.json
      • project.config.json 配置文件样板,这个文件不会被编译到dist中

安装gulp

本文写作时,gulp的版本是v4,api与之前的版本有一些变化。

创建一个空目录后,先安装依赖:

$ npm init
$ npm i -D gulp gulp-plumber gulp-rename del

在根目录新建gulpfile.js文件,引入依赖:

const gulp = require('gulp');
const plumber = require('gulp-plumber'); // 发生错误时阻止gulp退出并输出日志
const rename = require('gulp-rename'); // 输出时重命名文件
const del = require('del');

支持less

用less等预处理器书写样式,可能会更方便,安装依赖:

$ npm i -D gulp-less gulp-cssnano

假设我们的源码放在/src/miniprogram目录下,输出到/dist目录下。

现在让gulp支持less编译:

const less = require('gulp-less'); // 处理less
const cssnano = require('gulp-cssnano'); // 压缩代码
// 编译样式
gulp.task('build:style', () => {
  return gulp.src([ // 千万不要漏掉return,否则gulp不知道这个任务何时完成
    'src/miniprogram/pages/**/*.less', 
    'src/miniprogram/components/**/*.less',
    'src/miniprogram/spreadpack/**/*.less',
    'src/miniprogram/app.less'
  ], {base: 'src/miniprogram'})
    .pipe(plumber())
    .pipe(less())
    .pipe(
      cssnano({
        zindex: false,
        autoprefixer: false,
        discardComments: { removeAll: true }
      })
    )
    .pipe(
      rename(path => {
        path.extname = '.wxss'; // 我们用less做后缀名,但小程序只支持wxss,所以需要修改输出的后缀
      })
    )
    .pipe(gulp.dest('dist')); // 写入到dist文件夹中
});

处理脚本、模板和配置文件

本文只做简单的思路介绍,所以我们还是按照原来的方法编写小程序的js和模板。

这里只是把js,wxml和json复制到输出目录:

// 编译示例
gulp.task('build:main', gulp.series('build:style', () => {
  return gulp.src([
    'src/miniprogram/**/*',
    '!src/miniprogram/**/*.less', // 排除less后缀文件
    '!src/project.config.json', // 配置文件不写入到dist文件夹,开发时需手动拷贝到dist文件夹中 !!!
  ], {base: 'src/miniprogram', allowEmpty: true})
    .pipe(plumber())
    .pipe(gulp.dest('dist'));
}));

清理输出目录

在发布前,我们需要删除掉多余的文件,这里新增一个工作流用来清理输出目录:

gulp.task('clean', cb => {
  return del([
    'dist/**/*',
    '!dist/project.config.json'
  ], cb);
});

配置开发环境启动命令和构建命令

最后我们补全功能,首先增加一个开发环境启动配置:

gulp.task('build', gulp.series('build:main'));
// 监听文件(若文件修改则执行相关的任务)
function watch() {
  let watcher = gulp.watch('src/**', cb => cb());
  watcher.on('all', (event, path, stats) => {
    console.log('File ' + path + ' was ' + event + ', running tasks...');
  });
  return watcher;
}

gulp.task('default', gulp.series(watch));

package.json中增加脚本:

"scripts": {
  "start": "npm run clean && npm run build",
  "dev": "gulp",
  "build": "gulp build",
  "watch": "gulp watch",
  "clean": "gulp clean",
}

现在执行npm start或者npm run dev,用小程序开发工具打开dist目录,就能看到效果了。

注入环境变量

有了gulp,一切关于构建的问题都简单了。使用gulp-preprocess来支持环境变量。

gulp-preprocess的用法见官方文档

安装依赖:

$ npm i -D cross-env gulp-preprocess

修改构建配置

由于操作系统之间设置环境变量命令的差异,引入cross-env来解决,先修改package.json

"scripts": {
  "start": "cross-env NODE_ENV=prod npm run clean && npm run build",
  "dev": "cross-env NODE_ENV=dev gulp",
  "build": "cross-env NODE_ENV=prod gulp build",
  "watch": "cross-env NODE_ENV=dev gulp watch",
  "clean": "gulp clean",
}

这里增加了一个名为NODE_ENV的环境变量,并设置了devprod两个值,这样开发时会取dev变量,打包发布时会取prod变量。

然后增加gulp配置:

const preprocess = require('gulp-preprocess'); // 注入环境变量

gulp.task('build:main', gulp.series('build:style', () => {
  return gulp.src([
    'src/miniprogram/**/*',
    '!src/miniprogram/assets/**/*', // 新增配置在这里
    '!src/miniprogram/**/*.less',
    '!src/project.config.json',
  ], {base: 'src/miniprogram', allowEmpty: true})
    .pipe(plumber())
    .pipe(preprocess()) // 新增配置在这里
    .pipe(gulp.dest('dist'));
}));

// 由于preprocess这个插件会影响静态资源,所以需要把静态资源的打包拿出去
/* 处理静态资源 */
gulp.task('build:assets', () => {
  return gulp.src([
    "src/miniprogram/assets/**/*"
  ], { base: 'src/miniprogram', allowEmpty: true })
    .pipe(plumber())
    .pipe(gulp.dest('dist'));
});

// 修改构建配置
gulp.task('build', gulp.parallel('build:main', 'build:assets'));
function watch() {
  let watcher = gulp.watch('src/**', gulp.parallel('build:main', 'build:assets'), cb => cb());
  watcher.on('all', (event, path, stats) => {
    console.log('File ' + path + ' was ' + event + ', running tasks...');
  });
  return watcher;
}

代码示例

用这种方法注入环境变量:

let env = '/* @echo NODE_ENV */';
let apiRoot = '';
switch (env) {
  case 'dev':
    apiRoot = 'http://dev-api.tianzhen.tech';
    break;
  case 'prod':
    apiRoot = 'http://api.tianzhen.tech';
    break;
}

写一个demo,运行npm start试试吧!


天真小兮兮
137 声望27 粉丝