为什么不用wepy、mpvue、taro、uni-app等框架
基本情况:
- 组内:没做过小程序,会Vue;React本人会一点点,其他成员暂未接触
- 组外:声称要得急,声称只做微信端
分析:
- wepy: 小程序本来就是一套新语法,又整一套DSL,声称要得急,来不及学
- taro: 个人觉得看上去还不错,限于React
- mpvue uni-app: 都是Vue系列,风评参半
- 个人考虑:小程序本来就是坑,再套一层框架不知道会带来多大的问题;其次第一次做小程序,且只要微信端,想直接上原生进行尝试
有多坑
但是,微信小程序的工程化真的菜,开发体验是真的差…
存在的问题
- ES6+: 大部分都没什么问题,小程序开发工具提供了ES6转ES5的选项。是不是全支持我不知道,但我知道勾选以后
async/await
是用不了的…会报regeneratorRuntime is not defined
的错误。网传方法是:引入runtime,我试了,亲测无效…
- WXSS: 被SCSS惯坏了,觉得这玩意儿写起来好累;其次,组内大佬问我,SCSS前插全局的@import能实现不?
- rpx: 小程序搞的单位,按照我司一贯375px的设计稿,自己手写rpx需要乘2,当然是不愿意手写的..
- 图片: 组内大佬问我,能自动把小图片转base64么?
- 别名(alias): 文件层次深了以后就会出现
../../../../../a.js
,过于精污
- 环境变量: 查遍小程序文档,只字不提环境变量。
- 其他: ESLint,Prettier,stylelint,git flow,git hooks 这些配置在前端组内已经蛮成熟了,好说。
选型
其实一开始觉得存在的问题不是很大,所以觉得gulp
就能解决了,实际做了以后会发现没想象的那么简单,一怒之下第二版直接上了webpack
…这里先说一下第一版的思路
项目结构
1 2 3 4 5 6 7
| ... ├── dist ├── gulpfile.js ├── package.json ├── postcss.config.js ├── project.config.json └── src
|
主要思路是src
下面写代码,通过gulp
处理生成dist
目录,因此需要在小程序的project.config.json
文件中添加"miniprogramRoot": "dist/"
,告诉开发者工具小程序的入口是dist
gulpfile
准备工作:
1 2
| const { src, dest, parallel, watch, series, task } = require('gulp') const path = require('path')
|
文件glob
1 2 3 4 5 6
| const paths = { scss: ['src/**/*.scss', '!src/**/_*.scss'], js: 'src/**/*.js', wxml: 'src/**/*.wxml', base: 'src/**/*.{json,wxs}' }
|
scss
- scss编译为css
- postcss处理rpx
- 前插全局变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| const sass = require('gulp-sass') sass.compiler = require('node-sass') const rename = require('gulp-rename') const postcss = require('gulp-postcss') const postcssConfig = require('./postcss.config') const header = require('gulp-header')
const sassConfig = { header: ` @import "@/assets/styles/_variable.scss"; @import "@/assets/styles/_mixins.scss"; ` }
function scssTack() { return src(path.scss) .pipe(header(sassConfig.header)) .pipe(sass({ errorLogToConsole: true })) .on('error', sass.logError) .pipe(postcss(postcssConfig)) .pipe(rename({ extname: '.wxss' })) .pipe(dest('dist')) }
|
插件写的比较简陋..proportion
是比率,minPixelValue
是需要转换的最小值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const postcss = require('postcss') const pxRegExp = /"[^"]+"|'[^']+'|url\([^\)]+\)|(\d*\.?\d+)px/g
const px2rpx = postcss.plugin('postcss-px2rpx', (opts = {}) => { const { proportion = 1, minPixelValue = 0 } = opts
return root => { root.replaceValues(pxRegExp, { fast: 'px' }, string => { const pixels = parseInt(string) if (pixels < minPixelValue) return `${pixels}px` return `${proportion * parseInt(string)}rpx` }) } })
module.exports = [ px2rpx({ proportion: 2, minPixelValue: 5 }) ]
|
js
只搞了eslint
1 2 3 4 5 6 7
| function jsTask() { return src(paths.js) .pipe(eslint({ fix: true })) .pipe(eslint.format()) .pipe(dest('dist')) }
|
直接复制的
1 2 3 4
| function copyTask() { return src(paths.base).pipe(dest('dist')) }
|
环境变量
原生不支持,自己写env文件,用替换的方式实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const dotenv = require('dotenv')
const env = process.env.NODE_ENV const envFilePath = path.resolve(__dirname, `.env.${env}`) dotenv.config({ path: envFilePath }) const { API_BASE_URL } = process.env
function jsTask() { return src(paths.js) .pipe(aliases(aliasConfig)) .pipe(replace('__API_BASE_URL__', API_BASE_URL)) .pipe(eslint({ fix: true })) .pipe(eslint.format()) .pipe(dest('dist')) }
|
1 2 3
| "dev": "cross-env NODE_ENV=development gulp", "build": "cross-env NODE_ENV=production gulp build"
|
1 2 3 4 5 6 7
| API_BASE_URL = '/dev/' ...
API_BASE_URL = '/prod/' ...
|
使用方法: const baseURL = '__API_BASE_URL__'
alias
用的别人的插件 gulp-wechat-weapp-src-alisa
1 2 3 4 5 6 7 8 9 10
| const aliases = require('gulp-wechat-weapp-src-alisa') const aliasConfig = { '@': path.join(__dirname, 'src') }
function wxmlTask() { return src(paths.wxml) .pipe(aliases(aliasConfig)) .pipe(dest('dist')) }
|
图片
有点麻烦…
一开始做了一个插件,去解析wxml的image标签中的src:
/(<image.*?src=['"])([^{}]*?)(['"]>)/g
然后判断要不要转换base64,需要转换的就把src的url替换成base64:
1 2 3 4 5 6
| function image2base64(picturePath) { const buffer = fs.readFileSync(picturePath) const mimetype = mime.getType(path.extname(picturePath))
return `data:${mimetype};base64,${buffer.toString('base64')}` }
|
不需要的就复制图片。
但是!因为wxml语法的关系 有时候会这么写:
src="./images/icon_{{ type }}.png"
假如我在另外一个地方写了
src="./images/icon_warning.png"
这个图片又很小,那么这里的src会变成base64,前面动态url就会找不到图片…
解决办法:没有,第二版用了webpack解决
watch
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| const del = require('del') const logger = require('gulplog')
function watchHandler(type, file) { const { sep } = path const extname = path.extname(file) const delPath = file.replace(`src${sep}`, `dist${sep}`)
const categories = { add: { scss: scssTask, js: jsTask, wxml: wxmlTask, default: copyTask }, change: { scss: scssTask, js: jsTask, wxml: wxmlTask, default: copyTask }, remove: { scss: () => del([delPath.replace(extname, '.wxss')]), js: () => del([delPath]), wxml: () => del([delPath]), default: () => del([delPath]) } }
const category = categories[type] const task = category[extname.slice(1)] || category.default
task() }
function watchTask(cb) { const watcher = watch(['src'], { ignored: /[\/\\]\./ }) const { info } = logger
watcher .on('change', file => { watchHandler('change', file) info('File changed: ' + file) }) .on('add', file => { watchHandler('add', file) info('Add file: ' + file) }) .on('unlink', file => { watchHandler('remove', file) info('Remove file: ' + file) })
cb() }
|
注册task
1 2 3
| task('default', series(cleanTask, parallel(scssTask, jsTask, wxmlTask, copyTask), watchTask))
task('build', series(cleanTask, parallel(scssTask, jsTask, wxmlTask, copyTask)))
|
使用
开发: npm run dev
生产打包: npm run build
结论
- 图片转base64问题没有好的解决办法
async/await
无法使用
- 让小程序开发工具做了压缩、转码、sourcemap的工作,不可控
其他功能大致上都能实现
为了体验能更好一点,第二版开始探索webpack..