共计 7307 个字符,预计需要花费 19 分钟才能阅读完成。
背景
公司有几个小程序我的项目,应用的原生形式开发。原生形式有它本人的益处,比方不必踩框架的坑了,但弊病也很显著,无奈应用框架带来的效率的晋升。基于此,就简略的应用 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 = watchFiles | |
exports.preview = preview | |
// ci 主动构建 npm | |
exports.npm = npmBuild | |
exports.upload = mpUpload | |
exports.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 文件的代码比较简单,解释的太多感觉有点多余了。以上配置在我司的已上线小程序上安稳运行,革新老本也很低。如果有小伙伴想在自家小程序上应用,遇到任何问题欢送留言。
以上,感激浏览!