開發小程序時,最麻煩的事情莫過於在上線前需要反覆切換測試和正式環境接口地址。
本文介紹一種小程序工程化改造的思路,基於這個我們能夠實現小程序自動根據多環境打包。
參考項目
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
的環境變量,並設置了dev
和prod
兩個值,這樣開發時會取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
試試吧!