背景

公司有几个小程序我的项目,应用的原生形式开发。原生形式有它本人的益处,比方不必踩框架的坑了,但弊病也很显著,无奈应用框架带来的效率的晋升。基于此,就简略的应用gulp,配置了一套工作流,帮咱们在开发过程中省去一些反复的操作。

为什么应用gulp而没有用webpack?在对这两个工具都实际了一番后,发现针对咱们的问题,应用gulp更简略。。。

对于gulp本文不会做过多的介绍,侧重点在于性能的实现。

性能列表

  • 反对应用less书写css
  • css 主动补全
  • js 语法反对
  • css和js代码压缩
  • 图片主动上传到cdn,监听到图片变动后,主动上传cdn
  • 反对动态资源目录,例如di三方JS、须要放到本地的图片
  • 小程序原生npm性能,通过命令主动构建小程序实用的npm包
  • 单位默认转换,即px主动转为rpx,同时反对不同端屏幕的适配
  • 反对source map,可能定位构建后的代码是在哪里出错的
  • 通过命令创立page和component,主动生成四个文件
  • 反对环境变量,通过命令主动生成不同环境的代码
  • 引入miniprogram-ci,通过上传代码到微信后盾

目录构造

先看下配置好后的目录构造,其中src目录内容就是微信小程序的代码,内部的都是一些配置文件。

命令定义

上面是在package.json中定义的命令,接下来会一个个实现这些性能。

"scripts": {    "start": "rm -rf ./dist && cross-env NODE_ENV=development gulp",    "preview": "gulp preview",    "build-pro": "rm -rf ./dist && cross-env NODE_ENV=production gulp build",    "build-dev": "rm -rf ./dist && cross-env NODE_ENV=development gulp build",    "build-npm": "gulp npm",    "deploy-dev": "npm run build-dev && gulp upload",    "deploy-pro": "npm run build-pro && gulp upload",    "cp": "create-wxapp-page --type=page --dir=src --indent=2 --style=less",    "cc": "create-wxapp-page --type=component --dir=src --indent=2 --style=less"  },

gulpfile

gulpfile文件是所有的配置代码,前面会对每个task做具体解释。

const path = require('path');const fileExists = require('file-exists');const gulp = require('gulp');const less = require('gulp-less');const uglify = require('gulp-uglify');const cleanCSS = require('gulp-clean-css');const rename = require('gulp-rename');const del = require('del');const qcloud = require('qcloud-upload');const gulpif = require('gulp-if');const gutil = require('gulp-util');const replace = require('gulp-replace');const px2rpx = require('gulp-px2rpx');const ci = require('miniprogram-ci');const sourcemaps = require('gulp-sourcemaps');const alias = require('gulp-path-alias');const pkg = require('./package.json')const projectConfig = require('./project.config.json')const buildPath = path.join(__dirname, 'dist/')const uploadFolder = path.join(__dirname, './src/images')const isPro = process.env.NODE_ENV === 'production'const config = {// 腾讯CDN油漆桶配置  assetsCDN: 'https://xxx.cos.ap-guangzhou.myqcloud.com/',  cos: {    Bucket: 'xxx',    Region: 'xxx',    SecretId: 'xxx',    SecretKey: 'xxx',    prefix: `${pkg.name}/images`, // 上传到油漆桶的哪个文件夹    src: uploadFolder, // 上传哪个文件夹到油漆桶    overWrite: 1,  },  enablePx2Rpx: true,  enableCleanCSS: false,  enableAuto: true, // 主动补全css  enableUglify: false,  enableSourcemap: true,};const paths = {  styles: {    src: ['src/**/*.less'],    dest: buildPath  },  images: {    src: 'src/images/**/*.{png,jpg,jpeg,svg,gif}',    dest: buildPath  },  scripts: {    src: 'src/**/*.js',    dest: buildPath  },  copy: {    src: ['src/**', '!src/**/*.less', '!src/images/**', '!src/**/*.js', 'package.json'],    dest: buildPath  },}// 删除构建function clean() {  return del([buildPath])}function log() {  const data = Array.prototype.slice.call(arguments)  gutil.log.apply(false, data)}function upload() {  return new Promise(function (resolve, reject) {    // 一般函数,resolve()的时候,qcloud不肯定执行完结    qcloud(config.cos)    resolve()  })}// 工作处理函数function styles() {  return gulp    .src(paths.styles.src, { base: 'src' })    .pipe(alias({      paths: {        '@': path.resolve(__dirname, './src/'),      }    }))    .pipe(less())    .pipe(replace('%CDN_IMG%/', config.assetsCDN + config.cos.prefix + '/'))    .pipe(gulpif(config.enableCleanCSS, cleanCSS()))    .pipe(gulpif(config.enablePx2Rpx, px2rpx({      screenWidth: 375, // 设计稿屏幕, 默认750      wxappScreenWidth: 750, // 微信小程序屏幕, 默认750      remPrecision: 6    })))    .pipe(replace('PX', 'px'))    .pipe(rename(path => (path.extname = '.wxss')))    .pipe(gulp.dest(paths.styles.dest))}function scripts() {  return gulp    .src(paths.scripts.src, { base: 'src' })    .pipe(alias({      paths: {        '@': path.resolve(__dirname, './src/'), // src 目录      }    }))    .pipe(gulpif(config.enableSourcemap, sourcemaps.init()))    .pipe(gulpif(isPro, replace('%ENV%', 'production'), replace('%ENV%', 'development'))) // 环境变量动态替换    .pipe(replace('%CDN_IMG%/', config.assetsCDN + config.cos.prefix + '/'))    .pipe(replace('%VERSION%', pkg.version))    .pipe(gulpif(config.enableUglify, uglify()))    .pipe(gulpif(config.enableSourcemap, sourcemaps.write('.')))    .pipe(gulp.dest(paths.scripts.dest))}// 不须要解决的文件间接复制过来function copy() {  return gulp    .src(paths.copy.src)    .pipe(replace('%CDN_IMG%/', config.assetsCDN + config.cos.prefix + '/'))    .pipe(gulp.dest(paths.copy.dest))}function watchFiles() {  const w1 = gulp.watch(paths.styles.src, styles).on('unlink', function (file) {    log(gutil.colors.yellow(file) + ' is deleted')    const filePath = file.replace(/src\\/, 'dist\\')    del([filePath])  });  const w2 = gulp.watch(paths.scripts.src, scripts).on('unlink', function (file) {    log(gutil.colors.yellow(file) + ' is deleted')    const filePath = file.replace(/src\\/, 'dist\\')    del([filePath])  });  const w3 = gulp.watch(paths.copy.src, copy).on('unlink', function (file) {    log(gutil.colors.yellow(file) + ' is deleted')    const filePath = file.replace(/src\\/, 'dist\\')    del([filePath])  });  const w4 = gulp.watch(paths.images.src, upload).on('unlink', function (file) {    log(gutil.colors.yellow(file) + ' is deleted')    const filePath = file.replace(/src\\/, 'tmp\\')    del([filePath])  });  return Promise.all([w1, w2, w3, w4])}/** * 小程序ci相干函数 */let project = {}const keyFile = fileExists.sync(`./private.${projectConfig.appid}.key`)if (keyFile) {  project = new ci.Project({    appid: projectConfig.appid,    type: 'miniProgram',    projectPath: './dist',    privateKeyPath: `./private.${projectConfig.appid}.key`,    ignores: [],  })}async function npmBuild() {  await ci.packNpmManually({    packageJsonPath: './package.json',    miniprogramNpmDistDir: './src/',  })}async function mpUpload() {  const uploadResult = await ci.upload({    project,    version: pkg.version,    desc: pkg.description,    setting: {      es6: true,      minify: true,      autoPrefixWXSS: true,    },    onProgressUpdate: console.log,  })  console.log('[uploadResult:]', uploadResult)}async function preview() {  const previewResult = await ci.preview({    project,    desc: pkg.description, // 此备注将显示在“小程序助手”开发版列表中    qrcodeFormat: 'image',    qrcodeOutputDest: './preview.jpg',    onProgressUpdate: console.log,  })  console.log('[previewResult:]', previewResult)}exports.watch = watchFilesexports.preview = preview// ci 主动构建npmexports.npm = npmBuildexports.upload = mpUploadexports.default = gulp.series(styles, scripts, copy, upload, watchFiles)exports.build = gulp.series(clean, styles, scripts, copy, upload)

主动创立组件和页面

主动创立组件和页面应用了一个npm包,叫做create-wxapp-page,有了它就能在命令行间接创立一组文件了。

npm run cc # 而后输出组件名字 npm run cp # 而后输出页面名字

主动构建npm

小程序是反对npm的,然而须要在开发者工具开启后,手动点击编译,能力生成对应的npm包。还好微信提供了miniprogram-ci包,能够省去手动的过程。

const ci = require('miniprogram-ci');async function npmBuild() {  await ci.packNpmManually({    packageJsonPath: './package.json',    miniprogramNpmDistDir: './src/',  })}exports.npm = npmBuild

执行npm run build-npm,咱们装置的包就会构建到src的miniprogram_npm目录下。

环境变量反对

环境变量能够帮咱们辨别不同环境的要执行的代码,比方API地址。实现形式是动态替换。

pipe(gulpif(isPro, replace('%ENV%', 'production'), replace('%ENV%', 'development'))
const isPro = '%ENV%' === 'production'if (isPro) {  // 生产环境  } else {  // 测试,体验环境 }

图片上传到CDN

当监听到images文件夹中有图片变更,会执行上传操作。

function upload() {  return new Promise(function (resolve, reject) {    // 一般函数,resolve()的时候,qcloud不肯定执行完结。qcloud没有提供上传胜利的callback    qcloud(config.cos)    resolve()  })}

应用CND图片

<image src="%CDN_IMG%/index/empty" mode="aspectFit" class="empty__img" />

sourcemap

sourcemap也很简略,同样是一个包,gulp-sourcemap

.pipe(gulpif(config.enableSourcemap, sourcemaps.init())

动态目录

对于一些非凡的动态资源,比方tabbar的icon图片,能够放到src/static目录下,这个目录的文件会原样copy到dist目录。

js语法反对

js新语法倡议应用小程序工具自带的“es6转es5”和“加强编译”,实际发现应用glup-babel会导致js编译太慢。工具自带的曾经够用了。

应用CI上传代码

miniprogram-ci提供了上传代码到小程序管理后盾。第一步是先下载密钥,而后把文件命命名为private.{appId}.key,最初搁置到我的项目根目录。处于平安思考,更好的做法到是把ci性能放到服务器端,这里就先放到前端了。

执行npm run deploy-dev就能够把代码上传到体验版了,最初体验版选为ci机器人。

project.config.json变更

{   "description": "我的项目配置文件",  "miniprogramRoot": "dist/",  "packOptions": {    "ignore": [      {        "type": "folder",        "value": "images"      }    ]  }  }

dist这个目录是构建进去的小程序代码,因而要指定 miniprogramRoot为“dist/”

因为图片上传到cdn了,src/images文件夹就没有必要打包进小程序了,这样也缩小了体积。

### 结语

其实glupfile文件的代码比较简单,解释的太多感觉有点多余了。以上配置在我司的已上线小程序上安稳运行,革新老本也很低。如果有小伙伴想在自家小程序上应用,遇到任何问题欢送留言。

以上,感激浏览!