Skip to main content

小程序

前言

我对于小程序的态度,真的,其实是有些一言难尽。 就感觉代码的发展该是奔着一个开放的,公共的平台去的,而不是为了争夺用户而建立起自己的技术壁垒。 这种争斗让我想起互联网发展初期的各大浏览器厂商的互相争斗。

反正各种原因(也不太想多说),个人并不怎么看好小程序的开发生态。

但在另一个层面讲,小程序确实也解决了一部分痛点。 技术层面: 小程序的底层是应用的 hybrid 技术,它和 h5 差不多,但又多了一些(类似 RN, flutter 之类的)底层能力。 产品层面: 用户可以不用安装那些【庞大的包】,就能解决自己一部分问题。 (当然付出的代价,也许是宿主包的日益繁杂 🤐) 其次,在国内的市场环境,微信、抖音、支付宝的流量太大了,谁又愿意放弃这块市场呢?(我也不愿意呀)

小程序框架

国内比较火的小程序框架该是 Tarouni-app 了。 一般团队用 React 技术栈的话, Taro 的会比较多,用 Vue 的话,就 uni-app 比较多。 不过 Taro3.0+是支持 Vue 的~(我没用过 Taro + Vue 的模式) 这里就介绍一下 Taro+React 和 uni-app + Vue

Taro + React

uni-app + vue

Taro

文件上传

const getImageBase64ByPath = (tempFilePath: string) =>
new Promise((resolve) => {
//获取全局唯一的文件管理器
Taro.getFileSystemManager().readFile({
//读取本地文件内容
filePath: tempFilePath, // 文件路径
encoding: 'base64', // 返回格式
success: ({ data }) => {
return resolve('data:image/png;base64,' + data);
},
fail(res) {
console.log('fail', res);
}
});
});
export const getImgFileByBase64 = (dataUrl: string): File => {
const validUrl = dataUrl.startsWith('data:image') ? dataUrl : `data:image/jpeg;base64,${dataUrl}`;
// 将base64按照 , 进行分割 将前缀 与后续内容分隔开
const data = validUrl.split(',');
// 利用正则表达式 从前缀中获取图片的类型信息(image/png、image/jpeg、image/webp等)
const type = data[0]?.match(/:(.*?);/)?.[1];
// 从图片的类型信息中 获取具体的文件格式后缀(png、jpeg、webp)
const suffix = type?.split('/')?.[1];
const fileName = new Date().getTime();
// 使用atob()对base64数据进行解码 结果是一个文件数据流 以字符串的格式输出
const bstr = weAtob(data[1]);
// 获取解码结果字符串的长度
let n = bstr.length;
// 根据解码结果字符串的长度创建一个等长的整形数字数组
// 但在创建时 所有元素初始值都为 0
const u8arr = new Uint8Array(n);
// 将整形数组的每个元素填充为解码结果字符串对应位置字符的UTF-16 编码单元
while (n--) {
// charCodeAt():获取给定索引处字符对应的 UTF-16 代码单元
u8arr[n] = bstr.charCodeAt(n);
}
// 利用构造函数创建File文件对象
// new File(bits, name, options)
return new File([u8arr], `${fileName}.${suffix}`, {
type
});
};

const b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
const b64re = /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/;

export const weAtob = function (string: string) {
string = String(string).replace(/[\t\n\f\r ]+/g, '');
if (!b64re.test(string)) {
console.log('error', "Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.");
}
string += '=='.slice(2 - (string.length & 3));
let bitmap;
let result = '';
let r1;
let r2;
let i = 0;
for (; i < string.length; ) {
bitmap = (b64.indexOf(string.charAt(i++)) << 18) | (b64.indexOf(string.charAt(i++)) << 12) | ((r1 = b64.indexOf(string.charAt(i++))) << 6) | (r2 = b64.indexOf(string.charAt(i++)));

result +=
r1 === 64
? String.fromCharCode((bitmap >> 16) & 255)
: r2 === 64
? String.fromCharCode((bitmap >> 16) & 255, (bitmap >> 8) & 255)
: String.fromCharCode((bitmap >> 16) & 255, (bitmap >> 8) & 255, bitmap & 255);
}
return result;
};

小程序里目前(2023)还不支持 file 类型。 除非是 webView~ 所以上传图片的时候选择 base64 上传吧。 也可以让后端直接调用微信提供的 api,然后我们就可以使用这个方式,通过 tempFilePath 来上传。 前端上传方法 后端上传方法

wx.chooseImage({
success(res) {
const tempFilePaths = res.tempFilePaths;
wx.uploadFile({
url: 'https://example.weixin.qq.com/upload', //仅为示例,非真实的接口地址
filePath: tempFilePaths[0],
name: 'file',
formData: {
user: 'test'
},
success(res) {
const data = res.data;
//do something
}
});
}
});

formData形式上传

const createFormData = (params: { [key: string]: any } = {}, boundary = "") => {
console.log({ params });
let result = "";
for (let i in params) {
result += `\r\n--${boundary}`;
if (i === "file") {
console.log(params[i]);
result += `\r\nContent-Disposition: form-data; name="${i}"; filename="${params["fileName"]}"`;
result += `\r\nContent-Type: ${params[i]?.type}`;
result += "\r\n";
result += "\r\n";
} else {
result += `\r\nContent-Disposition: form-data; name="${i}"`;
result += "\r\n";
result += `\r\n${params[i]}`;
}
}
// 如果obj不为空,则最后一行加上boundary
if (result) {
result += `\r\n--${boundary}--`;
}
console.log({ result });
return result;
};

const getBoundary = () => `----WebKitFormBoundary${new Date().getTime()}`;

export const fileUpload = (file: string | ArrayBuffer, filePath: string) => {
const boundary = getBoundary();
const typeParse = filePath.split(".");
const type = typeParse[typeParse.length - 1];
return service.post(
`/api/XXX/upload`,
createFormData({ file, type, fileName: filePath }, boundary),
`multipart/form-data; boundary=${boundary}`
);
};