手把手教你在小程式專案中配置Gulp
如果是做原生小程式開發,你是否想過在你的小程式專案中也加入一些工程的東西?本文將手把手教你在小程式中配置一些工程化的東西,基於我自身的痛點,目前有 支援Less 、 支援路徑別名 、 圖片自動壓縮,上傳七牛 、 打包加入Eslint檢查 這麼幾個功能,後期有遇到其他需要再更新。
在小程式專案中我目前用的是 Gulp 。為啥不用熱門的webpack?因為我覺得對於小程式專案來說gulp足夠了,關鍵是配置非常簡單,流式管理簡單明瞭,不瞭解Gulp的大家可以去官網看看。
需要提醒的是,以下舉例的gulpfie基於 gulp 4.0
,配置上與之前的版本略有不同,不過變化不大。詳細的大家可以點 這裡 去看看。

專案結構
gulpfile.js 我們放在根目錄下,初始長這樣
const { src, dest, parallel, watch, series } = require('gulp'); function defaultTask(cb) { // place code for your default task here cb(); } exports.default = defaultTask
接下來我們將一個任務一個任務往裡面新增。
支援Less
由於平時專案中習慣了用CSS 預處理語言,wxss的原始css 寫法讓我寫起專案來很難受。我個人比較習慣使用 Less ,習慣使用sass的大家自己找一下相關外掛替換一下就行。gulp中處理less的外掛是 gulp-less ,gulp-less把檔案處理完成後我們還需要把檔名改成 .wxss
所以我們還需要一個重新命名外掛,這裡用的是 gulp-rename 。
gulpfile.js如下:
const { src, dest, parallel, watch, series } = require('gulp'); const Path = require('path'); const Less = require('gulp-less'); const Rename = require('gulp-rename'); const path = { lessPath: ['src/**/*.less'], }; function wxss() { return src(path.lessPath, { base: 'src/' }) .pipe(Less()) .pipe(Rename({ extname: '.wxss', })) .pipe(dest('dist')); } exports.default = series(wxss);
需要注意的是上面的 src()
在第二個引數傳入了 {base: 'src/'}
,至於原因嘛,跟我們配的匹配less檔案路徑 src/**/*.less
有關。感興趣的同學可以結合 Explaining Globs 看看,這裡我不細說。
當然如果你想對wxss進行壓縮可以使用 gulp-csso 。
const { src, dest, parallel, watch, series } = require('gulp'); const Path = require('path'); const Less = require('gulp-less'); const Rename = require('gulp-rename'); const Csso = require('gulp-csso'); const GulpIf = require('gulp-if'); const path = { lessPath: ['src/**/*.less'], }; function wxss() { return src(path.lessPath, { base: 'src/' }) .pipe(Less()) .pipe(GulpIf(process.env.NODE_ENV === 'production', Csso())) .pipe(Rename({ extname: '.wxss', })) .pipe(dest('dist')); } exports.default = series(wxss);
支援路徑別名
在專案中,如果應用檔案路徑過深過長,不僅開發者寫起來費勁,還容易出錯且程式碼觀賞性很差。小程式中路徑別名我們用 gulp-wechat-weapp-src-alisa ,支援在 .wxml
、 .wxss/less
、 .js
中使用。
const { src, dest, parallel, watch, series } = require('gulp'); const Path = require('path'); const Less = require('gulp-less'); const Rename = require('gulp-rename'); const Csso = require('gulp-csso'); const GulpIf = require('gulp-if'); const Alias = require('gulp-wechat-weapp-src-alisa'); // 匹配檔案路徑 const path = { lessPath: ['src/**/*.less'], jsPath: ['src/**/*.js'], copy: ['src/**/*.wxml', 'src/**/*.json', 'src/**/*.wxs'], }; // 路徑拼接 function _join(dirname) { return Path.join(process.cwd(), 'src', dirname); } // 引用路徑別名配置 const aliasConfig = { '@Libs': _join('libs'), '@Utils': _join('utils'), '@Components': _join('components'), '@Style': _join('style'), '@Images': _join('images'), }; function wxss() { return src(path.lessPath, { base: 'src/' }) .pipe(Alias(aliasConfig)) .pipe(Less()) .pipe(GulpIf(process.env.NODE_ENV === 'production', Csso())) .pipe(Rename({ extname: '.wxss', })) .pipe(dest('dist')); } function js() { return src(path.jsPath) .pipe(Alias(aliasConfig)) .pipe(dest('dist')); } // 針對wxs,wxml,json檔案直接複製 function copy() { return src(path.copy) .pipe(Alias(aliasConfig)) .pipe(dest('dist')); } exports.default = series(wxss, js);
使用效果
.js
import * as Utils from '@Utils/base'; // require('@Libs/WXPage/index'); // 編譯後 import * as Utils from '../../utils/base'; // require('libs/WXPage/index');
.less
// index.less @import '@Style/variables.less'; .bg { background-image: url('@Images/32821027.jpgg'); } .usermotto { margin-top: 200px; color: @txt-highlight; } // 編譯後 .bg { background-image: url('../../images/32821027.jpg'); } .usermotto { margin-top: 200px; color: #FD7622; }
.wxml
<!--logs.wxml--> <import src="@Utils/index.wxs" /> <imagesrc="@Images/32821027.jpg" mode="cover"></image> // 編譯後 <import src="../../utils/index.wxs" /> <imagesrc="../../images/32821027.jpg" mode="cover"></image>
圖片自動壓縮,上傳七牛
由於小程式對程式碼包有限制,每1KB空間對小程式專案都十分珍貴,而且從使用者體驗來說,過大的程式碼包對首次進入小程式的使用者來說下載時間會過長。基於這些原因我們幾乎不會選擇把圖片放在小程式程式碼包中,那就只能上傳圖片伺服器了。
上傳圖片之前需要對圖片進行壓縮,可能有些通許是通過手動來做這部分工作的,其實可以用工具來的嘛。壓縮圖片用的外掛是 gulp-imagemin , 我的圖片是上傳到 七牛 ,對應的外掛是 gulp-qiniu-utils 。
接下來我們在gulpfile中新增圖片相關任務
const { src, dest, parallel, watch, series } = require('gulp'); const Path = require('path'); const Less = require('gulp-less'); const Rename = require('gulp-rename'); const Csso = require('gulp-csso'); const GulpIf = require('gulp-if'); const Alias = require('gulp-wechat-weapp-src-alisa'); const ImageMin = require('gulp-imagemin'); const UrlPrefixer = require('gulp-url-prefixer'); const Qiniu = require('gulp-qiniu-utils'); // 匹配檔案路徑 const path = { lessPath: ['src/**/*.less'], jsPath: ['src/**/*.js'], copy: ['src/**/*.wxml', 'src/**/*.json', 'src/**/*.wxs'], }; // 七牛相關配置 const qiniuOptions = { ak: 'ac key', sk: 'sk key', zone: 'Zone_z0', // 空間對應儲存區域(華東:z0,華北:z1,華南:z2,北美:na0) bucket: 'hynal-com', // 七牛對應空間 upload: { dir: './dist/images', // 上傳本地目錄 // prefix: 'test/', // 上傳時新增的字首,可省略 except: /\.(html|js)$/, // 上傳時不上傳檔案的正則匹配 }, remote: { url: 'https://*****.com', // 七牛空間域名 prefix: { default: 'test/', // 七牛空間預設字首,如果下面三個相同可省略 remove: 'test/', // 七牛空間刪除字首 prefetch: 'test/', // 七牛空間預取字首 refresh: 'test/', // 七牛空間重新整理字首 }, }, }; const urlPrefix = { prefix: 'https://cdn.liayal.com/dist', tags: ['image'], }; // 路徑拼接 function _join(dirname) { return Path.join(process.cwd(), 'src', dirname); } // 引用路徑別名配置 const aliasConfig = { '@Libs': _join('libs'), '@Utils': _join('utils'), '@Components': _join('components'), '@Style': _join('style'), '@Images': _join('images'), }; function wxss() { return src(path.lessPath, { base: 'src/' }) .pipe(Alias(aliasConfig)) .pipe(Less()) .pipe(UrlPrefixer.css(urlPrefix)) .pipe(GulpIf(process.env.NODE_ENV === 'production', Csso())) .pipe(Rename({ extname: '.wxss', })) .pipe(dest('dist')); } function js() { return src(path.jsPath) .pipe(Alias(aliasConfig)) .pipe(dest('dist')); } function imagemin() { return src(path.images) .pipe(ImageMin()) .pipe(dest('dist/images')); } const images = series(imagemin, (cb) => { const qiniu = new Qiniu(qiniuOptions); qiniu.upload(); cb(); }); // 針對wxs,wxml,json檔案直接複製 function copy() { return src(path.copy) .pipe(Alias(aliasConfig)) .pipe(UrlPrefixer.html(urlPrefix)) .pipe(dest('dist')); } exports.default = series(wxss, js, images);
上面我們還添加了一個 UrlPrefixer()
流,這個是把我們專案中引用的圖片替換成上傳七牛後的地址,需要配合七牛對應配置來設定。
如:
// 替換前 <imagesrc="@Images/32821027.jpg" mode="cover"></image> // 替換後 <imagesrc="https://cdn.liayal.com/dist/images/32821027.jpg" mode="cover"></image>
打包加入Eslint檢查
打包時加入Eslint檢查可以讓我們提早發現一些由程式碼引發問題,也方便推行程式碼規範。
下面我們在js任務中加入 gulp-eslint
const { src, dest, parallel, watch, series } = require('gulp'); const Path = require('path'); const Less = require('gulp-less'); const Rename = require('gulp-rename'); const Csso = require('gulp-csso'); const GulpIf = require('gulp-if'); const Alias = require('gulp-wechat-weapp-src-alisa'); const ImageMin = require('gulp-imagemin'); const UrlPrefixer = require('gulp-url-prefixer'); const Qiniu = require('gulp-qiniu-utils'); const ESLint = require('gulp-eslint'); // 匹配檔案路徑 const path = { lessPath: ['src/**/*.less'], jsPath: ['src/**/*.js'], copy: ['src/**/*.wxml', 'src/**/*.json', 'src/**/*.wxs'], }; // 七牛相關配置 const qiniuOptions = { ak: 'ac key', sk: 'sk key', zone: 'Zone_z0', // 空間對應儲存區域(華東:z0,華北:z1,華南:z2,北美:na0) bucket: 'hynal-com', // 七牛對應空間 upload: { dir: './dist/images', // 上傳本地目錄 // prefix: 'test/', // 上傳時新增的字首,可省略 except: /\.(html|js)$/, // 上傳時不上傳檔案的正則匹配 }, remote: { url: 'https://*****.com', // 七牛空間域名 prefix: { default: 'test/', // 七牛空間預設字首,如果下面三個相同可省略 remove: 'test/', // 七牛空間刪除字首 prefetch: 'test/', // 七牛空間預取字首 refresh: 'test/', // 七牛空間重新整理字首 }, }, }; const urlPrefix = { prefix: 'https://cdn.liayal.com/dist', tags: ['image'], }; // 路徑拼接 function _join(dirname) { return Path.join(process.cwd(), 'src', dirname); } // 引用路徑別名配置 const aliasConfig = { '@Libs': _join('libs'), '@Utils': _join('utils'), '@Components': _join('components'), '@Style': _join('style'), '@Images': _join('images'), }; function wxss() { return src(path.lessPath, { base: 'src/' }) .pipe(Alias(aliasConfig)) .pipe(Less()) .pipe(UrlPrefixer.css(urlPrefix)) .pipe(GulpIf(process.env.NODE_ENV === 'production', Csso())) .pipe(Rename({ extname: '.wxss', })) .pipe(dest('dist')); } function js() { return src(path.jsPath) .pipe(Alias(aliasConfig)) .pipe(ESLint()) .pipe(ESLint.format()) .pipe(dest('dist')); } function imagemin() { return src(path.images) .pipe(ImageMin()) .pipe(dest('dist/images')); } const images = series(imagemin, (cb) => { const qiniu = new Qiniu(qiniuOptions); qiniu.upload(); cb(); }); // 針對wxs,wxml,json檔案直接複製 function copy() { return src(path.copy) .pipe(Alias(aliasConfig)) .pipe(UrlPrefixer.html(urlPrefix)) .pipe(dest('dist')); } exports.default = series(wxss, js, images);
效果大概是這樣的
也可以通過 eslint.failOnError() 或者 eslint.failAfterError() 在編譯報錯時中斷編譯。
END
最後完善一下gulpfile, 新增監測任務,新增一個clean任務
const { src, dest, parallel, watch, series } = require('gulp'); const Path = require('path'); const Less = require('gulp-less'); const Rename = require('gulp-rename'); const Csso = require('gulp-csso'); const GulpIf = require('gulp-if'); const Alias = require('gulp-wechat-weapp-src-alisa'); const ImageMin = require('gulp-imagemin'); const UrlPrefixer = require('gulp-url-prefixer'); const Qiniu = require('gulp-qiniu-utils'); const ESLint = require('gulp-eslint'); const Clean = require('gulp-clean'); // 匹配檔案路徑 const path = { lessPath: ['src/**/*.less'], jsPath: ['src/**/*.js'], copy: ['src/**/*.wxml', 'src/**/*.json', 'src/**/*.wxs'], }; // 七牛相關配置 const qiniuOptions = { ak: 'ac key', sk: 'sk key', zone: 'Zone_z0', // 空間對應儲存區域(華東:z0,華北:z1,華南:z2,北美:na0) bucket: 'hynal-com', // 七牛對應空間 upload: { dir: './dist/images', // 上傳本地目錄 // prefix: 'test/', // 上傳時新增的字首,可省略 except: /\.(html|js)$/, // 上傳時不上傳檔案的正則匹配 }, remote: { url: 'https://*****.com', // 七牛空間域名 prefix: { default: 'test/', // 七牛空間預設字首,如果下面三個相同可省略 remove: 'test/', // 七牛空間刪除字首 prefetch: 'test/', // 七牛空間預取字首 refresh: 'test/', // 七牛空間重新整理字首 }, }, }; const urlPrefix = { prefix: 'https://cdn.liayal.com/dist', tags: ['image'], }; // 路徑拼接 function _join(dirname) { return Path.join(process.cwd(), 'src', dirname); } // 引用路徑別名配置 const aliasConfig = { '@Libs': _join('libs'), '@Utils': _join('utils'), '@Components': _join('components'), '@Style': _join('style'), '@Images': _join('images'), }; function wxss() { return src(path.lessPath, { base: 'src/' }) .pipe(Alias(aliasConfig)) .pipe(Less()) .pipe(UrlPrefixer.css(urlPrefix)) .pipe(GulpIf(process.env.NODE_ENV === 'production', Csso())) .pipe(Rename({ extname: '.wxss', })) .pipe(dest('dist')); } function js() { return src(path.jsPath) .pipe(Alias(aliasConfig)) .pipe(ESLint()) .pipe(ESLint.format()) .pipe(dest('dist')); } function imagemin() { return src(path.images) .pipe(ImageMin()) .pipe(dest('dist/images')); } const images = series(imagemin, (cb) => { const qiniu = new Qiniu(qiniuOptions); qiniu.upload(); cb(); }); // 針對wxs,wxml,json檔案直接複製 function copy() { return src(path.copy) .pipe(Alias(aliasConfig)) .pipe(UrlPrefixer.html(urlPrefix)) .pipe(dest('dist')); } function clean() { return src('dist/*', { read: false }) .pipe(Clean()); } watch(path.lessPath, wxss); watch(path.jsPath, js); watch(path.copy, copy); watch(path.images, images); exports.default = series(clean, parallel(copy, wxss, js, images));
整個專案我放在github gulp-wechat-weapp