原生微信小程序全流程(基础知识+项目全流程)

这篇具有很好参考价值的文章主要介绍了原生微信小程序全流程(基础知识+项目全流程)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

小程序的基本使用

小程序文件类型

小程序主要提供了 4 种文件类型:

类型名称 作用 是否必须存在
.wxml 用于页面的布局结构,相当于网页中 .html 文件
.wxss 用于页面的样式,相当于网页中的 .css 文件
.js 用于页面的逻辑
.json 用于页面的配置

文件作用

文件名 作用 是否必须存在
app.js 小程序入口(首先执行的文件)
app.json 小程序的全局配置
app.wxss 小程序的全局样式
project.config.json 小程序开发者工具配置 是(会自动创建)
sitemap.json 小程序搜索优化

要点

  • data 初始化页面中的数据
  • setData 更新数据
  • {{}} 插值语法可以实现数据的渲染
  • bind:事件类型=事件回调
Page({  //index.js
  // 约定格式,使用data定义数据
  data: {
    message: 'nihao!'
  },
  changeMessage() {
    // this.setData修改数据
    this.setData({
      message: 'new!'
    })
  }
})

index.wxml

<view>{{message}}</view>
<button bind:tap="changeMessage" type="primary" size="mini">点我试试</button>

如何注册小程序事件监听?

Page({
处理函数() {}

})

<button bind:事件名=“处理函数”>

配置文件

原生微信小程序全流程(基础知识+项目全流程)

分类:

全局 app.json

页面 page.json

全局配置

app.json 是当前小程序的全局配置,包括了:

  • 小程序首页
  • 界面表现
  • 网络超时时间
  • 底部 tab
  • …等配置
全局配置pages

用于指定小程序由哪些页面组成。

  1. 每一项都对应一个页面的 路径(含文件名) 信息。
  2. 小程序中的每一个页面都必须在pages下登记一下。
  3. 文件名不需要写文件后缀,框架会自动去寻找对应位置的 .json, .js, .wxml, .wxss 四个文件进行处理。

是一个数组,每一项表示一个页面

{
  pages:[
    "pages/index/index",
    "pages/logs/logs"
  ]
}

数组中的第一个元素表示小程序启动时默认打开的页面-主页

页面跳转
<navigator url="/pages/logs/logs">跳转到log页面</navigator>
新建页面(自动优先)
新建页面-手动

共三步:

  1. 新建空目录
  2. 新建页面(4个文件)
  3. 在pages下手动补充页面地址

要点:

  1. 页面都放在pages下,一个页面一个文件夹。
  2. 新建page时,不需要写文件后缀名。
新建页面-自动

在pages下补充一项 pages/page1/page1 ,然后保存,则会自动添加一个页面。

app.json

{
  pages:[
    "pages/index/index",
    "pages/logs/logs",
    "pages/page1/page1"
  ]
}
小程序配置-全局配置—window

用于设置小程序的状态栏、导航条、标题、窗口背景色。

https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#window

常见配置:

原生微信小程序全流程(基础知识+项目全流程)

小程序配置-全局配置—tabBar

原生微信小程序全流程(基础知识+项目全流程)

tabBar

tabBar 定义小程序 tab 栏的表现,如下图即所谓的 tab 栏:

原生微信小程序全流程(基础知识+项目全流程)

常见配置属性

属性 类型 默认值 是否必须 说明
list array tab 的列表,详见 list 属性说明,最少 2 个、最多 5 个 tab
color 16 进制颜色 tab 上的文字默认颜色,仅支持十六进制颜色
selectedColor 16 进制颜色 tab 上的文字选中时的颜色,仅支持十六进制颜色
backgroundColor 16 进制颜色 tab 的背景色,仅只持 16 进制颜色
borderStyle string black tabbar 上边框的颜色, 仅支持 black / white
position string bottom tabBar 的位置,仅支持 bottom / top

上述配置中 list 具体又包含以下内容:

属性 类型 默认值 是否必须 说明
pagePath string 页面路径,必须在 pages 中先定义
text string tab 上按钮文字
iconPath string 图片路径,icon 大小限制为 40kb,建议尺寸为 81px * 81px,不支持网络图片,当 position 为 top 时,不显示 icon
selectedIconPath string 选中时的图片路径,icon 大小限制为 40kb,建议尺寸为 81px * 81px,不支持网络图片,当 position 为 top 时,不显示 icon

参考代码(app.josn)

{
  ......
  "tabBar": {
    "color": "#999",
    "selectedColor": "#e93b3d",
    "backgroundColor": "#fff",
    "list": [
      {
        "pagePath": "pages/index/index",
        "text": "首页",
        "iconPath": "static/tabbar/home-default.png",
        "selectedIconPath": "static/tabbar/home-active.png"
      },
      {
        "pagePath": "pages/logs/logs",
        "text": "日志",
        "iconPath": "static/tabbar/video-default.png",
        "selectedIconPath": "static/tabbar/video-active.png"
      },
      {
        "pagePath": "pages/index/demo",
        "text": "示例",
        "iconPath": "static/tabbar/face-default.png",
        "selectedIconPath": "static/tabbar/face-active.png"
      }
    ]
  }
}
小程序配置-页面配置
  • 页面配置只针对某个页面生效
    • 如 index.json 是针对 index 页面生效,demo.json 只针对页面 demo 生效
  • 不用写window字段
  • 优先级比全局配置高

常用配置:

属性 类型 默认值 是否必须 说明
navigationBarTitleText string 空白 导航栏标题文字内容
navigationBarTextStyle string black 导航栏标题颜色,仅支持 black / white
navigationBarBackgroundColor 16 进制颜色 #00000 导航栏背景颜色,如 #000000
navigationStyle string default 导航栏样式,仅支持 default / custom
enablePullDownRefresh boolean false 是否开启全局的下拉刷新
小程序适配-响应式单位rpx

rpx ,responsive pixel:

在小程序中的单位rpx,它的特点是能够自动地适配置不同尺寸的手机屏幕。

原理:不管手机屏幕具体多宽,100%的屏幕宽度就是750rpx

100%屏幕的宽度 = 750rpx

  1. 所有的设备宽度都是750rpx
  2. 在实际使用中只需要将设计稿调整为 750px 宽,然后 1:1 的比例来写长度(单位使用 rpx),如:设计稿中某个区域(盒子)的大小为 18090px ,写成小程序的尺寸为 18090rpx。

注:上述的规则仅适用于设计稿宽度为 750px

rpx (responsive pixel):规定不管屏幕为多少px,100%的屏幕宽度就是750rpx
100%屏幕的宽度 = 750rpx

内置组件—navigator(跳转)

navigator 是小程序中的导航标签,类似以前web中的a标签。通过 url 来指定跳转的页面

  • url: 页面路径
    • 支持相对和绝对路径
    • 路径为空会报错
    • 还可以跳到其他小程序
  • hover-class:点击态的样式
    • none 禁用点击效果
  • open-type:跳转方式
    • navigate。默认值
    • switchTab。跳转到tabbar页
属性名 类型 默认值 说明
url string 当前小程序内的跳转链接
open-type string navigate 跳转方式
target String self 在哪个目标上发生跳转,默认当前小程序
内置组件-image(图片)

原生微信小程序全流程(基础知识+项目全流程)

<image 
src="图片资源地址" 
mode="图片裁剪,缩放方式"></image> 

image组件是一个有默认大小(320*240)的盒子。

  • src: 图片资源地址。相对地址,绝对地址(外网地址)。
  • mode: 默认值为scaleToFill,用来设置图片裁剪、缩放的模式。
    • scaleToFill。不保证缩放比,图片拉伸填满容器
    • aspectFit。保证缩放比,使图片的长边显示出来
    • aspectFill。保证缩放比,使图片的短边显示出啦
  • lazy-load:默认为false,是否开启懒加载模式。(3屏)
内置组件—swiper(轮播图)

swiper可以理解为小程序内置的轮播图标签,可以让方便快速地实现轮播功能。

<swiper>
  <swiper-item>1屏的内容 </swiper-item>
  <swiper-item>2屏的内容 </swiper-item>
  <swiper-item>3屏的内容 </swiper-item>
  <swiper-item>4屏的内容 </swiper-item>
</swiper>
  1. swiper:滑块容器。内只能写swiper-item。它的默认高度是150px;
  2. swiper-item:滑块单元。它的大小是: 宽度 和 高度 为 100% * 100%;
表单相关
  1. 输入框:input
    1. password密码类型,placeholder占位文字
  2. 单选框:radio-group和radio
    1. value指定表单数据,checked选中状态
  3. 复选框:checked-group和checkbox
    1. value指定表单数据,checked选中状态
  4. 选择框:picker
    1. mode:指定不同类型的选择框
scroll-view(滚动)

``scroll-view 在页面中指定一个**可以滚动**的区域,并且这个可滚动的区域能够实现一些高级的交互,比如:下拉刷新`等。

scroll-view 中嵌套任意需要滚动的内容,要求内容必须有溢出(scroll-view有固定的尺寸),垂直滚动时 scroll-view 必须要指定高度。

属性

  • scroll-x 属性是否允许水平方面滚动
  • scroll-y 属性是否允许垂直方向滚动
  • refresher-enable 属性是否开启下拉刷新的交互
小程序样式-全局样式

app.wxss 定义全局样式,该文件中的样式会在所有的页面生效。

注:page 在每个页面中都有,它是由小程序自动添加上的,相当于网页中的 body 标签。

小程序样式-静态资源

小程序中 .wxss 文件中不支持使用本地路径的资源,比如背景图片是不允许使用本地图片路径的,必须使用网络路径(https:// 或 http:// 开头)或者转换成 base64 编码。

小程序样式-字体图标(iconfont)

小程序中字体图片的使用与网页中基本上是一致的,唯一的区别是小程序的 .wxss 文件中不支持使用本地字体文件,我们使用 iconfont 平台提供的服务生成字体文件后,直接使用其线上的字体文件地址。

资源参考:https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=4061819

小程序基本使用-请求数据并渲染

小程序模板语法-数据绑定

在js中定义数据

Page({
  data: {
    isOpen: true,
    message: 'hello world!'
  }
})

小程序的data是一个对象,不同于vue的data是一个函数

在模块中获取使用数据

小程序中使用 {{}} 实现数据与模板的绑定

  1. 内容绑定:<view>{{ 属性名 }}</view>
  2. 属性绑定: <input value="{{属性名}}" />

{{}} 内写的是表达式

简易双向绑定
  1. 小程序中提供了 model:value="{{数据名}}" 语法来实现双向的数据绑定

  2. 目前只能用在 inputtextarea 组件中。

    只能是一个单一字段的绑定,不能嵌套对象.否则出现以下报错:

    [pages/index/index] Two-way binding does not support complex data paths currently. This two-way binding is ignored.

    双向绑定目前不支持复杂的数据路径。这种双向绑定将被忽略。

小程序修改数据

格式:

<元素 bind:事件名="处理函数">
this.setData({
  属性名1: 新值1,
  属性名2: 新值2
})


this.setData({
  "属性1.属性2":})
模板语法—条件渲染

小程序中的条件渲染的方式有两种

1.wx:if

    • 在小程序中,使用wx:if="{{条件}}"来判断是否需要渲染该代码块
    • 也可以用wx:elif 和 wx:else来添加else判断

2.hidden

    • 在小程序中,使用hidden="{{条件}}"也能控制元素的显示与隐藏
    • 条件为true则隐藏,否则则显示
wx:if 与 hidden区别

1.区别

    1. wx:if是通过动态创建或移除元素来控制元素是否可见
    2. hidden 是通过样式(none/block)来控制元素是否可见

2.要点

  • 如果一个标签频繁切换显示,建议使用 hidden。例如:折叠面板,抽屉面板等等
  • 如果不频繁切换,建议使用wx:if,它有更好的初始化性能。
模板语法—列表渲染—基础

原生微信小程序全流程(基础知识+项目全流程)

格式

<元素 wx:for="{{列表数据}}" >
   <!--  wx:for 结构内可以使用两个变量(1)item:循环项(2)index:循环索引 -->
   {{item}}, {{index}}
</元素>

手动指定索引名和当前项的变量名

<view wx:for="{{list}}" wx:for-item="value" wx:for-index="key">
	{{key}}-{{value}}
</view>
模板语法-列表渲染-wx:key

wx:key 针对不同的数组类型有不同的写法

  • 普通数组 wx:key=“*this”
  • 数组对象 wx:key=“具有唯一性的某个属性名”
小程序内置API-网络请求

网络请求

调用 wx.request 能够在小程序中发起网络请求与后端接口进行数据的交互,其语法格式如下:

wx.request({
  url: '这里是接口的地址',
  method: '这里是请求的方法',
  data: '请求时提交的数据',
  header: {/* 请求头信息 */},
  success: () => {/* 成功的回调 */},
  fail: () => {/* 失败的回调 */},
  complete: () => {/* 成功或失败的回调 */}
})
配置网络请求合法域名

域名必须是https

原生微信小程序全流程(基础知识+项目全流程)
原生微信小程序全流程(基础知识+项目全流程)

小程序内置api-界面交互
showLoading效果

配合网络请求来使用

wx.showLoading 显示 loading 提示框

  • title 文字提示内容
  • mask 是否显示透明蒙层,防止触摸穿透
hideLoading
  • wx.hideLoading 隐藏 loading 提示框
showToast

wx.showToast 消息提示框(轻提示)

  • title 提示的标题
  • mask 是否显示透明蒙层,防止触摸穿透
  • duration 延迟时间(提示框显示多久,单位是毫秒)
  • icon 指定图标,none 不使用图标
操作注意:
  1. 发请求之前,showLoading
  2. 请求结束之后(无论成败),hideLoading
  3. 数据渲染成功之后,showToast
微信小程序本地存储
  • wx.setStorageSync(key, value) 存入一个数据,复杂类型数据不需要 JSON.stringify 处理
  • wx.getStorageSync(key) 读取本地key数据,复杂类型数据不需要 JSON.parse 处理
  • wx.removeStorageSync(key) 删除本地key数据
  • wx.clearStorageSync()清空本地全部数据
微信小程序API的特征

API的用法分类三类:

  1. 异步的api
  2. 同步的api
  3. 支持promise的api
异步 API

绝大部分的 API 都是异步方式,通过回调函数获取 API 执行的结果

  • success API 调用成功时执行的回调
  • fail API 调用失败时执行的回调
  • complete API 调用结束时执行的回调(无论成功或失败)

基本格式:

wx.api名称({success(res){ console.log(成功执行api的结果) }})
支持promise的api

部分异步的 API 也支持以 Promise 方式返回结果,此时可以配合 asyc/await 来使用。

例如:支持Promise格式的异步api有:

  • wx.getSystemInfoSync()
  • wx.getStorage

支持Promise格式的异步api有:

  • wx.request()
同步 API

部分 API 支持以同步方式获取结果,这些 API 的名称都 **Sync** 结尾。如

  • wx.getStorageSync : 获取本地存储
  • wx.getSystemInfoSync: 获取系统信息

基本格式:

const result = wx.api名称()
事件处理-事件对象&传参

事件对象

bind:事件类型=事件回调 //回调函数第1个参数即为事件对象

事件回调传参

小程序的事件回调不支持传参数

因此要将模板中的数据传递到事件回调中就必须要通过事件对象来实现。

方式1:

  • 补充参数:

     <button bind:tap="eventHandler" **mark:属性名="值"**>点击我看看</button>
    
    
  • 获取值:

    eventHandler(ev){ console.log(**ev.mark.属性名**) 
    
    

方式2:

  • 补充参数 :

    <button bind:tap="eventHandler" **data-属性名="值"** >点击我看看</button>
    
    
  • 获取值:

    eventHandler(ev){ console.log(**ev.target.dataset.属性名**) }
    
    
事件处理-组件事件

事件类型只属于某个组件,我们将其称为组件事件

前面介绍的 tap 事件可以在绝大部分组件是监听,我们可以将其理解为通用事件类型,然而也有事件类型只属于某个组件,我们将其称为组件事件。

组件不同,支持的事件也不同

scroll-view组件中的事件

  1. bind:scrolltolower 当滚动内容到达底部或最右侧时触发
  2. bind:refresherrefresh 执行下拉操作时触发

另外,还有注意一个特别的属性

refresher-triggered 用它来控制下拉刷新状态

事件处理-表单组件中的事件

如何获取表单中,用户选择的值?

  1. input: 简易双向绑定
  2. radioGroup: 绑定change事件,在事件对象中detail.value拿到值
  3. checkboxGroup: 绑定change事件,在事件对象中detail.value拿到值
  4. picker: 绑定change事件,在事件对象中detail.value拿到值
  • change 表单数据发生改变时触发(input 不支持)

​ 5.整体表单提交

  • form: submit事件 表单提交时触发,button 按钮必须指定 form-type 属性
生命周期-页面生命周期
  • 分类
    • 应用生命周期
    • 页面生命周期
    • 组件生命周期

生命周期是一些名称固定,会自动执行的函数。

页面生命周期-基本使用

  • onLoad 在页面加载完成时执行,只会执行 1 次,常用于获取地址参数和网络请求

  • onReady页面初次渲染完成

  • onShow 在页面处于可见状态时执行,常用于动态更新数据或状态

  • onHide 在页面处于不见状态时执行,常用于销毁长时间运行的任务,如定时器

    页面生命周期-应用场景

onLoad(){ // 发起请求 }

onShow(){ // 动态更新数据或状态 }

onHide 在页面处于不见状态时执行,常用于销毁长时间运行的任务,如定时器

onReady 在页面初次渲染完成时执行,只会执行 1 次,常用于节点操作或动画交互等场景

生命周期-应用生命周期

app.js

  • onLaunch 小程序启动时执行1次,常用于获取场景值或者启动时的一些参数(如自定义分享)
  • onShow 小程序前台运行时执行,常用于更新数据或状态
  • onHide 小程序后台运行时执地,常用于销毁长时间运行的任务,如定时器。
// pages/lifetimes/index.js
Page({
  
  // 小程序转发/分享
  onShareAppMessage() {
    return {
      title: '小程序学习',
      path: '/pages/index/index?test=测试数据',
      imageUrl: '/static/images/cover.png'
    }
  }
})

小程序基础-分包加载&&自定义组件&&&项目全流程

小程序分包加载

小程序分包加载-为什么要分包加载

  • 微信平台对小程序单个包的代码体积限制为 2M,超过 2M 的情况下可以采用分包来解决
  • 即使小程序代码体积没有超过 2M 时也可以拆分成多个包来实现按需加载
  • 配置文件能忽略的只有静态资源,代码无法被忽略

原生微信小程序全流程(基础知识+项目全流程)

配置忽略文件

project.config.json

{
  "description": "项目配置文件",
  "packOptions": {
    "ignore": [
      {
        "value": "static/uploads",
        "type": "folder"
      }
    ],
    "include": []
  },

type: 表示要忽略的资源类型

value: 表示具体要忽略的

小程序分包加载-使用分包配置

分类:

  1. 主包:
  • 每个小程序必定含有一个主包

  • 默认启动页面、TabBar 页面,以及公共资源/JS 脚本必须放在主包;

    2.分包 https://developers.weixin.qq.com/miniprogram/dev/framework/subpackages.html

  • 通过subPackages来配置

  • 所有包的大小之和不超过20M

app.json

{
  // 省略其他的...
  
  "subPackages": [
      {
        "root": "subpkg_user", // 分包代码的目录,其实就是一个独立的文件夹
        "pages": [
          "pages/profile/profile"
        ]
      },
      {
        "root": "subpkg_order", // 文件夹
        "pages": [
          "pages/order_list/index", 
          "pages/order_list/index"
        ]
      }
  ]
}

注意: 写完分包之后,如果对应的文件夹和页面不存在,它会自动创建文件夹和页面

小程序分包—预加载

https://developers.weixin.qq.com/miniprogram/dev/framework/subpackages/preload.html

在打开小程序启动的时候只下载主包代码,分包并不会下载,因此能够提升小程序启动时的打开速度,但是分包的代码只有在访问到分包的页面时才去下载,这样用户就需要有一定时间的等待(一般不太影响),通过分包预加载技术可以实现提前去下载分包的代码,这样分包页面的访问速度也会得到提升。
小程序通过 preloadRule 配置需要预加载的分包。

app.json

{   ......
	"preloadRule": {
    "页面地址,进入这个页面就需要预加载分包": {       // pages/index/index 
      "network": "网络环境",                     // "wifi"
      "packages": ["要预加载的包名"]              //["goods_pkg"]
    }
  },   ......
}      //当用户访问到 pages/index/index 时,在 wifi 网络前提下预先下载 goods_pkg 分包的代码。
  • 指定某个页面路径做为 key,含义是当访问这个页面时会去预加载一个分包
  • network 预加载分包的网络条件,可选值为 all、wifi,默认为 wifi
  • packages 指定要预下载的分包名或根路径

配置完成之后,访问指定页面时,就会在控制台输出提示。

原生微信小程序全流程(基础知识+项目全流程)

自定义组件—基本使用

创建组件

通常将项目中的组件都放在一个独立的目录下,并且一般就给这个文件夹取名为:components 。这个目录需要我们手动进行创建。

  1. 新建一个目录:components

  2. 在components上点击鼠标右键,选择「新建Component」

  3. 填入组件的名字。它会自动创建4个同名的文件。(这一点和创建页面是一样的)

原生微信小程序全流程(基础知识+项目全流程)

组件和页面的结构区别:

  1. 组件的配置文件(.json文件)中,有一个配置项:component: true
  2. 组件的 .js 文件中调用 Component 函数,页面的.js文件中调用Page函数

注册组件

  • 页面注册是在使用组件的(xxxx.json)中通过 usingComponents 进行注册,只能在当前页面中组件
  • 全局注册是在 app.json 文件中通过 usingComponents 对自定义组件进行注册,可以在任意页面中使用
"usingComponents": {
    "my-test": "/components/MyTest/index"
}

使用组件

在wxml中,直接通过标签的方式使用即可。

自定义组件—组件样式
  1. 组件中的样式不要使用标签选择器
  2. 组件中,样式默认是隔离的: 自定义组件的样式只受到自定义组件 wxss 的影响
  3. 通过对组件的配置,可以取消这个隔离的状态

样式隔离注意点

  • app.wxss中的全局样式对组件无效
  • 只有class选择器具有样式隔离效果,id选择器、属性选择器、标签选择器不受样式隔离的影响

建议:在组件和引用组件的页面中建议使用class选择器,不要使用id、属性、标签选择器

修改组件样式的隔离选项

默认情况下,自定义组件的样式隔离特性能够防止组件内外样式互相干扰的问题。但有时,我们希望外界能够控制组件内部的样式,此时,可以通过在组件的.js文件中设置: options → addGlobalClass 为true

XX.js

Component({
  options: {
    addGlobalClass: true
  }
})

在页面中设置的同类名的选择器就能作用于子组件内部的元素。但是,组件内的class选择器,不能影响页面的元素。

自定义组件—组件样式-外部样式类

组件希望接受外部传入的样式类。此时可以在 Component 中用 externalClasses 定义若干个外部样式类。

在开发组件时,主动暴露给组件使用者,修改组件内部样式

组件 custom-component.js

/* 组件 custom-component.js */
Component({
  externalClasses: ['my-class']
});

组件 custom-component.wxml

<!-- 组件的wxml -->
<!-- 这里的my-class相当于一个占位符 -->
<view class="my-class">components/MyTest/index.wxml</view>

页面的 WXML

<!-- 页面的 WXML -->
<custom-component my-class="red-text" />
<custom-component my-class="large-text" />

页面的wxss

.red-text{ color: red; }
.large-text {font-size: 50px; }

外部样式类相当于用一个类名去当占位符,以便于在后期使用时替换成真实的类名,方便添加额外的样式。

参考:https://vant-contrib.gitee.io/vant-weapp/#/button#wai-bu-yang-shi-lei
原生微信小程序全流程(基础知识+项目全流程)

自定义组件—数据方法

组件的典型结构

// borderImage.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    
  },

  /**
   * 组件的初始数据
   */
  data: {
    
  },

  /**
   * 组件的方法列表
   */
  methods: {
    
  }
})

定义数据

在小程序中,用于组件模板渲染的私有数据,需要定义到data

methods方法

在小程序的组件中,事件处理函数和自定义方法需要定义到methods

自定义组件—组件插槽
单个插槽

在小程序中,默认情况下每个自定义组件中只允许使用一个插槽进行占位。

<!--components/MyTest2/index.wxml-->
<view>
  <text>components/MyTest2/index.wxml</text>
  <!-- 对于不确定的内容,可以使用slot进行占位,具体内容交给使用者确定 -->
  <slot></slot>
</view>

使用组件

<my-test2>
  <!-- 这里的内容将被放到组件中<slot>的位置 -->
  <view>
    这里是slot里的内容
  </view>
</my-test2>

多插槽(具名插槽)

组价.js

Component({
  options: {
    multipleSlots: true // 在组件定义时的选项中启用多 slot 支持
  },
  // ... 省略其他
})

此时,可以在这个组件的 wxml 中使用多个 slot ,以不同的 name 来区分。

定义插槽

<view>
  <text>components/MyTest2/index.wxml</text>
  <!-- 对于不确定的内容,可以使用slot进行占位,具体内容交给使用者确定 -->
  <!-- <slot></slot> -->
  <slot name="before"></slot>
  <view>
    ---------这里是分割线--------
  </view>
  <slot name="after"></slot>
</view>

使用组件

<my-test2>
  <!-- 这里的内容将被放到组件中<slot>的位置 -->
  <!-- <view>
    这里是slot里的内容
  </view> -->
  <view slot="before">
    这里是before slot里的内容
  </view>
  <view slot="after">
    这里是after slot里的内容
  </view>
</my-test2>

自定义组件—生命周期

组件生命周期-lifetimes

生命周期 参数 描述
created 在组件实例刚刚被创建时执行,此时还不能调用 setData,一般用于给组件的this添加一些自定义的属性字段
attached 在组件实例进入页面节点树时执行,绝大多数初始化工作可以在这个时机进行,例如发请求获取初始数据
ready 在组件在视图层布局完成后执行
moved 在组件实例被移动到节点树另一个位置时执行
detached 在组件实例被从页面节点树移除时执行,适合做一些清理工作
error Object Error 每当组件方法抛出错误时执行

生命周期函数要写在lifetimes里边

lifetimes: {
    created() {
      console.log('组件被created') // 这里使用setData不会引起视图的更新
      this.setData({ msg: 'abc!' })
    },
    attached() {
      this.setData({ msg: 'abcd' })
    }
  }

自定义组件-属性(父传子)

在小程序中,properties是组件的对外属性,用于接收外界传递到组件中的数据

父组件传入属性值

<my-test isOpen max="9" min="1" />

子组件.js中接收

Component({
	properties: {
    isOpen: Boolean,
  	min: Number, // 直接写类型
    max: {       // 写类型 + 初始值
    	type: Number,
      value: 10 // value用于指定默认值
    }
  }
})

自定义组件-组件通讯-自定义事件triggerEvent(子传父)

原生微信小程序全流程(基础知识+项目全流程)

Vant组件库

官方文档:https://vant-contrib.gitee.io/vant-weapp/#/quickstart

步骤一 通过 npm 安装

npm i @vant/weapp -S --production

步骤二 修改 app.json

将 app.json 中的 "style": "v2" 去除

步骤三 修改 project.config.json

开发者工具创建的项目,miniprogramRoot 默认为 miniprogrampackage.json 在其外部,npm 构建无法正常工作。

需要手动在 project.config.json 内添加如下配置,使开发者工具可以正确索引到 npm 依赖的位置。

{
  ...
  "setting": {
    ...
    "packNpmManually": true,
    "packNpmRelationList": [
      {
        "packageJsonPath": "./package.json",
        "miniprogramNpmDistDir": "./miniprogram/"
      }
    ]
  }
}

步骤四 构建 npm 包(重点)

开发者工具上 > “工具” > “构建npm”

原生微信小程序全流程(基础知识+项目全流程)

使用

去app.json(全局注册)或页面.json(局部注册)中注册

"usingComponents": {
  "van-button": "@vant/weapp/button/index"
}

在页面中使用

<van-button type="primary">按钮</van-button>

小程序开发环境-优化目录结构

项目的根目录
├── miniprogram  // 项目相关的代码夹
├── node_modules // npm包目录
├── package.json
├── project.config.json
├── package-lock.json
└── sitemap.json

原生微信小程序全流程(基础知识+项目全流程)

修改 project.config.json 中的配置项

{
  // 省略其他......
  "setting": {
    // 省略其他......
    "packNpmManually": true,
    "packNpmRelationList": [
      {
        "miniprogramNpmDistDir": "./miniprogram",
        "packageJsonPath": "package.json"
      }
    ]
  },
  "miniprogramRoot": "miniprogram/"
}

启用 less/sass

通过 less/sass 可以更好的管理 css 样式,通过 project.config.json 可以启用对 less/sass 的支持。

{
  "setting": {
    "useCompilerPlugins": ["sass"]
  }
}

然后将 .wxss 文件后缀改换成 .scss 即可。

启动项目

  1. 拉取代码
  2. 导入项目

使用小程序开发者工具导入【项目】的代码

3. 使用小程序开发者工具构建 npm

  1. 安装包:npm install
  2. 手动构建: 【工具】→【构建npm】

project.config.json的几个配置

{
  "miniprogramRoot": "miniprogram/",
  "setting": {
    "useCompilerPlugins": ["sass"],
    "packNpmManually": true,
    "packNpmRelationList": [
      {
        "packageJsonPath": "./package.json",
        "miniprogramNpmDistDir": "./miniprogram"
      }
    ],
  }
}
  • miniprogramRoot 项目的根目录为 miniprogram
  • setting.useCompilerPlugins 启用了 sass 支持
  • packNpmRelationList 指定了 npm 构建时所需的 package.json 的位置以及构建后代码的生成位置

4. 改成自己的appid

这个项目中的appid是别人的,如果我们需要改成自己的。

基础封装-消息反馈

将所有通用的工具方法封装到 utils/utils.js 中

/**
 * 用户消息反馈
 * @param {string} title 文字提示的内容
 */
export const toast = (title = '数据加载失败...') => {
  wx.showToast({
    title,
    mask: true,
    icon: 'none',
  })
}
// 挂载到全局对象 wx
wx.$toast = toast

app.js

// 在入口中执行 utils.js
import './utils/utils.js'
App({
  // ...
})

使用

 wx.$toast('//提示文字', "icon图标")
基础封装-网络请求

安装第三方的包-构建

  1. npm install wechat-http
  2. 安装完成后还必须要构建 npm后才可以使用

wechat-http用法与 axios 类似:

  • http.baseURL 配置接口基础路径
  • http.getGET 方法发起请求
  • http.postPOST 方法发起请求
  • http.putPUT 方法发起请求
  • http.deleteDELETE 方法发起请求
  • http.intercept 配置请求和响应拦截器
  • http 本身做为函数调用也能用于发起网络请求

二次封装

新建 utils/http.js 文件

// 导入 http 模块
import http from 'wechat-http'
// 基础路径
http.baseURL = 'https://live-api.itheima.net'
// 挂载到全局对象
wx.http = http
// 普通的模块导出
export default http

以全局对象方式调用时需要在入口中执行 utils/http.js

// 执行 uitls/http.js
import './utils/http.js'
App({
  // ...
})

配置响应拦截器

// 配置响应拦截器
http.intercept.response = function ({ data, config }) {

  // 检测接口是否正常返回结果
  if (data.code !== 10000) {
    wx.$toast()
    return Promise.reject(data)
  }

  // 只保留data数据,其它的都过滤掉
  return data.data
}
跳转传参

点击公告列表后将公告的ID通过地址参数传递到公告详情页面,在公告详情页 onLoad 生命周期中读取到公告 ID,然后调用接口获取公告详情的数据。

原生微信小程序全流程(基础知识+项目全流程)
原生微信小程序全流程(基础知识+项目全流程)

van-count-down 组件(倒计时)的应用
<van-count-down use-slot time="{{6000}}" bind:change="countDownChange">
  <text>{{timeData.seconds}}秒后重新获取</text>
</van-count-down>

time: 指定了倒计时多少毫秒

bind:change每隔一秒的回调,它会传出来当前的倒计时信息

 countDownChange(ev) {
    console.log(ev.detail)
    this.setData({
      timeData: ev.detail,
      getCodeBtnVisible: ev.detail.minutes === 0 && ev.detail.seconds === 0,
    })
  },
表单验证插件使用

先在data中设置mobile,再在模板中进行双向绑定

model:value=“{{mobile}}”

  1. 安装构建 表单验证码插件 wechat-validate
npm install wechat-validate
  1. 将插件导入到项目中
  • behaviors 将插件注入到页面中
  • rules 由插件提供的属性,用来定义数据验证的规则(类似于 Element UI)
  • validate 由插件提供的方法,根据 rules 的规则来对数据进行验证
// 导入表单验证插件
import validate from 'wechat-validate'
Page({
  data: {
    mobile: '' // 省略其他
  },
  behaviors: [validate], // 将插件注入到页面实例中
  rules: {
    mobile: [
      {required: true, message: '请填写手机号码!'},
      {pattern: /^1[3-8]\d{9}$/, message: '请填写正确的手机号码!'}
    ]
  },
  getSMSCode() {
    // 获取验证结果
    const { valid, message } = this.validate('mobile')
    // 如果验证不合法则不再执行后面的逻辑
    if (!valid) return wx.$toast(message)
    console.log('getCode')
    this.setData({ getCodeBtnVisible: false })
  },
})
保存token-跳转
  1. 在app.js中设置setToken方法,保存到本地存储
App({
  // ...
  setToken(key, token) {
 		// 将 token 记录在应用实例中   
    this[key] = token
    // 将 token 存入本地
    wx.setStorageSync(key, token)
  }
})

2.点击登录 | 注册发送请求成功之后

  const app = getApp()    //小程序中获取全局的实例对象
  app.setToken('token', res.token)
  app.setToken('refreshToken', res.refreshToken)
 //  跳转
  const url = '/pages/profile/index'
  wx.redirectTo({ url })
登录检测-鉴权组件

原生微信小程序全流程(基础知识+项目全流程)

1.在根目录中创建 components 文件夹用来存放全局的组件,然后通过小程序开发者工具创建一个名为 authorization 的组件

2.接下来全局来注册这个组件,保证任何页面中都可以直接应用 authorization 组件

{
  "usingComponents": {
    "authorization": "/components/authorization/index"
  },
}

3.到用户信息页面中应用 authorization 使用做为页面根节点

<authorization>
  	...
</authorization>

4.在authorization中补充插槽和状态

<!--components/authorization/index.wxml-->
<slot wx:if="{{isLogin}}"></slot> 
  data: {
    isLogin: false
  },

读取本地存储token

读取本地存储的 token 数据,用于判断是否曾登录过

// app.js
App({
  ......
    getToken() {
    // 将 token 数据记到应用实例中
    // return this.token = wx.getStorageSync('token')
    return this.token
  }
})

在组件内读token并处理

data: {
  isLogin: false
},
lifetimes: {
  attached() {
    const isLogin = !!getApp().getToken()    
    //const app = getApp()  const isLogin = !!app.getToken()
    this.setData({ isLogin })
    if (!isLogin) {
      wx.redirectTo({ url: '/pages/login/index' })
    }
  }
},

地址重定向,登录成功后跳回到原来的页面

authoirzation 组件检测登录时获取当前页面栈实例,并在跳转到登录页面时在 URL 地址上拼凑参数:

// /components/authorization/index.js
Component({
  // ...
  lifetimes: {
    attached() {
      // 获取登录状态
      const isLogin = !!getApp().token
      // 变更登录状态
      this.setData({ isLogin })
      // 获取页面栈
      const pageStack = getCurrentPages()
      // 获取页面路径
      const currentPage = pageStack.pop()
      // 未登录的情况下跳转到登录页面
      if (!isLogin) {
        wx.redirectTo({
          url: '/pages/login/index?redirectURL=/' + currentPage.route,
        })
      }
    },
  },
})

原生微信小程序全流程(基础知识+项目全流程)

用户管理-显示默认值

app.js中添加初始值

App({
  globalData: {},
  userInfo: { avatar: '', nickName: '微信用户1' }
}

在onLoad中加载值

data: {
  avatar: '',
  nickName: ''
},
onLoad() {
  const app = getApp()
  console.log(app.userInfo)
  const { avatar, nickName } = app.userInfo
  this.setData({ avatar, nickName })
  // 用户未登录时不必请求
  app.token && this.getUserProfile()
},
配置请求拦截器

将用户的登录状态通过自定义的头信息 Authorization 随接口调用时一起发送到服务端。

// 导入 wechat-http 模块
import http from 'wechat-http'
// 配置接口基础路径
http.baseURL = 'https://live-api.itheima.net'
// 配置请求拦截器

http.intercept.request = function (options) {
  console.log('请求拦截器', options.header)
  // 扩展头信息
  const defaultHeader = {}
  // 身份认证
  const token = getApp().getToken()
  if (token) {
    defaultHeader.Authorization = 'Bearer ' + getApp().getToken()
  }
  // 与默认头信息合并
  options.header = Object.assign({}, defaultHeader, options.header)
  // 处理后的请求参数
  return options
}

注:传递 token 时需要拼凑字符串前缀 "Bearer "

文件上传(例:更新用户头像)

获取用户选择的头像地址,通过 wx.uploadFile 将图片上传到服务端。

wx.uploadFile 的基本语法:

  • url 上传接口地址
  • filePath 待上传文件的临时路径(该路径只能用于小程序内部)
  • name 接口接收上传文件的数据名称(由后端指定)
  • formData 除上传文件外的其它数据
  • header 自定义头信息
  • success 上传成功的回调函数
  • fail 上传失败后的回调函数
  • complete 上传完成时的回调(无论成功或失败)

注:该 API 不支持返回 Promise,调用该 API 时,需要提前在小程序管理后台添加服务器域名。

<!-- pages/profile/index.wxml -->
<authorization>
  <view class="profile">
    <van-cell center title="头像">
      <van-icon slot="right-icon" name="arrow" size="16" color="#c3c3c5" />
      <button
        class="button"
        size="mini"
        hover-class="none"
        bind:chooseavatar="updateUserAvatar" //事件,事件名全部小写,A千万不要大写,不会触发
        open-type="chooseAvatar">    //与上边事件对应
        <image class="avatar" src="{{avatar}}"></image>
      </button>
    </van-cell>
    ...
  </view>
</authorization>
// pages/profile/index.js
const pageStack = getCurrentPages()
Page({
    ...
  // 更新用户头像
  updateUserAvatar(ev.detail.avatarUrl) {
    // 调用 API 上传文件
    wx.uploadFile({
      // 接口地址
      url: wx.$http.baseURL + '/upload',
      // 待上传的文件路径
      filePath: avatar,
      name: 'file',// wx.uploadFile 要求必传。
      header: {
        Authorization: 'Bearer ' + getApp().getToken()   // 用户登录状态
      },
      formData: { // 是我们自己的接口文档的要求。可以不传,默认就是avatar
        type: 'avatar'
      },
      success: (result) => {
        console.log(JSON.parse(result.data))
        const res = JSON.parse(result.data)
        // console.log(res.data.url)
        const avatar = res.data.url
        // 1. 在页面上显示
        this.setData({ avatar })
        // 2. 更新全局数据
        const app = getApp()
        app.userInfo.avatar = avatar
        // 3. 通过页面栈找到my/index页面,更新它的avatar信息
        const pages = getCurrentPages()
        // pages[0].data.nickName = nickName 直接修改数据不会让视图更新
        // 调用setData更新
         pages[0].setData({ avatar })
      }
    })
  }
})

上述代码中通过 wx.http.baseURL 获取接口服务器地址,通过应用实例获取 token

refresh_token使用

原生微信小程序全流程(基础知识+项目全流程)

  • token:
    • 作用:在访问一些接口时,需要传入token,就是它。
    • 有效期:2小时(安全)。
  • refresh_token
    • 作用: 当token的有效期过了之后,可以使用它去请求一个特殊接口(这个接口也是后端指定的,明确需要传入refresh_token),并返回一个新的token回来(有效期还是2小时),以替换过期的那个token。
    • 有效期:14天。(最理想的情况下,一次登陆可以持续14天。)

原生微信小程序全流程(基础知识+项目全流程)

1.用户在首次完成登录时会分别得到 token 和 refresh_token

2.当 token 失效后(例如2小时之后),调用接口A会返回 401 状态码(这是与后端约定好的规则)

3.检测状态码是否为 401**,如果是,则携带refreshToken去调用刷新token的接口

4.刷新 token 的接口后会返回新的 token 和 refreshToken

5.把401的接口A重新发送一遍

注意:
refresh_token也是有过期时间的,只不过一般会比token过期时间更长一些。这就是为啥如果某个应用我们天天打开,则不会提示我们登录,如果是有几周或更长时间去打开时,会再次要求我们登录。
refresh_token一个更常见的名字叫token无感刷新。

refreshToken功能-基本实现
// 响应拦截器
http.intercept.response = async ({ statusCode, data, config }) => {
  console.log(statusCode, data, config)
  // console.log(statusCode) // http 响应状态码
  // console.log(config) // 发起请求时的参数
  if (data.code === 401) {
    const app = getApp()
    // 调用接口获取新的 token
    const res = await http({
      url: '/refreshToken',
      method: 'POST',
      header: {
        Authorization: 'Bearer ' + app.getToken('refreshToken'),
      }
    })

    app.setToken('token', res.token)
    app.setToken('refreshToken', res.refreshToken)
     // 获得新的token后需要重新发送刚刚未完成的请求
    config = Object.assign(config, {
      header: {
        // 更新后的 token
        Authorization: 'Bearer ' + res.token,
      },
    })
    // 重新发请求
    return http(config)
  }
  // 拦截器处理后的响应结果
  if (data.code === 10000) {
    return data.data
  } else {
    wx.$toast(data.message || '请求失败')
    return Promise.reject(data.message)
  }
}
refreshToken也过期的特殊处理

原生微信小程序全流程(基础知识+项目全流程)

完整版响应拦截器

// 响应拦截器
http.intercept.response = async ({ statusCode, data, config }) => {
  console.log(statusCode, data, config)
  // console.log(statusCode) // http 响应状态码
  // console.log(config) // 发起请求时的参数
  if (data.code === 401) {
++    if (config.url.includes('/refreshToken')) {
++      console.log('/refreshToken过期了')
++      // 获取当前页面的路径,保证登录成功后能跳回到原来页面
++      const pageStack = getCurrentPages()
++      const currentPage = pageStack.pop()
++      const redirectURL = currentPage.route
++      // 跳由跳转(登录页面)
++      wx.redirectTo({
++        url: '/pages/login/index?redirectURL=/' + redirectURL,
++      })
++      return Promise.reject('refreshToken也过期了,就只能重新登录了')
++    }
    const app = getApp()
    // 调用接口获取新的 token
    const res = await http({
      url: '/refreshToken',
      method: 'POST',
      header: {
        Authorization: 'Bearer ' + app.getToken('refreshToken'),
      }
    })

    app.setToken('token', res.token)
    app.setToken('refreshToken', res.refreshToken)
    config = Object.assign(config, {
      header: {
        // 更新后的 token
        Authorization: 'Bearer ' + res.token,
      },
    })
    // 重新发请求
    return http(config)
  }
  // 拦截器处理后的响应结果
  else if (data.code === 10000) {
    return data.data
  } else {
    wx.$toast(data.message || '请求失败')
    return Promise.reject(data.message)
  }
}
腾讯位置服务-需要提前注册

文档地址:https://lbs.qq.com/miniProgram/jsSdk/jsSdkGuide/jsSdkOverview

使用步骤(共4步)

  1. 申请开发者密钥(key):申请密钥(地址:https://lbs.qq.com/dev/console/application/mine)
  2. 开通webserviceAPI服务:控制台 ->应用管理 -> 我的应用->添加key-> 勾选WebServiceAPI -> 保存(小程序SDK需要用到webserviceAPI的部分服务,所以使用该功能的KEY需要具备相应的权限)

原生微信小程序全流程(基础知识+项目全流程)

3.下载微信小程序JavaScriptSDK,微信小程序JavaScriptSDK v1.1(https://mapapi.qq.com/web/miniprogram/JSSDK/qqmap-wx-jssdk1.1.zip) JavaScriptSDK v1.2(https://mapapi.qq.com/web/miniprogram/JSSDK/qqmap-wx-jssdk1.2.zip) js文件

4.安全域名设置,在小程序管理后台-> 开发 -> 开发管理 -> 开发设置 -> “服务器域名” 中设置request合法域名,添加https://apis.map.qq.com

地理定位-wx.getLocation

原生微信小程序全流程(基础知识+项目全流程)

获取用户所在位置的经纬度。在小程序中调用这个接口时必须先在 app.json 中申请调用权限(开发环境可以省略)。

//app.json
{
  "requiredPrivateInfos": [
++    "getLocation"
  ],
  "permission": {
    "scope.userLocation": {
      "desc": "你的位置信息将用于小程序位置接口的效果展示"
    }
  },
}

原生微信小程序全流程(基础知识+项目全流程)

Page({
  onLoad() {
    this.getLocation()
  },
  async getLocation() {
    const res = await wx.getLocation() // 要提前申请权限
    console.log(res)
  },
})

wx.getLocation返回的结果格式大致如下:

accuracy: 65
errMsg: "getLocation:ok"
horizontalAccuracy: 65
latitude: 30.88131
longitude: 114.37509
speed: -1
verticalAccuracy: 65

wx.getLocation 只能得到经纬度信息

逆地址解析-reverseGeocoder

由坐标 → 坐标所在位置的文字描述的转换,输入坐标返回地理位置信息和附近poi列表

文档地址:https://lbs.qq.com/miniProgram/jsSdk/jsSdkGuide/methodReverseGeocoder

1. 导入 QQMapWX 并设置好 key

原生微信小程序全流程(基础知识+项目全流程)

2.在代码中补充getPoint方法:

  1. 调用接口把经纬度转换成对应位置的文字
  2. 保存文字到address
// 导入位置服务实例
import QQMap from '../../../utils/qqmap'

Page({   ......
  onLoad() {
    this.getLocation()
  },
  async getLocation() {
    // 调用小程序API获取经纬度等信息
    const { latitude, longitude } = await wx.getLocation()  //获取用户经纬度
    this.getPoint(latitude, longitude)  
  },
  getPoint(latitude, longitude) {
    // 逆地址解析(根据经纬度来获取地址)
    QQMap.reverseGeocoder({
      location: [latitude, longitude].join(','),
      success: (result) => {
        const address = res.address
        this.setData({ address })
      },
    })
  }
})

3.渲染页面

 <van-cell-group border="{{false}}" title="当前地点">
  <van-cell title="{{address}}" border="{{false}}">  //border="{{false}}"设置无边框样式
    <text bind:tap="chooseLocation" class="enjoy-icon icon-locate">重新定位</text>
  </van-cell>
</van-cell-group>
QQMap地点搜索—search

根据当前的定位,调用 QQMap.search() 找到周边的信息。

搜索周边poi(Point of Interest),比如:“酒店” “餐饮” “娱乐” “学校” 等等
文档地址:https://lbs.qq.com/miniProgram/jsSdk/jsSdkGuide/methodSearch

原生微信小程序全流程(基础知识+项目全流程)

在小程序中调用这个接口时必须要在 app.json 中申请调用权限

//app.json
{
  "requiredPrivateInfos": [
++    "chooseLocation"
  ]
}
// 选择新的位置
async chooseLocation() {
  // 调用小程序 API 获取新的位置
  const { latitude, longitude } = await wx.chooseLocation()
  this.getPoint(latitude, longitude)   // 获取新的位置经纬度
},

getPoint(latitude, longitude) {
  wx.showLoading({
    title: '正在加载...',      // 显示loading提示
  })

  // 逆地址解析(根据经纬度来获取地址)
  QQMap.reverseGeocoder({
    location: [latitude, longitude].join(','),
    success: ({ result: { address } }) => {
      this.setData({ address })
    },
  })

  QQMap.search({
    keyword: '住宅小区', //搜索关键词
    location: [latitude, longitude].join(','), //设置周边搜索中心点
    page_size: 5,   //只显示5条信息,不设置此项默认为10
    success: (result) => {     //success 是一个回调函数,表示搜索成功后的处理逻辑。
      const points = result.data
      this.setData({ points }) // 渲染数据
    },
    fail: (err) => {    //fail 是一个回调函数,表示搜索失败后的处理逻辑。
      console.log(err.message)
    },
    complete: () => {//complete 是一个回调函数,表示搜索结束后的处理逻辑(无论搜索成功还是失败)
      wx.hideLoading()   // 隐藏loading提示
    },
  })
},
重新定位-wx.chooseLocation

原生微信小程序全流程(基础知识+项目全流程)

申请权限

获取用户指定位置的经纬度。在小程序中调用这个接口时必须要在 app.json 中申请调用权限

{
  "requiredPrivateInfos": [
    "chooseLocation"
  ]
}

示例代码:

// 选择新的位置
async chooseLocation() {
  // 调用小程序 API 获取新的位置
  const { latitude, longitude } = await wx.chooseLocation()
  // 获取新的位置附近的小区
  this.getPoint(latitude, longitude)

  console.log('起点位置:', latitude, longitude)
},

getPoint(latitude, longitude) {
  // 显示loading提示
  wx.showLoading({
    title: '正在加载...',
  })

  // 逆地址解析(根据经纬度来获取地址)
  QQMap.reverseGeocoder({
    location: [latitude, longitude].join(','),
    success: ({ result: { address } }) => {
      // console.log(address)
      // 数据数据
      this.setData({ address })
    },
  })

  QQMap.search({
    keyword: '住宅小区', //搜索关键词
    location: [latitude, longitude].join(','), //设置周边搜索中心点
    page_size: 5,
    success: (result) => {
      // console.log(result)
      // 过滤掉多余的数据
      const points = result.data.map(({ id, title, _distance }) => {
        return { id, title, _distance }
      })

      // console.log(points)
      // 渲染数据
      this.setData({ points })
    },
    fail: (err) => {
      console.log(err.message)
    },
    complete: () => {
      // 隐藏loading提示
      wx.hideLoading()
    },
  })
},
图片收集(收集身份证信息)

小程序没有input type="file"用于选择文件,要实现类似功能,用以下api:
wx.chooseMedia**:**拍摄或从手机相册中选择图片或视频。
低版本请用wx.chooseImage。

1.绑定事件选择身份证图片上传。

2.发送请求上传图片,拿到上传后的图片地址。

Page({
 ...
  async uploadPicture(ev) {
    // 获取图片临时地址
    const res = await wx.chooseMedia({
      count: 1,
      mediaType: ['image'],
      sizeType: ['compressed'],
    })
    const tempPath = res.tempFiles[0].tempFilePath
    const type = ev.mark.type
    // 上传图片到服务器
    wx.uploadFile({
      url: wx.$http.baseURL + '/upload',
      filePath: tempPath,
      name: 'file',
      header: {
        Authorization:  'Bearer ' +  getApp().getToken(),
      },
      success: (res) => {
        const res = JSON.parse(result.data)
        console.log(res.data.url) 							// 上传成功的回调
        this.setData({ [type]: res.data.url })
      },
    })
  },
})
校验表单信息

获取了全部的表单数据后再对数据进行验证,说明如下:

  • 房屋的信息是通过url地址获取的不需要验证
  • 性别可以指定默认值也不需要验证
  • 剩下的数据通过 wechat-validate 插件进行验证:
// house_pkg/pages/form/index.js
// 导入表单验证插件
import wxValidate from 'wechat-validate'
Page({
  behaviors: [wxValidate],
  data: {
    point: '',
    building: '',
    room: '',
    name: '',
    gender: 1,
    mobile: '',
    idcardFrontUrl: '',
    idcardBackUrl: '',
  },
  rules: {
    name: [
      { required: true, message: '业主姓名不能为空!' },
      { pattern: /^[\u4e00-\u9fa5]{2,5}$/, message: '业主姓名只能为中文!' },
    ],
    mobile: [
      { required: true, message: '业主手机号不能为空!' },
      { pattern: /^1[3-8]\d{9}$/, message: '请填写正确的手机号!' },
    ],
    idcardFrontUrl: [
      { required: true, message: '请上传身份证国徽面!' }
    ],
    idcardBackUrl: [
      { required: true, message: '请上传身份证照片面!' }
    ],
  },
})
表单收集—收集预约日期

1.时间选择控件:van-datetime-pickerhttps://vant-contrib.gitee.io/vant-weapp/#/datetime-picker

2.弹出层控件:van-popuphttps://vant-contrib.gitee.io/vant-weapp/#/popup

  1. 给选择日期单元绑定事件——显示选择日期弹层(van-popup)

2.给日期控件绑定确认事件confirm

3.在确认回调中获取时间戳

4.将时间戳格式化并显示

selectDate(ev) {
  // console.log(ev)
  this.setData({
    currentDate: ev.detail,
    appointment: wx.$utils.formatDate(ev.detail), // 格式化日期
  })
  this.closeDateLayer()
},

补充格式化日期的函数

formatDate(time) {
  const d = new Date(time)
  const year = d.getFullYear()
  let month = d.getMonth() + 1 // 获取月份,月份从0开始,所以加1
  let day = d.getDate()
  month = month < 10 ? '0' + month : month
  day = day < 10 ? '0' + day : day
  return `${year}-${month}-${day}`
},

wxml

<van-cell title-width="100" title="预约日期" value-class="{{appointment && 'active-cell'}}" bind:click="openDateLayer"
        is-link value="{{appointment || '请选择上门维修日期'}}" />
...省略
<van-popup bind:close="closeDateLayer" round show="{{ dateLayerVisible }}" position="bottom">
  <van-datetime-picker bind:cancel="closeDateLayer" bind:confirm="selectDate" type="date" value="{{ currentDate }}"
    min-date="{{ 1664582400000 }}" />
</van-popup>
加载更多

在scroll-view上添加 bindscrolltolower=“loadMore”

  <scroll-view 
  bindscrolltolower="loadMore"
  show-scrollbar="{{false}}" enhanced scroll-y>
    <view class="repairs">
      <view class="repairs-title">我的报修</view>
loadMore() {
    // if(是否有更多的数据)
    if (this.data.total <= this.data.list.length)
      return
    console.log('更多的数据')
    // 把页码+1,发请求,请求回来的数据要 追加 到原数组
    // [5] → [10]
    this.data.page++
    this.getList()

  },
  async getList() {
    // 发请求
    const { pageTotal, total, rows: list } = await wx.$http.get('/repair', { current: this.data.page, pageSize: this.data.pageSize })
    console.log(list, pageTotal, total)
    // 渲染数据
    // 在原来的基础上添加数据
    this.setData({ total, list: [...this.data.list, ...list] })
  },

路线规划

路线规划是常见的一个功能,它用来在地图上展示两点间路线,要使用这个功能需要用到两部分的知识:

  1. 小程序提供的 map 组件用来在页面渲染地图

map | 微信开放文档https://developers.weixin.qq.com/miniprogram/dev/component/map.html)

  1. 腾讯位置服务计算两点路线的所有坐标点(经纬度)

首先来看小程序提供的地图组件 map

  • latitude 指定地图中心点的纬度
  • longitude 指定地图中心点的经功
  • scale 指定地图初始的缩放比例,取值范围 3 - 20
  • markers 地图上的标记点
  • polyline 地图上的路线

latitude、longitude、scale 相对容易理解,重点来看一下 markers 的使用:

repqir_pkg/pages/detail/index.js

Page({
  data: {
    markers: [
      {
        id: 1,
        latitude: 40.22077,
        longitude: 116.23128,
        width: 24,
        height: 30,
      },
      {
        id: 2,
        latitude: 40.225857999999995,
        longitude: 116.23246699999999,
        iconPath: '/static/images/marker.png',
        width: 40,
        height: 40,
      },
    ],
  }
})

在定义标记点时每个标记点必须要指定 ID 属性,否则会有错误产生,通过 iconPath 可以自定义标记点的图片,width/height 定义标记点的大小尺寸。

polyline

polyline 用来在在图上展示两点间的行进路线,需要传递给它路线对应的坐标点(很多个点组成的线),获取这些坐标点需要通过位置服务计算得到。

计算两点间路线的坐标点需要用到位置服务的[路线规划]https://lbs.qq.com/miniProgram/jsSdk/jsSdkGuide/methodDirection方法

repqir_pkg/pages/detail/index.js

// repqir_pkg/pages/detail/index.js
Page({
  data: {},
  onLoad() {
    // 生成路线
    this.getPolyline()
  },
  // 调用位置服务(路线规划)
  getPolyline() {
    qqMap.direction({
      mode: 'bicycling',
      from: '40.227978,116.22998',
      to: '40.22077,116.23128',
      success: ({ result }) => {
        const coors = result.routes[0].polyline
        const points = []
        //坐标解压(返回的点串坐标,通过前向差分进行压缩)
        for (let i = 2; i < coors.length; i++) {
          coors[i] = Number(coors[i - 2]) + Number(coors[i]) / 1000000
        }
        // 获取经纬度
        for (let i = 0; i < coors.length; i += 2) {
          points.push({ latitude: coors[i], longitude: coors[i + 1] })
        }
        // 渲染数据
        this.setData({
          latitude: points[30].latitude,
          longitude: points[30].longitude,
          polyline: [
            {points, color: '#5591af', width: 4},
          ],
        })
      },
    })
  },
})

计算出来的坐标点是经过压缩的,需要按着官方指定的方式对数据进行解压才可以获取真正的坐标点,并且为了适应小程序地图组件的用法,还需要对数据进行二次的加工。

关于数据的处理大家只需要参考文档来实现就可以,可以说之所这么操作是腾讯位置服务规订好的,做为开发者按着官方提从的方法来应用即可。

自定义分享

[onShareAppMessage]https://developers.weixin.qq.com/miniprogram/dev/reference/api/Page.html#onShareAppMessage-Object-object

监听用户点击页面内转发按钮(button 组件 open-type=“share”)或右上角菜单“转发”按钮的行为,并自定义转发内容。

注意:只有定义了此事件处理函数,右上角菜单才会显示“转发”按钮

Page({
  onLoad({ id, encryptedData }) {
    this.getPassport(id)
    this.getPassportShare(encryptedData)
  },
  // 获取访客详情(通行证)
  async getPassport(id) {
    // 检测是否存在 id
    if (!id) return
    // 调用接口
    const passport = await wx.$http.get('/visitor/' + id)
    // 渲染数据
    this.setData({ ...passport })
  }
  async getPassportShare(encryptedData) {
    // 检测是否存在 id
    if (!encryptedData) return
    // 调用接口
    const passport = await wx.$http.get('/visitor/share/' + this.data.encryptedData)
    // 渲染数据
    this.setData({ passport })
  },
  onShareAppMessage() {
    return {
      title: '查看通行证',
      path: '/visitor_pkg/pages/passport/index?encryptedData=' + encryptedData,
      imageUrl: 'https://enjoy-plus.oss-cn-beijing.aliyuncs.com/images/share_poster.png',
    }
  },
})
保存到本地api介绍

[saveImageToPhotosAlbum]https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.saveImageToPhotosAlbum.html

保存图片到系统相册。

[getImageInfo]https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.getImageInfo.html

获取图片信息。网络图片需先配置 download 域名才能生效。

保存到本地实现

1.给按钮绑定事件,调用getImageInfo获取图片临时路径

2.调用saveImageToPhotosAlbum传入临时路径完成保存功能文章来源地址https://www.toymoban.com/news/detail-495075.html

// 保存图片
  async saveQRCode() {
    try {
      // 读取图片信息
      const { path } = await wx.getImageInfo({
        src: this.data.url,
      })
      // 保存图片到相册
      wx.saveImageToPhotosAlbum({ filePath: path })
    } catch (err) {
      wx.$toast('保存图片失败,稍后重试!')
    }
  },

ush({ latitude: coors[i], longitude: coors[i + 1] })
}
// 渲染数据
this.setData({
latitude: points[30].latitude,
longitude: points[30].longitude,
polyline: [
{points, color: ‘#5591af’, width: 4},
],
})
},
})
},
})


计算出来的坐标点是经过压缩的,需要按着官方指定的方式对数据进行解压才可以获取真正的坐标点,并且为了适应小程序地图组件的用法,还需要对数据进行二次的加工。

关于数据的处理大家只需要参考文档来实现就可以,可以说之所这么操作是腾讯位置服务规订好的,做为开发者按着官方提从的方法来应用即可。

### 自定义分享

**[onShareAppMessage]https://developers.weixin.qq.com/miniprogram/dev/reference/api/Page.html#onShareAppMessage-Object-object**

监听用户点击页面内转发按钮([button](https://developers.weixin.qq.com/miniprogram/dev/component/button.html) 组件 open-type="share")或右上角菜单“转发”按钮的行为,并自定义转发内容。

**注意:只有定义了此事件处理函数,右上角菜单才会显示“转发”按钮**

```js
Page({
  onLoad({ id, encryptedData }) {
    this.getPassport(id)
    this.getPassportShare(encryptedData)
  },
  // 获取访客详情(通行证)
  async getPassport(id) {
    // 检测是否存在 id
    if (!id) return
    // 调用接口
    const passport = await wx.$http.get('/visitor/' + id)
    // 渲染数据
    this.setData({ ...passport })
  }
  async getPassportShare(encryptedData) {
    // 检测是否存在 id
    if (!encryptedData) return
    // 调用接口
    const passport = await wx.$http.get('/visitor/share/' + this.data.encryptedData)
    // 渲染数据
    this.setData({ passport })
  },
  onShareAppMessage() {
    return {
      title: '查看通行证',
      path: '/visitor_pkg/pages/passport/index?encryptedData=' + encryptedData,
      imageUrl: 'https://enjoy-plus.oss-cn-beijing.aliyuncs.com/images/share_poster.png',
    }
  },
})
保存到本地api介绍

[saveImageToPhotosAlbum]https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.saveImageToPhotosAlbum.html

保存图片到系统相册。

[getImageInfo]https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.getImageInfo.html

获取图片信息。网络图片需先配置 download 域名才能生效。

[外链图片转存中…(img-M8SkNwoO-1687267859406)]

保存到本地实现

1.给按钮绑定事件,调用getImageInfo获取图片临时路径

2.调用saveImageToPhotosAlbum传入临时路径完成保存功能

// 保存图片
  async saveQRCode() {
    try {
      // 读取图片信息
      const { path } = await wx.getImageInfo({
        src: this.data.url,
      })
      // 保存图片到相册
      wx.saveImageToPhotosAlbum({ filePath: path })
    } catch (err) {
      wx.$toast('保存图片失败,稍后重试!')
    }
  },

到了这里,关于原生微信小程序全流程(基础知识+项目全流程)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 第四章 云原生架构之Kubernetes基础知识

    ​ Kubernetes是一个可移植、可扩展的开源平台,用于管理容器化的工作负载和服务,简称 K8S。 K8S的本质是一组服务器集群,可以在对应服务器集群的每个节点上运行程序,来对节点中的容器进行管理 。类似Master-Work方式,每个服务器上安装特定的k8s组件,就可以形成集群,然

    2024年02月17日
    浏览(46)
  • 【三维重建】三维重构基础知识、三维数据、重建流程

    1.使用几何建模软件,通过人机交互生成人为控制下的三维:3DMAX、Maya、AutoCAD、UG 2.获取真实的物体形状:三维重构 三维图像重构: 摄像机获取图像,对图像分析处理,结合CV知识推导出现实中物体的三维信息 从二维图像到三维空间的重构(模仿生物两只眼睛观察物体产生的

    2024年02月02日
    浏览(56)
  • webpack基础知识二:说说webpack的构建流程?

    一、运行流程 webpack 的运行流程是一个串行的过程,它的工作流程就是将各个插件串联起来 在运行过程中会广播事件,插件只需要监听它所关心的事件,就能加入到这条webpack机制中,去改变webpack的运作,使得整个系统扩展性良好 从启动到结束会依次执行以下三大步骤: 初

    2024年02月14日
    浏览(43)
  • 【云原生持续交付和自动化测试】5.3 持续交付和DevOps实践基础知识

    往期回顾: 第一章:【云原生概念和技术】 第二章:【容器化应用程序设计和开发】 第三章:【基于容器的部署、管理和扩展】 第四章:【微服务架构设计和实现】 第五章:【5.1 自动化构建和打包容器镜像】 第五章:【5.2 自动化测试和集成测试】 云原生下对持续交付(

    2024年02月09日
    浏览(53)
  • 基础课19——客服系统知识库的搭建流程

    注意:我们在做业务数据收集时,往往是甲方提供给我们的,这时就需要确定一个标准,否则对知识库梳理工作会带来很大的难度,建议和甲方沟通确认一个双方都统一的知识库原材料。 在创建知识库时,我们最常见的就是把问题分为单轮、多轮,来满足不同场景的需求,如

    2024年02月05日
    浏览(43)
  • 网络编程 tcp udp http编程流程 网络基础知识

    OSI分层:应用层 表示层 会话层 传输层 网络层 数据链路层 物理层 tcp/ip: 应用层 传输层 网络层 数据链路 ip地址:唯一标识一台主机 ipv4 32位 ipv6 128位 寻址 可以反映物理上的一个变化 MAC地址:48 固化在计算机中 ip地址又两部分构成:网络号+主机号 端口号:标识一个应用程序

    2024年02月13日
    浏览(76)
  • 云原生可观测框架 OpenTelemetry 基础知识(架构/分布式追踪/指标/日志/采样/收集器)...

    OpenTelemetry 是一个开源的可观测性框架,由云原生基金会(CNCF)托管。它是 OpenCensus 和 OpenTracing 项目的合并。旨在为所有类型的可观测信号(如跟踪、指标和日志)提供单一标准。 https://opentelemetry.io https://www.cncf.io https://opencensus.io OpenTelemetry 指定了如何收集遥测数据并将其发送到

    2024年01月16日
    浏览(62)
  • Gpt微信小程序搭建的前后端流程 - 前端小程序部分-1.基础页面框架的静态设计(二)

    Gpt微信小程序搭建的前后端流程 - 前端小程序部分-1.基础页面框架的静态设计(二) 在开始这个专栏,我们需要找一个小程序为参考,参考和仿照其界面,聊天交互模式。 这里参考小程序- 小柠AI智能聊天 ,可自行先体验。 该小程序主要提供了以下几点 功能向需求 : 每天免费

    2024年02月14日
    浏览(43)
  • 【前端知识】React 基础巩固(三十二)——Redux的三大原则、使用流程及实践

    单一数据源 整个应用程序的state被存储在一颗object tree 中,并且这个object tree 只存储在一个store中; Redux并没有强制让我们不能创建多个Store,但是那样做不利于数据维护; 单一的数据源可以让整个应用程序的state变得方便维护、追踪、修改; State是只读的 唯一修改State的方法

    2024年02月15日
    浏览(62)
  • mysql数据库面试题基础知识,Hadoop之MapReduce04,腾讯java面试流程

    该方法的执行过程比较复杂,我们慢慢来分析,首先来看下简化的时序图 3.1waitForCompletion public boolean waitForCompletion(boolean verbose ) throws IOException, InterruptedException, ClassNotFoundException { // 判断任务的状态,如果是DEFINE就提交 if (state == JobState.DEFINE) { submit(); } if (verbose) { // 监听并且

    2024年04月14日
    浏览(62)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包