小程序 & Taro

2021/12/29

小程序的官方开发文档日新月异,要想不踩坑则必须时刻关注官方文档!!!

# 小程序登录流程

  • 参考这篇文章 (opens new window)

  • 简单来说就是:1.先 wx.login()拿到 code,2.把 code 传给后端,后端拿到 code 后跟微信服务器通信,获得用户的 openid 和 sessionkey 等信息,3.再进行自定义登录流程即可。

  • 每个用户的 openid 是固定。后端跟微信服务器通信时也需要这个小程序的 appkey、appsecret 等信息,这是固定的不会变化。

# 获取用户设置然后获取授权拿到用户信息

Taro.getSetting({
	success(res) {
		if (!res.authSetting["scope.userInfo"]) {
			Taro.authorize({
				scope: "scope.userInfo",
				success() {
					console.log("getSetting", res);
				},
			});
		} else {
			// 必须是在用户已经授权的情况下调用
			Taro.getUserInfo({
				success(result) {
					console.log("getUserInfo", result);
					const userInfo = result.userInfo;
					setUserInfo({ ...userInfo });
					Taro.navigateTo({ url: "/pages/index/index" });
				},
			});
		}
	},
	fail(err) {
		Taro.showToast({
			title: `获取用户信息失败,请授权 ${err}`,
			icon: "none",
			duration: 2000,
		});
	},
});
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

# 获取写入图片权限

Taro.getSetting({
	success(res) {
		if (!res.authSetting["scope.writePhotosAlbum"]) {
			Taro.authorize({
				scope: "scope.writePhotosAlbum",
				success() {
					console.log("writePhotosAlbum", res);
				},
			});
		} else {
			// 必须是在用户已经授权的情况下调用
			// TODO save picture
			save2album();
		}
	},
	fail(err) {
		Taro.showToast({
			title: `获取用户信息失败,请授权 ${err}`,
			icon: "none",
			duration: 2000,
		});
	},
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 绘制 Canvas

  • 使用 Painter
<Painter
	palette={palette}
	onImgOK={onImgOK}
	onImgErr={onImgErr}
	customStyle="position:fixed; left:-9999rpx;"
/>;

/**
 * 保存图片到本地相册
 */
const save2album = () => {
	if (filePath !== "") {
		Taro.saveImageToPhotosAlbum({
			filePath,
			success(res) {
				Taro.showModal({
					title: "图片保存成功",
					content: "图片成功保存到相册了,快去发朋友圈吧~",
					showCancel: false,
					confirmText: "确认",
				});
			},
			fail(err) {
				Taro.showModal({
					title: "图片保存失败",
					content: "请重新尝试!",
					showCancel: false,
					confirmText: "确认",
				});
			},
		});
	} else {
		Taro.showToast({
			title: "获取图片地址失败",
			duration: 2000,
		});
	}
};
/**
 * Painter绘制canvas成功后的回调
 */
const onImgOK = (e) => {
	setFilePath(e.path);
};
/**
 * Painter绘制canvas失败后的回调
 */
const onImgErr = (e) => {
	Taro.showToast({
		title: "生成图片失败",
		duration: 2000,
	});
};
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

# Taro 保存图片

const saveCard = async () => {
	// 将Canvas图片内容导出指定大小的图片
	const res = await Taro.canvasToTempFilePath({
		x: 0,
		y: 0,
		width: 750,
		height: 1980,
		destWidth: 750,
		destHeight: 1980,
		canvasId: "cardCanvas",
		fileType: "png",
	});

	const saveRes = await Taro.saveImageToPhotosAlbum({
		filePath: res.tempFilePath,
	});

	if (saveRes.errMsg === "saveImageToPhotosAlbum:ok") {
		Taro.showModal({
			title: "图片保存成功",
			content: "图片成功保存到相册了,快去发朋友圈吧~",
			showCancel: false,
			confirmText: "确认",
		});
	} else {
		Taro.showModal({
			title: "图片保存失败",
			content: "请重新尝试!",
			showCancel: false,
			confirmText: "确认",
		});
	}
};
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

# SCSS/SASS/Less/Stylus/CSS 互相转换

网站 (opens new window)

  • eg.
bg-image($url)
  background-image: url($url + '@2x.png')
  @media (-webkit-min-device-pixel-ratio: 3), (min-device-pixel-ratio: 3)
    background-image: url($url + '@3x.png')
1
2
3
4
@mixin bg-image($url) {
	background-image: url($url + "@2x.png");
	@media (-webkit-min-device-pixel-ratio: 3), (min-device-pixel-ratio: 3) {
		background-image: url($url + "@3x.png");
	}
}
1
2
3
4
5
6

# 字体转换工具

  • 字体库转 base64

Here (opens new window)

# iPhone X+ 机型适配

  • 文章 (opens new window)

  • 最简单的方案即使用 Apple 官方推出的 css 函数 env()、constant()来适配,对于底部空白的部分可以自行加个 view。

/*兼容 IOS<11.2*/
padding-bottom: calc(108px + constant(safe-area-inset-bottom));
/*兼容 IOS>11.2*/
padding-bottom: calc(108px + env(safe-area-inset-bottom));
1
2
3
4
  • 配合 js 判断机型
import Taro from "@tarojs/taro";

// iphone X
const IPHONE_X = /iphone x/i;
// >= iPhone11
const IPHONE_X_11 = /iphone 1\d/i;
// 未适配机型-新机型
const IPHONE_UNKNOWN = /unknown\(iphone\)/i;

// 方法一:使用model判断是否是IPhoneX及其他包含安全区域的机型手机
export const isIpx = () => {
	const model = Taro.getSystemInfoSync().model;
	return (
		model.search(IPHONE_X) > -1 ||
		model.search(IPHONE_X_11) > -1 ||
		model.search(IPHONE_UNKNOWN) > -1
	);
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 按需注入

  • 降低小程序的启动时间和运行时内存
{
	"lazyCodeLoading": "requiredComponents"
}
1
2
3

# 全局注册组件

{
  usingComponents:{
    'keyboard':'components/../index',
  }
}
1
2
3
4
5

# 分享对话、朋友圈

useShareAppMessage(() => {
	return {
		title: "啦啦啦啦",
		imageUrl:
			"https://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eraEeftaBoduGGBHx3noVp5XQ8aYKwWAN74LqWR26mMhD8rTdXbGDxf3LpdYhO0BVtMZhHsg2VIBA/132",
		path: `/pages/index`,
	};
});

useShareTimeline(() => {
	return {
		title: "啦啦啦啦",
		imageUrl:
			"https://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eraEeftaBoduGGBHx3noVp5XQ8aYKwWAN74LqWR26mMhD8rTdXbGDxf3LpdYhO0BVtMZhHsg2VIBA/132",
	};
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# Taro3.x 版本中接入 Painter,实现图片导出功能

目前我在 Taro3.x+React+TypeScript 项目中通过如下方式可以使用,应该有更好的方法,该方法只是应急:

  1. 通过 submodule 把核心模块引入到项目中
git submodule add https://github.com/Kujiale-Mobile/PainterCore.git components/drawer
1
  1. 在 app.config.ts 中全局注册 Painter
  usingComponents: {
    'my-painter': 'components/drawer/painter',
  }
1
2
3
  1. 之后在.tsx 文件中直接可以使用
<my-painter
	palette={palette}
	onImgOK={onImgOK}
	onImgErr={onImgErr}
	customStyle={painterStyle}
	use2D
/>
1
2
3
4
5
6
7
  1. 观察编译后的小程序原生代码长这样:
<my-painter
	is="components/drawer/painter"
	bind:img-err="eh"
	bind:img-ok="eh"
	id="_n_256"
>
	...
</my-painter>
1
2
3
4
5
6
7
8

而 components/drawer/painter 里 getImageInfo(filePath)函数触发的是 imgOK,如下:

that.triggerEvent("imgOK", {
	path: filePath,
});
1
2
3

尝试把 "imgOK" 替换为 "img-ok",可以正常触发回调 "onImgOK",所以在回调 "onImgOK"里通过 e.detail.path 获取到生成的 canvas 图片地址 filePath,然后按照正常步骤保存图片即可。

// .tsx中的回调
const onImgOK = (e) => {
	console.log("onImgOK", e);
	setFilePath(e.detail.path);
};
1
2
3
4
5
  1. 对于 customStyle,一般不需要显示这个 canvas,所以设置为:const painterStyle = 'position:fixed; left:-9999rpx;';

  2. 剩下的就是把 palette 写出来。

# 如何配置项目,避免开发环境 appid 和生产环境 appid 管理使用混乱

Taro 官方貌似没有配置的教程,所以要么是手动改,要么也可以参考如下方案:

  1. 我们知道 Taro 对环境的配置文件放在 config/目录下,因此我们在 config 下(其他地方也可以)新建一个 updateConfig.js 文件

  2. 利用 nodejsfs 模块对文件进行读写。updateConfig.js 代码如下:

/**
 * 根据环境更新../project.config.json中的appid字段
 * @env NODE_ENV=development 将config/dev.js中的appid配置设置到../project.config.json中
 * @env NODE_ENV=production 将config/prod.js中的appid配置设置到../project.config.json中
 */

const fs = require("fs");
const path = require("path");
const DEV_CONFIG = require("./dev");
const PROD_CONFIG = require("./prod");

const { readFileSync, writeFileSync } = fs;

function updateProjectConfig(filePath) {
	const fileOption = { encoding: "utf-8" };
	const fileContent = readFileSync(filePath, fileOption);
	const config = JSON.parse(fileContent.toString());
	if (process.env.NODE_ENV === "production") {
		config.appid = PROD_CONFIG.appid;
		console.log(`[PROD] appId = ${config.appid}`);
	} else {
		config.appid = DEV_CONFIG.appid;
		console.log(`[DEV] appId = ${config.appid}`);
	}
	const newConfig = JSON.stringify(config, null, 2);
	writeFileSync(filePath, newConfig, fileOption);
}

updateProjectConfig(path.join(__dirname, "../project.config.json"));
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
  1. 把开发和生产的 appid 分别配置到 config/dev.jsconfig/prod.js 中;

  2. 然后再 package.json 中配置一下启动命令即可。例:

"scripts": {
    "start": "npm run dev:weapp",
    "build": "npm run build:weapp",
    "build:weapp": "taro build --type weapp",
    "dev:weapp": "npm run precond-weapp:dev && npm run build -- --watch",
    "precond-weapp:dev": "cross-env NODE_ENV=development node ./config/updateProjectConfig.js",
    "precond-weapp:prod": "cross-env NODE_ENV=production node ./config/updateProjectConfig.js",
    "build:dev": "npm run precond-weapp:dev && npm run build",
    "build:prod": "npm run precond-weapp:prod && npm run build"
},
1
2
3
4
5
6
7
8
9
10
  • 这样运行 npm start 即可在开发模式下进行开发,如果要在真机预览,则新建一个有 NODE_ENV=production 的命令,打包的时候压缩一下体积即可。
  • 对于生产的包,运行 npm run build:prod 即可得到。
  • --watch 开启热更新。

# 页面跳转之前进行「简单提示」的做法

useEffect(() => {
	Taro.eventCenter.once(getCurrentInstance()?.router?.onHide, () => {
		console.log("onHide");
	});
	Taro.eventCenter.on("beforegoback", () => {
		console.log("beforegoback");
	});
	Taro.disableAlertBeforeUnload();
	Taro.enableAlertBeforeUnload({
		message: "询问对话框内容询问对话框内容询问对话框内容询问对话框内容",
		success(res) {
			console.log(res);
			Taro.navigateBack({ delta: 1 });
		},
		fail(e) {
			console.log(e);
		},
	});
}, []);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# Hooks in Taro

  • 常用的除了 React 中的 hooks,还有 taro 封装的:useRouter,useReady,useDidShow,useDidHide,useShareAppMessage 等等

  • 文档链接 (opens new window)

上次更新: 8/25/2023