### [放心,手把手教你写微信小程序](https://www.prosaas.cn/article/16519) **Published:** 2020-10-22T10:56:58 **Author:** 5201314 **Excerpt:** 微信小程序开发栏目今天详细教大家写微信小程序。小程序的历史介绍什么是微信小程序?微信小程序,简称小程序。英文名… # 微信小程序开发栏目今天详细教大家写微信小程序。 ![](https://img.php.cn/upload/article/000/000/052/5f9164f6d4303152.jpg) # 小程序的历史介绍 ## 什么是微信小程序? 微信小程序,简称小程序。英文名`mini program`,是一种不需要下载安装就可以直接使用的应用。他实现了触手可及的梦想。用户扫一扫或搜一下就可以直接打开应用。 ## 为什么是微信小程序 1. 微信有海量用户 2. 推广app或公众号成本太高 3. 开发适配成本低 4. 容易小规模试错,然后快速迭代 5. 跨平台 ## 历史 1. 2016年1月11日,张小龙,微信内部研究新的形态,`应用号`,后改名`小程序`。 2. 2016年8月12日,开始内测 3. 2017年1月9日,上线 #环境规范 1. 注册账号 mp.weixin.qq.com/  (帐号信息 --- 邮箱激活 --- 信息登记) 2. 获取id APPID (\[登录微信公众平台\](https://mp.weixin.qq.com/wxamp/devprofile/get\_profile?token=942994743&lang=zh\_CN) ---> 开发 ---> 开发设置)复制代码 ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/25d7088598ce45b3a304a999018c1fe9~tplv-k3u1fbpfcp-zoom-1.image) 3. 开发者工具 微信开发者工具下载 # 小程序开发者工具     开发者工具介绍     快捷键: 1. ctrl + shift + F (搜索) 2. alt + shift + F (代码格式化---VSCode)复制代码 ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6e8eee16d0ca4e529f078e6ef2b553e9~tplv-k3u1fbpfcp-zoom-1.image) # 小程序原生框架 小程序的原生框架,mina框架   框架详情 ## 小程序配置文件(写配置文件在微信开发者工具工具写,有提示) 1. app.json   全局配置文件 配置全局文件 \* pages:添加要创建的文件项,保存后就会自动生成文件 \* \[windows\](https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/page.html):设置小程序的状态栏,导航条,标题窗口颜色复制代码 ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/920738a3ebb54b7aae722ee957f01039~tplv-k3u1fbpfcp-zoom-1.image) \* \[tabBar\](https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html)复制代码 ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4e4e475460ba4aa3b039a57c34700913~tplv-k3u1fbpfcp-zoom-1.image) 2. page.json   页面配置文件 3. sitemap # 小程序的模板语法 WXML   --->   HTML   (结合基础组件,事件系统,构件出页面结构) 1. 相当于 ,行内标签,不会换行 2. 相当于,块级元素,会换行 ## 数据绑定 {{ 数据 }} 1. 运算 --> 表达式( 数值计算,字符串拼接,三元表达式) 2. 列表循环 (wx:for) wx:key绑定的是数组中的为唯一属性,wx:key=_this表示数组是`普通数组`,\`_this\`是循环项     索引: {{ index }}     名称: {{ item.name }}复制代码 3. 标签   --->   占位标签 4. 条件渲染(wx:if)(wx:if, wx:elif, wx:else) (hidden  属性是通过添加样式的方式来呈现的) 当标签不是频繁的切换使用if,频繁切换使用hidden ## 事件绑定 关键字:bind (bindinput,bindtap【点击事件】) 获取事件源对象的值:  e.detail.value复制代码 获取data中数据的值:  this.data.属性名复制代码 将事件源对象的值设置回data中:  this.setData({         num: this.data.num + operation });复制代码 事件绑定是不能直接传参,要通过自定义属性的方式传参( {{ 传递的参数}} ):      bandletap(e) {     // console.log(e);     const operation = e.currentTarget.dataset.operation;     this.setData({         num: this.data.num + operation     });   },复制代码 ## 样式 ### 尺寸单位 当屏幕宽度等于 750px 时,1px = 1rpx 当屏幕宽度等于375px时, 1px =0.5rpx ### 样式导入只支持相对路径 ### 选择器(微信小程序不支持通配符) # 小程序的内置组件 小程序中常用的布局组件: view,text,rich-text,button,image,icon,swiper,radio,checkbox等。复制代码 1. view标签  相当于  p标签 2. text标签 只能嵌套text标签  长按文字可以复制【selectable】(只有这个标签有这个功能) 可以对回车,空格进行编码 (decode) 3. image标签 (打包上线的大小不能超过2M,使用图片的时候统一使用外网图片) 1. 图片存在默认的宽高(320px \* 240px) 2. mode 决定 图片内容 和 图片标签 做适配 scaleToFill  默认值   不保持纵横比,拉伸至标签定义的宽高 aspectFit  保持宽高比,保证图片的长边完全显示(常用 轮播图) aspectFill    短边完全显示 widthFix   宽度不变,高度自动变化,保持原宽高比不变 top,left,bottom,right   背景图定位 3. 小程序中的图片  直接支持 懒加载 lazy-load 会自己判断  当图片出现在视口的上下三屏之内的时候,自己开始加载图片 4. swiper标签   ---》 轮播图 swiper高度  =  swiper的宽度  \*  图片的高度  /  原图的宽度     // 图片存在默认宽高  320 \* 240    复制代码 5. navigator  导航组件 (块级元素,默认换行) 复制代码 6. rich-text(富文本标签,将字符串解析成对应标签,相当于v-html) // 1 标签字符串// 2 对象数组复制代码 7. button  按钮 大小(size:mini/default),颜色(type:default/primary/warn),是否镂空(plain),是否在文字前有加载loading(loading),开发能力(opentype) 开放能力(opentype): 1. concat 直接打开  客服对话  功能,需要在小程序的后台配置 1. 将小程序的appid由测试号改为自己的appid 2. 登录微信小程序官网,添加  客服  -   微信 2. share 转发当前小程序到微信朋友中 ,不能把小程序转发到朋友圈中 3. getPhoneNumber 获取当前用户的手机号码,结合事件来使用,不是企业的小程序账号 没有权限来获取用户的手机号码 4. getUserInfo获取用户的个人信息 5. launchApp在  小程序   中直接打开 app 6. openSetting 打开小程序内置的授权页面 只会出现用户点击过的权限 7. feedback   打开小程序内置的意见反馈页面 8. icon type:类型 success,success\_no\_circle,info,warn,wating.success\_no\_circle,info,warn,waiting,cancel,downkload,search,clear size:大小   number /  string color:颜色 9. radio 单选框     男     女选中的是: {{ gender }} data:{     gender: "" }, handleChange(e) {     // 获取单选框选中的值     let gender = e.detail.value;     // 把值赋值给data中的数据     this.setData({         gender // 相当于 gender:gender     }) }复制代码 10. checkbox  多选框     {{ item.name }}           选中的是: {{ checkedList }} checkedList:\[\] handleChange(e) {     let checkedList = e.detail.value;     this.setData({         checkedList     }) }复制代码 # 小程序的生命周期 ## 应用生命周期 触发过程: onLaunch    -》 onShow App({    // 1 应用 第一次启用的时候触发     onLaunch() {        // 在应用第一次启动的时候 获取用户信息     }         // 2 应用 被用户看到的时候触发     onShow() {        // 常用于小程序界面之间的切换         // 对应用的数据或者页面的效果进行重置     }    // 3 应用 被隐藏的时候触发     onHide() {        // 暂停或者清楚定时器     }    // 4 应用 代码发生报错的时候  执行     onError() {        // 在应用发生代码报错的时候,收集用户的错误信息,通过异步请求,将错误信息发送到后台去     }    // 5 页面找不到的时候就会触发     // 应用第一次启动的时候,如果找不到第一个入口页面,才会触发     onPageNotFound() {        // 如果页面不存在了  通过js的方式来重新跳转页面  重新跳转到第二个首页         // 不能跳转到tabbar页面  导航组件类似         wx.navigateTo({            url: "/pages/demo02/index"         })     } })复制代码 ## 页面生命周期 onLoad   ->  onShow  ->  onReady Page({    data: {              },    onLoad: function(options) {        // onload发送异步请求来初始化页面数据     },    onShow: function() {        // 页面显示加载     },    onReady: function() {        // 页面初次渲染完成     },    onHide: function() {        // 页面隐藏时加载  一个页面跳转到另外一个页面也会触发onHide事件     },    onUnload: function() {        // 页面卸载  也可以通过超链接   关闭当前页面就会触发onUnload事件         // demo01                  // 关闭当前页面就代表着卸载页面     },    onPullDownRefresh: function() {        // 监听用户下拉事件  "enablePullDownRefresh":true         //  页面效果的重新加载     },    onReachBotton: function() {        // 监听用户上拉触底事件   【需要让页面出现上下的滚动才行】         // 常用于  上拉加载下一页操作     },    onShareAppMessage: function() {        // 用户点击右上角转发     },    onPageScroll: function() {        // 页面滚动就触发     },    onResize: function() {        // 页面尺寸发生变化的时候触发            // 常指 手机横屏竖屏的时候  触发             //  app.json中添加   "pageOrientation":"auto"     },    onTabItemTap: function() {        // 1. 当前页面必须为tabbar页面         // 2. 点击的是自己的tab  item的时候才触发     } })复制代码 # 小程序自定义组件 步骤: 1. 创建 2. 声明(那个页面要使用自定义组件,就在那个页面的json文件中声明) {    "usingComponents": {        "Tabs": "../../components/Tabs/Tabs"     } }复制代码 3. 使用 复制代码 注意: - 页面的.js文件中,存放事件回调函数的时候,存放在data同层级下 - 组件的.js文件中,存放时间的回调函数的时候,存放在methods中 - 在小程序中不要直接通过`this.data.x.`来修改数组的值(建议先拷贝一份数组,然后再对拷贝的数组进行修改) let tabs = JSON.parse(JSON.stringify(this.data.tabs));let tabs = this.data;复制代码 ## 组件之间的传值 ### 父组件向子组件传值 通过 `标签的属性`来传递的: 1. 父组件传递 复制代码 2. 子组件接收 Component({    // 里面存放的是要从父组件中接收的数据     properties: {        // 要接受的数据的名称         aaa:{            //type 接收数据的类型             type: String,            //value 默认值             value: ""         }     } });复制代码 3. 子组件使用父组件中传递过来的数据 将接收过来的数据当作本身data中的数据来使用 {{ aaa }}复制代码 ### 子组件向父组件传值 通过`事件`来传递的。 tab切换栏,点击切换。 1. 绑定点击事件   需要在methods中绑定 2. 获取被点击的索引 3. 获取原数组 4. 对数组循环 1. 给每一个循环项  选中属性   改为  false 2. 给   当前的索引  的   项  添加激活选中效果 5. 点击事件触发的时候,触发父组件中的自定义事件同时传递给父组件 1. this.triggerEvent("父组件自定义事件的名称",要传递的参数) Tabs页面中: {{ item.name }}     // 占位符  传递的参数会替换掉      复制代码 子组件的js文件中:(这样写不能改变组件内部的数据,只是基于样式的改变,不是基于功能) methods: {     hanldeItemTap(e) {        // 获取索引         const {index} = e.currentTarget.dataset;        // let {tabs} = this.data;         // tabs.forEach((v,i) => i===index?v.isActive=true:v.isActive=false);         // 修改data中的数据         // this.setData({         //    tabs         // })                  // 触发父组件中的自定义事件同时传递给父组件         this.triggerEvent("itemChange", {             index         })     } }复制代码 在**父组件**中的自定义组件中添加自定义事件:     1     2     3复制代码 **父组件**的js中: data: {    tabs: \[         {            id: 1,            name: "首页",            isActive: true         },         {            id: 2,            name: "待发货",            isActive: false         },         {            id: 3,            name: "待付款",            isActive: false         }     \] }// 自定义事件   接收子组件传递的数据的handleItemChange(e) {    // 接收传递过来的参数     const {index} = e.detail;    // 拿到原数组     let {tabs} = this.data;     tabs.forEach((v,i) => i===index?v.isActive=true:v.isActive=false);    // 修改data中的数据     this.setData({         tabs     }) }复制代码 ## 其他属性 | 定义段 | 类型 | | 描述 | | --- | --- | --- | --- | | properties | Object Map | | 组件的对外属性,是属性名,是属性设置的映射表 | | data | Object | | 常用于父组件向子组件传值,子组件接收父组件的值 | | observers | Object | | 监听properties和data的数据变化 | | methods | Object | | 组件的方法 | | created | Function | | 组件的生命周期函数(组件实例刚刚被被创建时执行)此时不能调用setData | | attached | Function | | 组件实例进入页面节点树时执行 | | ready | Function | | 组件布局完成时执行 | | moved | Function | | 移动执行 | | detached | Function | | 移除执行 | | | | | | # 项目 1. 首页 2. 商品列表 3. 购物车 4. 授权页面 5. 商品搜索 6. 商品收藏 7. 商品分类 8. 商品详情 9. 结算 10. 订单列表 11. 个人中心 12. 意见反馈 ## 小程序的第三方框架 1. 腾讯 wepy  类似于  vue 2. 美团  mpvue  类似于  vue 3. 京东 taro  类似于  react 4. 滴滴   chameleon 5. uni-app  类似于  vue 6. 原生框架 MINA ## 使用阿里字体图标库 1. 在阿里图标官网,将要使用的图标,加入购物车 2. 将图标,加入项目 3. 小程序 pyg   ---》    Font class(通过类的方式来使用图标)   ---》   查看在线链接 4. 在项目的styles文件夹中,创建`iconfont.wxss`文件 5. 打开链接,将链接中的内容复制到`iconfont.wxss`文件中 6. 使用字体图标库中的字体 1. 在全局wxss文件中,引入wxss文件 @import "./styles/iconfont.wxss"复制代码 2. 使用 复制代码 ## tabBar **在app.json中配置** tbaBar: {    "color": "",    //未选中的字体的颜色     "selectedColor": "",    //选中后的字体的颜色     "backgroundColor": "",  // 背景色     "position": "", //定位     "borderStyle": "",   //边框样式      "list": \[         {            "pagePath": "",   // 页面的路径             "text": "",   // 标题的名称             "iconPath": "",   // 未选中的图标路径             "selectedIconPath": ""   // 选中后的图标的路径         }     \] }复制代码 ## 页面样式的初始化 **注意:在小程序中是不支持 通配符(\*)的** 在`app.wxss`文件中 page,view,text,swiper,swiper-item,image,navigator {     padding: 0;     margin: 0;     box-sizing: border-box; } /\*     主题颜色     1. less 中是存在  变量  的     2. 原生的css和wxss 也是支持 css的 \*/ page {     --themeColor: #eb4500;     // 设计稿大小为 375px 时,1px = 2rpx,14px = 28rpx     font-size: 28rpx; }复制代码 **使用主题颜色:** view {     color: var(--themeColor); }复制代码 ## 头部 设置主题色: "window": {    "backgroundTextStyle": "light",   // 字体颜色     "navigatorBarBackgroundColor": "#2b4500",  // 背景色     "navigatorBarText": "唯品会",  // 字体提示     "navigatorBarTextStyle": "white",  // 字体样式    }复制代码 ## 使用接口数据 Page({    data: {        swiperList: \[\]     },    // 页面加载事件     onLoad: function() {        /\*             1. 发送异步请求 获取使用的数据         \*/         wx.request({            url: '',  // 接口地址             success: (result) => {                // 请求成功 给swiperList数组赋值                 this.setData({                    swiperList: result.data.message                 })             }                      });        /\*             wx.request异步请求太多了就会产生   回调地狱   的问题                          解决方法:  es6中的promise         \*/     } })复制代码 **请求报错(两种解决方法):** 1. 在小程序 `详情` 界面  勾选上  `不校验合法域名,web-view(业务域名),TLS版本以及HTTPS证书` 2. 配置请求接口  见  **8.7.将小程序请求的域名添加到后台** ## 解决回调地狱的问题(es6的promise) 在项目的request文件夹中创建`index.js`文件 **通过封装方法,然后调用函数传递参数的方式来使用** // 同时发送异步代码的次数let ajaxTime=0;export const request=(params) => {     ajaxTime++;    // 数据加载效果     wx.showLoding({       title: "加载中",        mask: true     });    return new Promise((resolve, reject) => {         wx.request({            // 解构传递的参数             ...params,            success: (result) => {                 resolve(result);             },            faile: (err) => {                 reject(err);             },            // 不管是成功还是失败都会调用这个函数             complete: () => {                 ajaxTime--;                if(ajaxTime === 0) {                   // 关闭正在等待的图标                     wx.hideLoading();                 }                             }         });     }); }复制代码 **使用封装好的请求方法:** //  引入封装文件 (注意: 一定要把路径补全)import { request } from '../../request/index.js';  // 这里引入的是封装的request函数Page({    data: {        swiperList: \[\]     },    // 页面加载事件     onLoad: function() {        /\*             1. 发送异步请求 获取使用的数据         \*/         /\*             wx.request({                 url: '',  // 接口地址                 success: (result) => {                     // 请求成功 给swiperList数组赋值                     this.setData({                         swiperList: result.data.message                     })                 }             });         \*/         /\*             wx.request异步请求太多了就会产生   回调地狱   的问题                          解决方法:  es6中的promise         \*/                  // 调用方法         this.getSwiperList();              },    // 调用封装好的方法     getSwiperList() {        //  这里填充的数据会替换掉request方法中的...params,         request({ url: 'htltps://api/zbtbs/home/swiperList'});          //  数据获取成功         .then (result => {            this.setData({                swiperList: result.data.message             })         });     } })复制代码 ## 将小程序请求的域名添加到后台 1. 进入    `微信公众平台` 2. 开发 3. 开发设置 4. 服务器域名 5. 添加request合法域名 ## 获取本地存储的数据 **web中的本地存储  和    小程序中的本地存储的区别:** 1. 写代码的方式不一样 1. web中: 1. 存储方式:localStorage.setItem("key",  "value"); 2. 获取方式: localStorage.getItem("key"); 2. 小程序中: 1. 存储方式:wx.setStorageSync("key",  "value"); 2. 获取方式:wx.getStorageSync("key",  "value"); 2. 存的时候   有没有做类型转换 1. web:不管存的数据是什么类型的数据,最后都会通过toString()方法转换为字符串类型的数据 2. 小程序:不存在数据的类型转换 // 接口返回的数据Cates: \[\],onLoad: function(options) {    // 获取本地存储中有没有旧数据     const Cates = wx.getStorageSync("cate");    // 判断本地是否存在     if(!Case) {        // 不存在  发送请求数据         this.setCates();     }else {        // 有旧的数据         // 定义数据过期的时间         if(Date.now() - Cates.time > 1000 \* 10) {           // 重新发送请求             this.getCates();         } else {            this.Cates = Cates.data;            // 渲染数据         }     } }// 获取请求的数据getCates() {    // 把接口的数据存储到本地存储中     wx.setStorageSync("cates", {time:Date.now(),data: this.Cates}); }复制代码 ## 定义公共的url 在request.js文件中,封装请求方法 export const request=(params) => {    // 定义公共的url     const baseUrl = "https://api.zbsb.cn/api/public"     return new Promise((resolve, reject) => {         wx.request({            // 解构传递的参数             ...params,            url: baseUrl + params.url;             success: (result) => {                 resolve(result);             },            faile: (err) => {                 reject(err);             }         });     }); }复制代码 ## 小程序支持es7的async语法 1. 在微信开发者工具中勾选`es6转es5语法` 2. 在github里面下载regenerator库中的runtime.js 3. 在小程序目录文件下新建文件夹/lib/runtime/runtime.js,将代码拷贝进去 4. 在每一个需要使用async语法的页面js文件中,引入文件 import regeneratorRuntime from '../lib/runtime/runtime';复制代码 使用asyn语法: async getCates() {    // 1 使用es7的async await来发送请求     const res=await request({url:"/categories"}); }复制代码 ## 小程序url传参 // 传递参数// 拿取参数 onLoad:function(options) {     consol.log(options);  // 打印输出options的值 }复制代码 ## 封装tab切换组件 封装的Tab组件中: properties: {    tabs: Array,    value: \[\] }                              {{ item.value }}                   // 切换内容                  methods: {    // 点击事件     handleItemTap(e) {        // 获取点击的索引         const {index} = e.currentTarget.dataset;        // 触发父组件中的事件         this.triggerEvent("tabsItemChange", {index});     } }复制代码 使用封装的Tab组件: //bindtabsItemChange 监听自定义事件     1     2     3// 标题的点击事件 bindtabsItemChange(e) {   // 获取被点击的标题的索引   const {index} = e.detail;   // 修改原数组   let {tabs} = this.data;   tabs.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false);   // 赋值到data中   this.setData({     tabs   }); }复制代码 ## 滚动条触底事件(页面上滑事件) 滚动条触底,加载下一页数据 总页数 =  Math.ceil(总条数 /  每页显示的数据数) // 获取商品数据列表async getGoodsList() {    const res=await request({url:"/goods/search",data:this.QueryParams});    // 获取总条数     const total = res.total;    // 计算总页数     this.totalPage = Math.ceil(total / this.QueryParams.pagesize);    // 拼接数组     this.setData({        goodsList: \[...this.data.goodsList,...res.goods\]     });    // 关闭下拉刷新的窗口     wx-stopDownRefresh(); }// 滚动条触底事件onReachBottom() {    // 判断还有没有下一页数据     if(this.QueryParams.pagenum >= this.totalPage) {        // 当前页码 > 总页数    没有下一页              }    else {        // 还有下一页  当前页码++ 重新发送请求  数据请求回来后要对data中的数组进行拼接         this.QueryParams.pagenum++;        this.getGoodsList();     } }复制代码 ## 下拉刷新页面 1. 触发下拉刷新事件(需要在页面的json文件中开启一个配置项)【enablePullDownRefresh: true,backgroundTextStyle: dark】 2. 重置  数据  数组 3. 重置页码   设置为1 4. 重新发送请求 5. 数据请求成功,手动关闭等待效果 onPullDownRefresh() {    // 重置  数据  数组     this.setData({        goodsList: \[\]     });    // 重置页码   设置为1      this.QueryParams.pagenum=1;    // 重新发送请求     this.getGoodsList(); }复制代码 ## wx.showModel改变this的指向问题 wx.showModel({    title: '提示',    content: '您是否要删除?',    success :(res) => {         ...     } })复制代码 ## js中的删除 cart.splice(index, 1);  // 删除索引为index的元素cart.filter(v => v.checked);  // 挑选出cart数组中checked为true的值复制代码 ## 弹窗的封装 **在asyncWX.js文件中** export const showModel=({content}) => {    return new Promise((resolve,reject) => {         wx.showModel({            title: '提示',            content: content,            success :(res) > {                 resolve(res);             },            fail :(err) => {                 reject(err);             }         })     }) }复制代码 **使用** import {showModel} from '../../utils/asyncWx.js';async showTips() {    const res=await showModel({content: '您是否要删除?'})    if(res.confirm) {        cart.splice(index, 1);        this.setData(cart);     } }复制代码 ## 获取缓存中的数据 wx.getStorageSync("address");复制代码 ## 微信支付 1. 企业账号 2. 在企业账号的小程序后台中  必须  给  开发者添加上白名单 1. 一个AppID可以绑定多个开发者 2. 绑定之后的开发者就拥有了开发者的权限了 3. 支付按钮 1. 先判断缓存中有没有token 2. 没有  跳转到授权页面   获取用户的 token 值 3. 有  执行支付操作 **流程:创建订单,准备预支付,发起微信支付,查询订单** **一,获取token** handleOrderPay() {   try {        // 1. 判断缓存中有没有token值         const token = wx.getStorageSync("token");        // 2. 判断         if(!token) {            // 跳转到  授权 页面             wx.navigateTo({                url: "/page/auth/index"             })            return;         }        // 3. 创建订单         // 准备创建订单需要的参数         const header = {Authorization:token};        // 准备请求体参数         const order\_price = this.data.totalPrice;   // 订单总价格         const consignee = this.data.address.all;  // 详细地址         let goods = \[\];        const cart = this.data.cart;         goods.forEach(v => goods.push({            goods\_Id: v.goods\_Id, // 商品的id             goods\_number: v.goods\_number, //商品数量             goods\_price: v.goods\_price  // 商品的单价         }))        const orderParams = {order\_price,consignee,goods}        // 4. 准备发送请求  创建订单  获取订单编号         const {order\_number}=await request({url:                     "/order/create"},method:"POST",data:orderParams,head:head});        // 5. 发起 预支付接口         const {pay}=await request({url:"/order/req\_unifiedorder",method:"POST",head,data:{order\_number}});        // 6. 发起微信支付         await requestPayment(pay);        // 7. 查询后台  订单状态是否成功         const res=await request(url:"/orders/chkOrder",method:"POST",head,data:{order\_number}});        // 提示支付成功         await showToast({title:"支付成功"});        // 手动删除 缓存中支付成功购物车的数据         let newCart = wx.getStorageSync("cart");        // 过滤出没有被选中的数据         newCart = newCart.filter(v => !v.checked);         wx.setStorageSync("cart",newCart);        // 8.支付成功  跳转到订单页面         wx.navigateTo({            url: '/page/order/index'         })    } catch(err) {       await showToast({title:"支付失败"});    }      }复制代码 page/autn/index页面中: 复制代码 // 获取用户信息// encryptedData  // rawData// iv// signatureasync handleGetUserInfo(e) {    try {        // 获取用户信息         const { encryptedData,rawData,iv,signature } = e.detail;        // 获取小程序登录之后的token值  在asyncWX.js中封装token请求方法         // wx.login({         //     timeout: 1000,         //    success: (result) => {         //      cost { code } = result;            //    }         // })         const {code} = await login();        const loginParams = { encryptedData, rawData, iv, signature, code }        // 发送请求  获取用户的token         const {token}=await request(url: '/user/wxlogin',data: loginParams,methods:         "post");        // 将获取到的token存储到缓存中,同时跳转回上一个界面         wx.getStroageSync("token", token);         wx.navigateBack({            data: 1 // 返回上一层         })         } catch(err) {        console.log(err);     } }复制代码 在`asyncWX.js`文件中封装支付方法: export const requestPayment=(pay) => {    return new Promise((resolve,reject){         wx.request({            ...pay,           success: (result) => {                 resolve(result);            },           fail: (err) => {                reject(err);            }         })                        }) }复制代码 **二,准备预支付(获取参数 pay)** **三,发起微信支付(提交pay参数)** **四,查询订单** **五,删除缓存中已经被选中购买的商品** **六,删除后的购物车数据 填充会缓存** **七,跳转页面** ## 图片上传(wx.uploadFile) 图片上传的时候,存储上传的图片的时候,要先拼接上之前的图片。 chooseImage:\[\], this.setData({    _chooseImage: \[上传之前的图片组, 上传的图片组\]_     chooseImage: \[...this.data.chooseImage, ...chooseImage\] }) **上传文件的api不支持多个文件同时上传** **解决方法:遍历数组  挨个上传** wx.uploadFile({    url: '', // 图片要上传到哪里     filePath: '', // 被上传文件的路径     name: '',  // 上传的文件的名称  后台通过定义的上传的名称来获取名称     formData: {}, // 顺带的文本信息})复制代码 # 项目发布 **注意:发布之前记得关闭详情界面的  _不校验合法域名_** 上传的每个文件大小不超过2M,总大小不超过10M。 上传: 1. 版本号 1. 第一个数字(大版本的更新) 2. 第二个数字(重要功能更新) 3. 第三个数字(最小功能,小bug,小补丁) 2. less文件是不会被打包上传的 3. 上传成功后的小程序还是一个体验版本的小程序,如果需要将体验版本的小程序变成线上版本的小程序,就在 **微信公众平台**将提交的体验本的小程序,**提交审核**(审核的时间大概是一个小时)。 # 小程序插件 1. 小程序开发助手![](https://img.php.cn/upload/article/000/000/052/a44104ea9aa35e5cb89a9306c2060764-4.png) 2. 安装easy less插件![](https://img.php.cn/upload/article/000/000/052/ec4bd01fc964a4240fe607c31543d491-5.png)在vscode中配置(ctrl + shift + p 然后输入 setting,然后添加上如下配置): "less.compile": {    "outExit": ".wxss"}复制代码 > **相关免费学习推荐:****微信小程序开发** 以上就是放心,手把手教你写微信小程序的详细内容,更多请关注知企PROSAAS其它相关文章! **Tags:** bug, checkbox, class, default, es6, for, github, html, https, if **Categories:** 后端开发 ---