# 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. 注:一定要注册小程序的后台管理,否则将无法将项目推送到微信里面。下面是微信小程序后台管理系统的界面。 ![](images\微信小程序的后台管理.png) ## **三、小程序管理后台的基本操作** 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. 打开微信开发者工具,新增一个项目 ![](images\step1.jpg) 2. 填写项目对应的信息 **注意点1**:AppID,这个是在微信小程序后台管理找的。 **注意点2**:后端服务的可以先选择第二项:不使用云服务 ![](images\step2.jpg) ![](images\step3.jpg) 3. 项目的大概结构 ![](images\step4.jpg) ## 六、熟悉原生项目结构 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 等应用。