您现在的位置: 微信小程序 > 微信小程序运营 > 经验 >

写微信小程序这一个月

来源:微信小程序 编辑:Yiyongtong.com 发布时间:2020-05-18 10:54热度:

 

最近在做一个微信小程序的项目,遇到了不少坑,不得不说微信小程序的官方文档写的真的不怎么样,一开始技术选型的时候也有考虑过第三方框架,mpvue,uniapp等,但是最后考虑了下,还是先了解一下原生小程序比较好,我始终认为一开始就上第三方框架并不是很好。

下面我总结了一些坑点和开发过程中容易遇到的问题。

wxss or less

 

对于写惯了less的开发来书,写wxss实在是效率低下,太煎熬了,对此可以使用vscode插件 easy-less 来解决,我是用vscode来写代码,微信开发工具只作预览。

网络请求

 

wx.request是小程序的网络请求方法,默认情况下,我们希望success是在服务器响应的状态码在2xx的时候触发,实际上不是,只要网络请求成功发出了,success方法就会触发,比如500状态码也会触发success,所以fail也只在网络请求没有发出的情况下触发,所以你必须在success重复写判断逻辑,而且也不能用promise,所以有必要封装一下

// 封装前
wx.request({
  url: 'test.php', //仅为示例,并非真实的接口地址
  data: {
    x: '',
    y: ''
  },
  header: {
    'content-type': 'application/json' // 默认值
  },
  success (res) {
    // 假设服务器的响应体是{success: true, data: 123}
    if (res.statusCode === 2xx) {
        fn(res.data.data)
    } else {
        wx.showToast({            icon: 'none',            title: res.data.message || '网络异常',            duration: 1500        })
    }
  },
  fail (res) {
    console.log(res);
  }
})

// 封装后
// request.js
module.exports = function ({url, data, method}) {    const app = getApp();    return new Promise((resolve, reject) => {        wx.request({            method: method,            url: `${app.globalData.apiUrl}/${url}`,            data: data,            header: {                token: app.globalData.token            },            success: function (res) {                if (res.statusCode === 200) {                    const data = res.data.data;                    resolve(data);                } else if (res.statusCode === 401) {
                    // 登录失效,包含业务逻辑,根据需求添加
                    wx.setStorageSync('token', null); // 清除token                                        app.globalData.token = null;                    wx.setStorageSync('userInfo', {}); // 清除用户数据                    app.globalData.userInfo = {};                    wx.switchTab({                        url: '/pages/user/user'                    });                    reject(res.data);                } else if (res.statusCode >= 500) {                    wx.showToast({                        icon: 'none',                        title: res.data.message || '网络异常',                        duration: 1500                    });                    reject(res.data);                }            },            fail: function () {                wx.showToast({                    icon: 'none',                    title: '网络异常',                    duration: 1500                });            }        });    })}

// app.js
const request = require('./utils/request.js');App({    onLaunch: function () {        this.request = request;    },    globalData: {        token: wx.getStorageSync('token'),        userInfo: wx.getStorageSync('userInfo'),        apiUrl: 'http://localhost:3300/api/'            }})// index.js
const app = getApp();app.request({
   method: 'GET',   url: '/test'}).then(res => {    // do something});复制代码

Promise finally

 

因为在某些接口请求前会加loading,防止多次点击,然后在接口的finally中取消loading即可,在开发工具中,一切正常,但是一到真机调试就会报错,查阅资料发现微信小程序不支持promise finally,太坑了,只能加个 polyfill 

canvas绘图

 

小程序一般都会有分享图片的需求,图片一般都带用户信息和小程序码,这时候就要用到canvas,而原生的canvas是如此难用和坑,所以想使用三方库,一开始试了html2canvas,

html2canvas

它确实很强大,可以直接获取dom绘制,但是微信小程序无法获取dom,引入之后直接报错,thirdScriptError Cannot read property 'document' of undefined TypeError: Cannot read property 'document' of undefined

html2canvas(document.body).then(function(canvas) {
    document.body.appendChild(canvas);
});复制代码

Painter

painter可以使用json数据描述绘图,不用繁琐操作canvas的一大坨函数,方便很多,基本能满足需求了。中间遇到一个小问题,开发工具里,绘图后保存到相册是有头像的,但是发小程序正式包后,真机操作时,绘图的头像就会丢失,一开始以为是painter的兼容问题,后来才发现是因为开发工具是开启了不校验合法域名的,但是线上包会校验合法域名,天真的以为微信自己的头像域名不用添加,结果并不是,微信连自己都不放过,在微信小程序后台添加下合法域名就好了 https://wx.qlogo.cn

获取小程序码

 

微信有3中方式获取小程序码

wxacode.createQRCode

获取小程序二维码,适用于需要的码数量较少的业务场景。通过该接口生成的小程序码,永久有效,有数量限制

wxacode.get

获取小程序码,适用于需要的码数量较少的业务场景。通过该接口生成的小程序码,永久有效,有数量限制

wxacode.getUnlimited

获取小程序码,适用于需要的码数量极多的业务场景。通过该接口生成的小程序码,永久有效,数量暂无限制

好像没有什么理由不选第三种吧

这3个接口返回的都是图片Buffer,我们需要做下处理,有2种方式

方式1:把图片存到服务器本地或转存到alioss等第三方对象服务器上,最后把地址返回给小程序前端

// 服务器
let readable;
let filePath = xxx; // 自己创建一个存图片的目录
const url = `https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=${access_token}`;const result = await axios.post(url, {                scene: `a=${xxx}` // 小程序码带的参数,比如带一个邀请码            }, {                 headers: {                    'Content-Type': 'application/json' // POST 参数需要转成 JSON 字符串,不支持 form 表单提交。                },                responseType: 'arraybuffer'             });            readable = result.data;            readable.pipe(fs.createWriteStream(filePath));复制代码

方式2:把Buffer转成base64返回给小程序前端,前端在转成图片存到小程序本地

// 服务器
const url = `https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=${access_token}`;            const result = await axios.post(url, {                scene: `a=${xxx}` // 小程序码带的参数,比如带一个邀请码            }, {                 headers: {                    'Content-Type': 'application/json' // POST 参数需要转成 JSON 字符串,不支持 form 表单提交。                },                responseType: 'arraybuffer'             });            const base64 = Buffer.from(result.data).toString('base64');
            // 返回前端            res.send({                success: true,                data: `data:image/jpg;base64,${base64}`            });

// 小程序前端
// base64src.js base64转图片存到小程序临时目录中
const fsm = wx.getFileSystemManager();const FILE_BASE_NAME = 'qrcode_base64src';const base64src = function(base64data) {  return new Promise((resolve, reject) => {    const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64data) || [];    if (!format) {      reject(new Error('ERROR_BASE64SRC_PARSE'));    }    const filePath = `${wx.env.USER_DATA_PATH}/${FILE_BASE_NAME}.${format}`;    const buffer = wx.base64ToArrayBuffer(bodyData);    fsm.writeFile({      filePath,      data: buffer,      encoding: 'binary',      success() {        resolve(filePath);      },      fail() {        reject(new Error('ERROR_BASE64SRC_WRITE'));      },    });  });};module.exports = base64src;// index.js
let base64Data = xxx; // 服务器返回的base64数据base64src(base64Data).then((src) => {                wx.getImageInfo({                    src: src,                    success: function (r) {                        console.log(r.path); // 图片本地路径                    },                    fail: function (r) {                        console.log(r);                    }                });            });

复制代码

自定义组件 behavior

 

有时候某些自定义组件会有大部分的相似功能,小部分差异,这时候可以使用behavior封装共同的属性和方法等,类似vue中的mixins。