# Applet
**Repository Path**: GPdreaming/applet
## Basic Information
- **Project Name**: Applet
- **Description**: Make a wechat applet
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2021-10-16
- **Last Updated**: 2021-10-17
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
## **一、初识微信小程序**
1. 微信小程序,小程序的一种,英文名Wechat Mini Program,是一种不需要下载安装即可使用的应用,它实现了应用“触手可及”的梦想,用户扫一扫或搜一下即可打开应用。
2. 微信小程序是一种不用下载就能使用的应用,也是一项创新,经过将近两年的发展,已经构造了新的微信小程序开发环境和开发者生态。微信小程序也是这么多年来中国IT行业里一个真正能够影响到普通程序员的创新成果,已经有超过150万的开发者加入到了微信小程序的开发,与我们一起共同发力推动微信小程序的发展,微信小程序应用数量超过了一百万,覆盖200多个细分的行业,日活用户达到两个亿,微信小程序还在许多城市实现了支持地铁、公交服务。微信小程序发展带来更多的就业机会,2017年小程序带动就业104万人,社会效应不断提升。
3. 小程序为什么存在?为企业或个人提供便利的用户连接工具;它可以在一定程度上可以替代掉部分手机APP的作用(用完即走) 。
4. 产品设计标准:小而美、开发周期较短。
5. [张小龙亲自“引爆”微信小程序(附演讲全文)](https://links.jianshu.com/go?to=https%3A%2F%2Fwww.sohu.com%2Fa%2F122892706_480349)
## **二、微信小程序开发前准备**
1. [翻阅微信小程序官方文档](https://links.jianshu.com/go?to=https%3A%2F%2Fdevelopers.weixin.qq.com%2Fminiprogram%2Fdev%2Fframework%2F)
2. [下载、安装“微信者开发工具”](https://links.jianshu.com/go?to=https%3A%2F%2Fdevelopers.weixin.qq.com%2Fminiprogram%2Fdev%2Fdevtools%2Fstable.html)
3. [注册一个小程序账号(管理后台)](https://links.jianshu.com/go?to=https%3A%2F%2Fmp.weixin.qq.com%2Fcgi-bin%2Fregistermidpage%3Faction%3Dindex%26lang%3Dzh_CN%26token%3D)
4. [微信小程序注册官网注册流程](https://kf.qq.com/faq/170109iQBJ3Q170109JbQfiu.html)
5. 注:一定要注册小程序的后台管理,否则将无法将项目推送到微信里面。下面是微信小程序后台管理系统的界面。

## **三、小程序管理后台的基本操作**
1. 版本管理
- 小程序认证:填写基本信息、注意选择行业类目、备案付费300元。
- 小程序有三个版本:开发版、审核版、线上版(默认代码体积不能超过2M)。
- 小程序项目中用到的静态资源,可以放到CDN上,以减小代码体积。
2. 成员管理
- 管理员(1人),是注册账号的微信用户。
- 项目成员(15人),可以登录小程序管理后台,开发者必须是项目成员。
- 体验成员(15人),只有体验的权限,没有开发的权限。
3. 开发管理
- AppID,相当是小程序的身份证号码,创建项目、调试项目、小程序之间的跳转都要用到,还有比如支付等也要用到。
- AppSecret,小程序密钥,一般要给后端,在登录、支付等功能中都要用到。
- Request 地址,就是api 的 baseURL,本地开发时可以关闭https验证,上线时一定要小程序管理后台中添加上这个地址,并且要求https协议的。
## **四、微信开发者工具的基本使用**
- 如何创建新项目?
- 如何导入旧项目?
- 调试项目(真机调试、微信开发者工具调试)
- 如何上传代码至开发版?
- 关闭“开发环境下校检 https”的限制
- 注意小程序api 版本库和微信版本之间兼容性问题。
## 五、搭建项目结构
[参考链接](https://blog.csdn.net/weixin_45522071/article/details/106391359)
1. 打开微信开发者工具,新增一个项目

2. 填写项目对应的信息
**注意点1**:AppID,这个是在微信小程序后台管理找的。
**注意点2**:后端服务的可以先选择第二项:不使用云服务


3. 项目的大概结构

## 六、熟悉原生项目结构
1. 项目名称:大学生心理测试 (CollegePsychologyTest)
2. 项目文件讲解:
a. miniprogram:编写小程序的核心文件,所有的页面和逻辑都是存放在这个文件中。
b. typings:引入TypeScript的语法,编写程序需要按照强类型的写法。
c. .eslintrc.js: 严格控制编写程序的规范文件
d. package.json: 重点文件,是记录项目第三方引入包的文件
e. project.config.json: 小程序的基本配置文件
f. tsconfig.json: ts语法的配置项文件
3. miniprogram文件讲解:
a. pages: 程序中所有的页面都统一放置在此文件中,便于维护和管理
b. utils: 小程序要使用的工具类方法
c. app.json:配置小程序的路由文件,具体点配置底部tabbar的配置文件
d. app.scss:类似于css文件
e. app.ts: 编写逻辑代码的地方
f. sitemap.json: 官方的一些信息
4. 认识四种文件
- .wxml,类似 HTML 的角色。
- .wxss,具有 CSS 大部分的特性,小程序在 WXSS 也做了一些扩充和修改。
- .js 和 .ts,编写脚本与用户交互,响应用户的点击、获取用户的位置等等。
- .json,是一种数据格式,并不是编程语言,在小程序中,JSON扮演的静态配置的角色。
## 七、引入**Taro**框架开发小程序
其实微信官方文档是有推出编写小程序的写法,但是原生的写法比较苦涩,建议使用有京东推出的Taro框架。
**大学生心理测试小程序技术选型**
**前端技术选型**
- 选用Taro框架
- 语法风格:React
- css预处理器:Sass
- 代码强类型限制:TypeScript
- 数据管理:mobx
**后端技术选型**
1、在引入Taro框架前,需要阅读[Taro的官方文档](https://taro-docs.jd.com/taro/docs/GETTING-STARTED),具体的演示步骤官网比较清除。
2、创建的新的Taro框架的项目:CollegePsychologicalTest
```js
创建一个Taro矿建的项目: taro init CollegePsychologicalTest
```
## **自带配置风格的小程序**
微信小程序根目录下的 `app.json` 文件用来对微信小程序进行全局配置,决定页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 等。
```json
{
"pages": [
"pages/index/index",
"pages/books/books"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "好程序员",
"navigationBarTextStyle": "black"
},
"tabBar": {
"color": "#aaaaaa",
"selectedColor": "#ff0000",
"list": [
{
"pagePath": "pages/index/index",
"text": "福利",
"iconPath": "/assets/tabbar/index.png",
"selectedIconPath": "/assets/tabbar/index-on.png"
},
{
"pagePath": "pages/books/books",
"text": "书城",
"iconPath": "/assets/tabbar/book.png",
"selectedIconPath": "/assets/tabbar/book-on.png"
}
]
}
}
```
## **App类与应用级别的生命周期**
注册小程序。接受一个 `Object` 参数,其指定小程序的生命周期回调等。`App()` 必须在 `app.js` 中调用,必须调用且只能调用一次。不然会出现无法预期的后果。
```jsx
App({
// 整个应用程序的入口
onLaunch() {
wx.login({
success: res => console.log('登录', res.code)
})
},
globalData: {
userInfo: null,
}
})
```
## Page类与页面级别的生命周期**
```jsx
Page({
data: { },
onLoad: function (options) { },
onReady: function () {},
onShow: function () { },
onHide: function () {},
onUnload: function () {},
onPullDownRefresh: function () { },
onReachBottom: function () { },
onShareAppMessage: function () { }
})
```
## **Component类与组件级别的生命周期**
```jsx
Component({
properties: { }, // 父组件传递过来的属性
data: { }, // 自有的状态数据
ready: function(){ }, // 生命周期
methods: { } // 自有的方法
})
```
## **10、自定义`Cate`组件**
```csharp
# cate.wxml
{{item.cate_zh}}
```
```css
# cate.wxss
.cate_item {
padding: 0 10rpx;
display: inline-block;
border: 1rpx solid #ccc;
line-height: 45rpx;
}
.cate_item.on {
color: red;
}
```
```csharp
# cate.js
Component({
properties: {
list: {type: Array, value: [] },
value: {type:String, value: ''}
},
methods: {
tapClick(e) {
// 父子组件事件通信
// this.triggerEvent('input', e.target.dataset.cate)
// model 双向绑定的写法
this.setData({value: e.target.dataset.cate})
}
},
// 生命周期
lifetimes: {},
pageLifetimes: {}
})
```
```bash
# cate.json
{
"component": true
}
```
## **11、使用自定义封装的`Cate`组件**
```bash
# study.json
{
"usingComponents": {
"qf-cate": "/components/cate/cate"
}
}
```
```xml
```
## 12、列表渲染
```xml
{{idx+1}}
{{act.id*100}}
{{act.title}}
```
## 13、条件渲染
```xml
第一行文字
第二行文字
第三行文字
第四行文字
第十行文字
```
## **14、动态样式**
```jsx
动态样式文字
```
## **15、事件绑定(bind / catch)**
```xml
测试事件(bind)
测试事件(catch绑定)
```
```jsx
Page({
data: {},
click1(e) {
console.log('click1', e)
console.log('arg', e.target.dataset.msg)
},
click2(e) {
console.log('click2', e)
},
clickOuter(e) {
console.log('outer', e)
}
})
```
## **16、表单绑定(单向绑定、双向绑定)**
```xml
```
```kotlin
Page({
data: {
username: '小明',
password: '123',
},
// 手动取值
usernameChange(e) {
console.log('e', e.detail.value)
this.setData({username: e.detail.value})
},
submit() {
const data = {
username: this.data.username,
password: this.data.password
}
console.log('提交', data)
}
})
```
## **17、微信小程序动画(最新写法)**
```jsx
```
```tsx
Page({
data: { },
startAnim() {
// 创建动画
// 第1参数是节点选择器
// 第2个参数是动态帧(数组)
// 第3个是过滤时间
// 第4个参数是回调参数,用于清除动画,这是一种性能优化方案
this.animate('.abc', [
{ opacity: 1.0, rotate: 0, backgroundColor: '#FF0000', scale: [1,1] },
{ opacity: 0.5, rotate: 45, backgroundColor: '#00FF00', scale: [0.6,0.6]},
{ opacity: 0.2, rotate: 80, backgroundColor: '#FF0000',scale: [0.2,0.2] },
], 5000, ()=> {
// 清除动画
// 它的第二个参数,用于控制动画的样式是否保留最后一帧的样式,默认是保留的
this.clearAnimation('.abc', {
opacity: false,
rotate: false,
// backgroundColor: false
}, ()=> {
console.log("清除动画成功")
})
})
}
})
```
## **18、使用Canvas画布(最新写法)**
```jsx
```
```kotlin
Page({
data: { },
rpx2px(arg) {
const res = wx.getSystemInfoSync()
return res.screenWidth * arg / 750
},
// 用于支持最新drawImage()写法
async getImage(path) {
const c = wx.createOffscreenCanvas({type: '2d'})
const image = c.createImage()
await new Promise(resolve => {
image.onload = resolve
image.src = path
})
return image
},
onReady() {
const $ = wx.createSelectorQuery()
$.select('.ad')
.fields({ node: true, size: true })
.exec((res) => {
this.canvas = res[0].node
const ctx = canvas.getContext('2d')
// 第1步,绘制一个和画布一样大的矩形
ctx.fillStyle = 'orange'
ctx.fillRect(0, 0, this.rpx2px(750), this.rpx2px(500))
// 第2步,绘制标题文字
ctx.font = "bold 25px serif"
ctx.fillStyle = 'white'
ctx.fillText('樱桃小丸子很可爱', this.rpx2px(0), this.rpx2px(50))
// 第3步,绘制图片
this.getImage('/assets/girl.png').then(img=>{
ctx.drawImage(img,50,50)
})
})
},
// 保存到相册
save() {
// 把canvas画布转换成临时路径
wx.canvasToTempFilePath({
x: 0,
y: 0,
width: this.rpx2px(750),
height: this.rpx2px(500),
destWidth: 1500,
destHeight: 1000,
canvas: this.canvas,
success(res) {
// 把临时路径中的图片保存到相册
wx.saveImageToPhotosAlbum({
filePath: res.tempFilePath
})
}
})
}
})
```
## **19、小程序初次启动时请求用户授权地理定位**
```tsx
# app.js
App({
onLaunch() {
// 获取用户的地理定位的授权
wx.getSetting({
success(res) {
console.log('当前用户的授权列表', res.authSetting)
// 如果用户未授权地理定位
if (!res.authSetting['scope.userLocation']) {
// 无须用户触发,直接弹框请求用户同意使用地理定位
// 当用户拒绝过一次后,这段代码没用了
wx.authorize({
scope: 'scope.userLocation',
success (res) {
console.log('地理定位:用户点击了同意', res)
},
fail (err) {
console.log('地理定位:用户点击了拒绝', res)
}
})
}
}
})
}
})
```
```bash
# app.json
{
"pages": [],
"permission": {
"scope.userLocation": {
"desc": "为了给你更好的服务,希望使用你的地理定位"
}
}
}
```
## **20、使用地理定位**
```xml
```
```kotlin
Page({
data: {
latLng: {}
},
getLngLat() {
var that = this
wx.getSetting({
success(res){
if(res.authSetting['scope.userLocation']) {
// 如果用户已经同意过地理定位,直接获取经纬度
wx.getLocation({
success(res) {
console.log('用户位置', res)
that.setData({latLng: res})
}
})
}else{
// 如果用户已经拒绝过地理定位,我们要打开微信内置的设置页面,让用户自己选择授权
wx.openSetting({
success(res) {
console.log('用户手动选择授权', res)
}
})
}
}
})
},
navTo() {
wx.openLocation({
latitude: this.data.latLng.latitude,
longitude: this.data.latLng.longitude,
name: '深圳',
address: '广东省深圳市'
})
}
})
```
## **21、onShareAppMessage 实现转发**
```xml
```
```kotlin
Page({
data: {},
// 实现转发的两种方案(胶囊按钮、button.open-type='share')
onShareAppMessage(e) {
console.log('转发', e)
if(e.from==='menu') {
return {
title: '招聘年薪百万',
path: 'pages/listen/listen',
imageUrl: 'https:70.jpg.webp'
}
}else if(e.from==='button') {
return {
title: '我正在拼团...',
path: 'pages/listen/listen',
imageUrl: 'https://img20.0.jpg.webp'
}
}
}
})
```
## **22、globalData 全局数据**
```css
App({
globalData: { msg: 'hello' }
})
```
```kotlin
const app = getApp()
Page({
data: {
msg: app.globalData.msg
},
updateMsg() {
app.globalData.msg = '456'
this.setData({msg: app.globalData.msg})
}
})
```
```xml
{{msg}}
```
## **23、onPageScroll 监听页面滚动**
```xml
```
```jsx
App({
// 页面滚动
onPageScroll(e) {
console.log('页面滚动', e.scrollTop)
},
// 使用scrollTop滚动到页面的任何位置
backToTop() {
wx.pageScrollTo({
scrollTop: 133,
duration: 2000
})
}
})
```
## **24、 实现媒体查询**
```xml
根据媒体设备来决定我是否显示
```
## **25、 实现拖拽**
```jsx
text
```
```kotlin
Page({
data: {
point: {x:0,y:0}
},
areaChange(e) {
this.setData({point: e.detail})
}
})
```
## **26、功能极其强大的 表单组件**
```xml
```
```jsx
Page({
data: {},
getMobile(e) {
// 目前已不支持个人版本的小程序
console.log('获取手机号', e)
},
// 获取用户信息,会弹框请求用户授权
// 即将过时,建议使用wx.getUserProfile获取用户信息
getUserInfo(e) {
console.log('获取用户信息', e)
},
getUser() {
wx.getUserProfile({
desc: '用于完善会员资料',
success(e) {
console.log('最新的用户信息', e)
// 拿到用户信息之后,要调接口发送给后端数据库
// 把用户保存在业务数据库
}
})
}
})
```
## **27、使用 组件选择省市区**
```jsx
省市区选择器
当前选择:{{region[0]}},{{region[1]}},{{region[2]}}
```
```kotlin
Page({
data: {
region: ['广东省', '广州市', '海珠区'],
customItem: '全部',
},
bindRegionChange(e) {
console.log('region picker', e)
this.setData({region: e.detail.value})
}
})
```
## **28、 组件使用再举例**
```jsx
选择品类
当前选择:{{cateArr[cateIdx].cate_zh}}
```
```jsx
Page({
data: {
cateArr: [
{id:0,cate:'all',cate_zh:'全部'},
{id:1,cate:"car",cate_zh:"汽车生活"},
{id:1,cate:"office",cate_zh:"办公用品"}
],
cateIdx: 0
},
bindCateChange(e) {
console.log('cate picker', e)
this.setData({cateIdx: parseInt(e.detail.value)})
}
})
```
## **29、使用 音频组件**
```xml
```
```jsx
Page({
startPlay() {
const audioCtx = wx.createInnerAudioContext()
console.log('ctx', audioCtx)
audioCtx.play()
}
})
```
## **30、使用 相机组件**
```xml
```
```jsx
Page({
data: {avatar:''},
takeCamera() {
const ctx = wx.createCameraContext()
ctx.takePhoto({
quality: 'high',
success: (res) => {
this.setData({
avatar: res.tempImagePath
})
}
})
}
})
```
## **31、小程序路由跳转**
```xml
```
```csharp
Page({
// 跳转到Tab页,使用switchTab,不能传参
skipToTabPage() {
wx.switchTab({
url: '/pages/listen/listen'
})
},
// 跳转到非Tab页,使用navigateTo,可以传参
// 在另一个页面中,使用 onLoad 生命周期来接收参数
skipToNotTabPage() {
wx.navigateTo({
url: '/pages/user/user?id=123&name=abc'
})
}
})
```
## **32、自定义ActionSheet**
```xml
```
```jsx
Page({
selectMethod() {
wx.showActionSheet({
itemList: ['使用积分兑换', '直接支付'],
success (res) {
console.log('用户选择的兑换方式是:', res.tapIndex)
}
})
}
})
```
## **33、使用小程序的功能 API**
```xml
```
```bash
Page({
testWXApi() {
wx.setNavigationBarTitle({title:'1234'})
wx.setBackgroundColor({ backgroundColor: '#ff0000' })
wx.hideTabBar()
}
})
```
## **34、从手机相册中选取照片**
```xml
```
```jsx
Page({
selectAvatar() {
// 可以先使用wx.getSetting先判断当前用户是否有访问相机和相册的权限
wx.chooseImage({
count: 9,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success (res) {
// tempFilePath可以作为img标签的src属性显示图片
const tempFilePaths = res.tempFilePaths
console.log('tempFilePaths', tempFilePaths)
}
})
}
})
```
## **35、微信小程序支付(伪代码)**
```xml
```
```cpp
Page({
pay () {
// 前提:先开通微信支付平台(收款平台),小程序要认证
// 1、在小程序管理后台绑定已有的支付账号
// 2、使用wx.request把订单信息发送给业务服务器,后端会返回支付信息
// 3、使用wx.requestPayment()请求完成支付
}
})
```
## **36、小程序中实现复制黏贴、打电话、扫码功能**
```xml
订单号:QF20120902093203023
```
```go
Page({
// 复制黏贴
copy() {
wx.setClipboardData({
data: 'QF20120902093203023'
})
},
call() {
wx.makePhoneCall({
phoneNumber: '0755-89890909'
})
},
scan() {
wx.scanCode({
scanType: 'barCode',
success(res){
console.log('扫码结果', res)
}
})
}
})
```
## **37、下拉刷新与触底加载**
```jsx
Page({
// 触底加载
onReachBottom() {
console.log('到底了,我准备调接口')
},
// 下拉刷新
onPullDownRefresh() {
console.log('正在下拉刷新')
setTimeout(()=>{
// 当调接口完成时,手动停止掉下拉刷新
wx.stopPullDownRefresh()
}, 1000)
},
})
```
```bash
# .json 局部配置
{
"navigationStyle": "custom",
"onReachBottomDistance": 100,
"enablePullDownRefresh": true
}
```
## **38、Request 封装**
```jsx
const baseUrl = 'http://localhost:8888'
const version = '/api/v1'
module.exports = function(url,method,data) {
return new Promise(function(resolve, reject){
wx.request({
url: baseUrl+version+url,
data,
method,
header: {
Authorization: wx.getStorageSync('token')
},
success (res) {
// 成功后数据过滤
resolve(res.data.data)
},
fail(err) {
wx.showToast({title:'网络异常'})
reject(err)
}
})
})
}
```
## **39、微信小程序的登录流程**
```tsx
# app.js
const api = require('./utils/api')
App({
// 整个应用程序的入口
onLaunch() {
// 登录
wx.login({
success: res => {
console.log('登录', res.code)
// 使用 wx.request 调接口,用code换取token
api.fetchLogin({code: res.code}).then(res=>{
// console.log('登录token', res)
wx.setStorageSync('token', res.token)
})
}
})
}
})
```
```tsx
# Node.js + Koa 代码示例接口
const axios = require('../utils/axios')
const jwt = require('../utils/jwt')
// 引入model
const userModel = require('../model/user')
class UserController {
// 登录接口
static async login(ctx) {
// 接收入参
console.log('post body', ctx.request.body)
let { code } = ctx.request.body
console.log('code', code)
// 第1步,用code+appid+secret换取微信服务器的openid+session-key
const res = await axios({
url: '/sns/jscode2session',
method: 'get',
params: {
appid: 'w2x2f8b3892386e5e2ccf',
secret: '345bc1b923ae7423bbf28146e31ff372e',
js_code: code,
grant_type: 'authorization_code'
}
})
// 第2步,openid不能重复入库
const list = await userModel.find({openid: res.openid})
if(list.length > 0) {
const token = jwt.createToken(res)
ctx.body = {
err: 0,
msg: 'success',
data: { token }
}
}else{
const ele = {
openid: res.openid,
}
await userModel.insertMany([ele])
const token = jwt.createToken(res)
ctx.body = {
err: 0,
msg: 'success',
data: { token }
}
}
}
}
module.exports = UserController
```
## **40、写在最后**
微信小程序原生写法比较麻烦,推荐使用 `Taro 3.0`开发微信小程序,它不仅支持 React风格、也支持 Vue 和 Vue3.0 的语法风格。
`Taro` 是一个开放式跨端跨框架解决方案(由京东凹凸实验室开源),支持使用 React/Vue/Nerv 等框架来开发 微信、京东、百度、支付宝、字节跳动、QQ小程序、H5、RN 等应用。