当前位置:   article > 正文

web前端学习(三):微信小程序基于H5规范,开发Android应用程序_基于h5系统开发

基于h5系统开发

前言:

  1. 微信小程序开发框架的目标是通过尽可能简单,高效的方式让开发者可以在微信中开发具有原生APP体验的服务。
  2. 整个小程序框架系统分为两部分,逻辑层,视图层,小程序提供了自己的视图层描述语言, WXMLWxss ,以及基于javascript的逻辑层框架, 并在视图层与逻辑层间提供了数据传输和事件系统,让开发者能够专注于数据与逻辑。

优点:

  1. 微信用户量大,开发门槛低,推广成本低,跨平台,无需安装。

  2. 微信小程序开发门槛低,可以实现消息通知,线下扫码,公众号关联等七大功能

  3. 同类型的小程序: 支付宝小程序,qq小程序。

微信小程序APP(ID): xxxxx

1.配置环境

1.应用配置

1.项目结构

|----|---index /首页
        |---index.js /首页逻辑文件
        |---index.json //首页配置文件
        |---index.wxml /首页视图文件
        |---index.Wxss //首页样式文件
|----|----1ogs“日志页面
        |---logs.js
        |---1ogs.json
        |---logs.wxml
        |---logs.wxss
|---utils /1工具文件夹
    |--util.js
|---app.js    //全局入口文件
|---app.json //全局配置文件
|---app.NXss  //全局样式文件
|---project.config.json //项目配置文件
|---sitemap.json //索引配置文件

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

2.app.json应用配置项

小程序根目录下的 app.json 文件用来对微信小程序进行全局配置,决定页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 等。完整配置项说明请参考小程序全局配置

以下是一个包含了部分常用配置选项的 app.json

{
  "pages": [
    "pages/index/index",
    "pages/logs/index"
  ],
  "window": {
    "navigationBarTitleText": "Demo"
  },
  "tabBar": {
    "list": [{
      "pagePath": "pages/index/index",
      "text": "首页"
    }, {
      "pagePath": "pages/logs/index",
      "text": "日志"
    }]
  },
  "networkTimeout": {
    "request": 10000,
    "downloadFile": 10000
  },
  "debug": true
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

其中 list 接受一个数组,只能配置最少 2 个、最多 5 个 tab。tab 按数组的顺序排序,每个项都是一个对象,其属性值如下:

属性类型必填说明
pagePathstring页面路径,必须在 pages 中先定义
textstringtab 上按钮文字
iconPathstring图片路径,icon 大小限制为 40kb,建议尺寸为 81px * 81px,不支持网络图片。 positiontop 时,不显示 icon。
selectedIconPathstring选中时的图片路径,icon 大小限制为 40kb,建议尺寸为 81px * 81px,不支持网络图片。 positiontop 时,不显示 icon。

2.页面配置

每一个小程序页面也可以使用同名 .json 文件来对本页面的窗口表现进行配置,页面中配置项会覆盖 app.jsonwindow 中相同的配置项。完整配置项说明请参考小程序页面配置

{
  "navigationBarBackgroundColor": "#ffffff",
  "navigationBarTextStyle": "black",
  "navigationBarTitleText": "微信接口功能演示",      // 页面导航标题
  "backgroundColor": "#eeeeee",
  "backgroundTextStyle": "light"
  "enablePullDownRefresh":true,           // 上拉刷新,开启后onPullDownRefresh才有效
  "backgroundTextStyle": "dark",          // 上拉刷新的等待图标
  "usingComponents": {                    // 引入组件
       ...
   }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

2.小程序生命周期

1.应用级生命周期

属性类型默认值必填说明最低版本
1.onLaunchfunction生命周期回调——监听小程序初始化。
2.onShowfunction生命周期回调——监听小程序启动或切前台。
3.onHidefunction生命周期回调——监听小程序切后台。
onErrorfunction错误监听函数。
onPageNotFoundfunction页面不存在监听函数。1.9.90
onUnhandledRejectionfunction未处理的 Promise 拒绝事件监听函数。2.10.0
onThemeChangefunction监听系统主题变化2.11.0
其他any开发者可以添加任意的函数或数据变量到 Object 参数中,用 this 可以访问
// app.js
App({
   /*  1.监听小程序的初始化  */
   // 获取用户信息
   onLaunch(){
      console.log("onLaunch")
   },

   /* 2.监听小程序启动或者切换前台   */
   // 对页面的数据和效果进行重置
   onShow(){
     console.log("onShow")
   },
   /* 3.监听小程序切后台 */
   // 清除定时器
   onHide(){
      console.log("onHide")
   },
 
 
   
   /* 错误监听函数  */
   // 收集错误,通过异步请求发送服务器
   onError(e){
      console.log(e)
   }, 
   /*
    * 页面不存在监听函数
    * 启动时的入口文件不存在时才触发
    * 一般多用于扫码进入页面时的处理
    */
   onPageNotFound(){
     // 当页面不存在的时候,进行重定向,跳转到存在的页面
     console.log("onPageNotFound")
     wx.navigateTo({
       url: '/pages/page3/page3',
     })
   }

  
})

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

app.js中有一个 globalData, 可以用于放置全局变量或者常量。

在其它js文件中使用以下代码,获取数据。

const app = getApp();
const baseUrl = app.globalData.baseUrl;
  • 1
  • 2

2.页面级生命周期

注意:前台、后台定义: 当用户点击左上角关闭,或者按了设备 Home 键离开微信,小程序并没有直接销毁,而是进入了后台;当再次进入微信或再次打开小程序,又会从后台进入前台。只有当小程序进入后台一定时间,或者系统资源占用过高,才会被真正的销毁。

属性类型说明
dataObject页面的初始数据
1.onLoad(mounted)function生命周期回调—监听页面加载
2.onReadyfunction生命周期回调—监听页面初次渲染完成
3.onShowfunction生命周期回调—监听页面显示
4.onHidefunction生命周期回调—监听页面隐藏
5.onUnload(unmounted)function生命周期回调—监听页面卸载
6.onPullDownRefreshfunction监听用户下拉动作
7.onReachBottomfunction页面上拉触底事件的处理函数
8.onTabItemTapfunction当前是 tab 页时,点击 tab 时触发
onShareAppMessagefunction用户点击右上角转发
onShareTimelinefunction用户点击右上角转发到朋友圈
onAddToFavoritesfunction用户点击右上角收藏
onPageScrollfunction页面滚动触发事件的处理函数
onResizefunction页面尺寸改变时触发,详见 响应显示区域变化
onSaveExitStatefunction页面销毁前保留状态回调
其他any开发者可以添加任意的函数或数据到 Object 参数中,在页面的函数中用 this 可以访问。这部分属性会在页面实例创建时进行一次深拷贝
Page({

  /*  页面的初始数据 */
  data: {

  },
  
  /* 1.生命周期函数--监听页面加载 */
  // 发送异步请求,初始化页面数据
  // 一个页面只会调用一次。
  // 接收页面参数 可以获取wx.navigateTo和wx.redirectTo及中的 query。
  onLoad: function (options) {
      console.log("onLoad")
  },

  /* 2.生命周期函数--监听页面初次渲染完成 */
  onReady: function () {
      console.log("onReady")
  },

  /* 3.生命周期函数--监听页面显示 */
  // 每次打开页面都会调用一次。**tabBar切换无效
  onShow: function () { 
    console.log("onShow")
  },

  /* 4.生命周期函数--监听页面隐藏 */
  onHide: function () { 
    console.log("onHide")
  },

  /* 5.生命周期函数--监听页面卸载 */
  // 当redirectTo或navigateBack的时候调用。 (navigateTo无效)
  onUnload: function () { 
    console.log("onUnload")
  },



  /* 1.页面相关事件处理函数--监听用户下拉动作 */ 
  onPullDownRefresh: function () { 
    console.log("onPullDownRefresh")
  },

  /* 2.页面上拉触底事件的处理函数 */
  // 上拉加载数据
  onReachBottom: function () { 
    console.log("onReachBottom")
  },
  
   /* 3.切换tab页 */
   // 当前是 tab 页时,点击 tab 时触发
  onTabItemTap: function () { 
    console.log("onReachBottom")
  },
 


})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

3.组件生命周期

​ 组件的生命周期,指的是组件自身的一些函数,这些函数在特殊的时间点或遇到一些特殊的框架事件时被自动触发。其中,最重要的生命周期是 created attached detached ,包含一个组件实例生命流程的最主要时间点。

  • 组件实例刚刚被创建好时, created 生命周期被触发。此时,组件数据 this.data 就是在 Component 构造器中定义的数据 data此时还不能调用 setData 通常情况下,这个生命周期只应该用于给组件 this 添加一些自定义属性字段。
  • 在组件完全初始化完毕、进入页面节点树后, attached 生命周期被触发。此时, this.data 已被初始化为组件的当前值。这个生命周期很有用,绝大多数初始化工作可以在这个时机进行。
  • 在组件离开页面节点树后, detached 生命周期被触发。退出一个页面时,如果组件还在页面节点树中,则 detached 会被触发。

1.组件的生命周期

生命周期参数描述最低版本
1.created在组件实例刚刚被创建时执行1.6.3
2.attached (mounted)在组件实例进入页面节点树时执行1.6.3
3.ready在组件在视图层布局完成后执行1.6.3
4.moved(update)在组件实例被移动到节点树另一个位置时执行1.6.3
5.detached (unmounted)在组件实例被从页面节点树移除时执行1.6.3
errorObject Error每当组件方法抛出错误时执行2.4.1

代码示例:

Component({
  // 自小程序基础库版本 [2.2.3](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html) 起,组件的的生命周期也可以在 `lifetimes` 字段内进行声明(这是推荐的方式,其优先级最高)。
  lifetimes: {
    created: function() {
      // 在组件实例进入创建节点树时执行
    },
    attached: function() {
      // 在组件实例进入页面节点树时执行
    },
    ready: function() {
      // 在组件实例进入创建节点树时执行
    },
    moved: function() {
      // 在组件实例进入创建节点树时执行
    },
    detached: function() {
      // 在组件实例被从页面节点树移除时执行
    },
  },  
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

2.组件所在页面的生命周期

还有一些特殊的生命周期,它们并非与组件有很强的关联,但有时组件需要获知,以便组件内部处理。这样的生命周期称为“组件所在页面的生命周期”,在 pageLifetimes 定义段中定义。其中可用的生命周期包括:

生命周期参数描述最低版本
1.show组件所在的页面被展示时执行2.2.3
2.hide组件所在的页面被隐藏时执行2.2.3
resizeObject Size组件所在的页面尺寸变化时执行2.4.0

代码示例:

Component({
  pageLifetimes: {
    show: function() {
      // 页面被展示
    },
    hide: function() {
      // 页面被隐藏
    },
    resize: function(size) {
      // 页面尺寸变化
    }
  }
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

3.wxml的使用

1."{{ }}"数据绑定

注意: 在标签中使用变量,必需使用双括号和引号共同作用,才能进行数据的绑定。

使用方式 和vue类似,都可以在双括号内使用变量,进行数据绑定。

双括号内可以进行三元运算、和算术运算, 组成新的数据, 但唯一不允许构造成对象使用

1.数据绑定


<!-- 在标签中的属性使用变量,需要双括号和引号共同使用 -->
<view data-msg="{{msg}}">{{obj.age}}</view>

<view wx:if="{{num == 1}}">{{ [msg,num] }}</view>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

2.表单数据的双向绑定

// vant组件中使用model进行绑定
<van-cell-group>
  <van-field model:value="{{ value }}"
    placeholder="请输入用户名"  border="{{ false }}"  />
</van-cell-group>
  • 1
  • 2
  • 3
  • 4
  • 5

2.wx:for列表循环

使用方法也与vue相同, 但是需要添加wx的专属属性绑定和数据绑定


 <!-- 1.简写模式 -->
 <view wx:for="{{arr}}"  wx:key="*this">{{item}}</view>

 <!-- 2.完整模式 --> 
 <!-- (1.遍历数组 -->
<!-- 默认列表项是item,索引是index, 可以省略 -->
<view wx:for="{{persons}}" 
  wx:for-item="item"          // wx:for-item是默认的
  wx:for-index="index"        // wx:for-index是默认的
  wx:key="id">                // wx:key 关键区分字段
    <view>索引:{{index}}</view>
    <view>姓名:{{item.name}}</view>
</view>
 
 <!-- (2.遍历对象 -->
 <view wx:for="{{obj}}"
     wx:key="*this"              // *this表示当前的item数据项
     wx:for-item="value"
     wx:for-index="key" >
    <view>对象的key:{{key}}     </view>
    <view>对象的value:{{value}} </view>
  </view>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

3.wx:if条件渲染

小程序是wx:elif,vue是v-else-if

  • wx:if是惰性的,hidden是活动的。
  • hidden就类似于v-show,它始终会被渲染,只是简单的控制显示与隐藏
  • wx:if是惰性的,如果在初始渲染条件为false,则不会有任何变化。如果条件为真,则才会开始局部渲染。
  • 也可以用 wx:elifwx:else 来添加一个 else 块:

<!-- if条件判断 --> 
<view wx:if="{{num == 1}}">{{ [msg,num] }}</view>

<!-- if..elif..条件判断 --> 
<view wx:if="{{length > 5}}"> 1 </view>
<view wx:elif="{{length > 2}}"> 2 </view>
<view wx:else> 3 </view>

<!-- wx:for列表渲染 -->

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

4.wx模板和引用

1.创建模板

模板拥有自己的作用域,只能使用 data 传入的数据以及模板定义文件中定义的 <wxs /> 模块。

  • 方法一:组件内定义模板
  • 方法二:组件外定义模板,使用import导入 wxml文件。 (使用方法比组件更简单)
<!-- 定义模板 -->
<template name="msgItem">
  <view>
    <text> {{index}}: {{msg}} </text>
    <text> Time: {{time}} </text>
  </view>
</template> 

<!-- 使用模板 -->
<template is="msgItem" data="{{...item}}"/>
<template is="msgItem" data="{{index:item.index, msg:item.msg, time:item.time}}"/>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

2.引用模板

  1. import方式: 可以在文件中使用目标文件定义的template
<!-- item.wxml -->
<template name="odd">
  <text> 单数</text>
</template>
<template name="even">
  <text> 复数</text>
</template>


<!-- 引入模板 -->
<import src="item.wxml"/> 
<block wx:for="{{[1,2,3,4,5]}}" wx:key="*this"> 
  <text>{{item}}</text>
  <template is="{{item % 2 == 0 ? 'even' : 'odd'}}"  />
</block>


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  1. include可以将目标文件除了 外的整个代码引入,相当于拷贝代码
<!-- index.wxml -->
<include src="header.wxml"/>
<view> body </view>
<include src="footer.wxml"/>

<!-- header.wxml -->
<view> header </view>


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

3.使用实例

<!--pages/components/load/load.wxml-->
 
<!-- 加载模板, 1.组件内模板,2.组件外模板, -->
<!-- 作用:可以有效因页面加载缓慢,而导致的空数据 -->
<template name="loadMask">
   <view wx:if="{{judge}}" class="mask"></view> 
</template>


<!-- 使用方法 -->
<!-- 1.组件内模板 -->
<!-- 
<template name="loadMask">
   <view wx:if="{{judge}}" class="mask"></view> 
</template>
 -->
<!-- 2.import导入模板, 引入wxss无效,需要组件内自定义 -->
<!-- 
<import src="../components/load/load.wxml"/>
<template is="loadMask" data="{{judge:!like_goods.length}}"/>
 -->
<!--2.1 wxss 样式
.mask{
  width: 100vw;
  height: 100vh;
}
 -->
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

5.bind事件绑定

  • bindtap, bindchange, bindinput 是原生 小程序的事件绑定

  • bind:click , bind:change, 是vant组件常用的事件绑定方式

  • catchtap 禁用事件冒泡,一般设置为中间层


<-- 监听点击事件-->
<view bind:tap="onHandleClick()"> </view>

<-- 阻止冒泡事件-->
<view catchtp >
	<view bind:tap="onHandleClick()"> </view>
</view>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

6.获取文档节点(略)

通过api获取当前页面对象,再使用select方法查找节点

警告:微信小程序,不是非常支持修改页面。所以,不建议获取页面文档,也无法做到浏览的复杂渲染的页面。因为这种基于数据绑定的应用,本身就脱离了页面的直接控制。

(1.selectorComponent方法

vant组件中的选择节点方法。


const checkbox = this.selectComponent(`.checkboxes-${index}`);
checkbox.toggle();           // 执行实例的方法

  • 1
  • 2
  • 3
  • 4

(2.SelectorQuery方法

  • SelectorQuery.in (对应的组件)
    • 将选择器的选取范围更改为自定义组件 component 内
    • 初始时,选择器仅选取页面范围的节点,不会选取任何自定义组件中的节点
  • SelectorQuery.select(string selector)
    • 在当前页面下选择第一个匹配选择器 selector 的节点
  • SelectorQuery.selectAll()
    • 在当前页面下选择匹配选择器 selector 的所有节点。
  • SelectorQuery.selectViewport()
    • 选择显示区域
    • 用于获取显示区域的尺寸、滚动位置等信息
  • SelectorQuery.exec(function callback)
    • 执行所有的请求
    • 请求结果按请求次序构成数组,在callback的第一个参数中返回
let query = wx.createSelectorQuery() 
query.select('#payTicketBtm').boundingClientRect().exec(function(res) {
     console.log("rect",res[0].height)
     //res就是 所有标签为payTicketBtm的元素的信息 的数组
})
  • 1
  • 2
  • 3
  • 4
  • 5

4.js数据源的使用

1.函数传值

  1. 获取input输入的值

    使用 bindinput 绑定输入事件, 可以通过 事件中的事件参数 e.detail.value获取输入的值

    使用bindtap绑定点击事件,

  2. 点击按钮执行求和

    改变data中的属性,相当于改变组件中的状态。需要使用this.setData({count:11})

  3. 处理事件的函数不能传参

    要通过组件属性的形式 data- 和 e.currentTarget.dataset.name

  4. // react中是setState, 小程序是setData, 类似react中的组件状态, this.setState

 // vue,react中是target.value, 小程序是detail.value 

 // vue是this.xx 小程序是this.data.xx
  • 1
  • 2
  • 3
--page4.js文件

    ...
      /**
       * 页面的初始数据
       */
      data: {
        count:0,
        inputValue:''
      },
       // 处理事件函数
       handleInput(e){
         console.log(e, e.detail.value)
         this.setData({
           inputValue:e.detail.value
         })
       },

      //  处理点击进行求和
       handleClick(e){
         this.setData({
           count: this.data.inputValue + 1
         })
       },
      //  处理事件传参
      handleParams(e){
         console.log("传入的参数是", e)
      },
    ...

----page4.wxml文件

    <input bindinput="handleInput" style="border:1px solid red"/>

    <button bindtap="handleClick">点我加一</button>

    <view>求和的结果是:{{count}}</view>

    <button bindtap="handleParams" data-name="Tome">点我传参</button>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

2.阻止冒泡事件

微信小程序的冒泡事件

  • 微信小程序的事件,并非是浏览器的事件,所以不能使用event.stopPropagation阻止 事件冒泡
  • 使用 捕获事件的方式阻止冒泡向外扩散
  • vant组件采用catch:tap 禁用默认的事件。

1.基本方法catchtap

// 添加一个中间层,捕获冒泡事件
<view  catchtap="handleStopPropagation">
   <view bind:change="xxx">xxxx</view>
</view>

 // 捕获事件冒泡,防止向父层扩散
  handleStopPropagation(){
     // 不执行任何数据
  },
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

2.vant组件的 catch修饰

 
 // vant checkbox组件的默认切换事件, 使用catch修饰tap方法
  <view class=" check-item" data-check="{{item.id}}" bindtap="handleCheck"  >
  		<van-checkbox name="{{item.id}}"  class="checkboxes-{{ item.id }}" catch:tap="handleStopPropagation"/> 
  </view>
  • 1
  • 2
  • 3
  • 4
  • 5

3.滚动事件监听

  • scroll-view组件,常用于组件中内容的滚动监听

  • 我们只需要将滑动报错的标签上加上一个样式即可 touch-action: none;
    touch-action :当你触摸并按住触摸目标时候,禁止或显示系统默认菜单。
    touch-action取值有一下两种
    none:系统默认菜单被禁用
    default:系统默认菜单不被禁用

  • 添加样式更改,将滑动报错的标签样式添加: touch-action: none;

或者设置全局公共样式: *{touch-action: none;}

4.空白加载处理

  • 可以使用wx:if + 加载数据标记判断数据
  • 可以使用hidden + 延迟显示标记方式
  • 添加占位空白页面 + 加载数据标记判断数据

5.wxs公有模块


var hello = function(str){
  return 'hello' + str;
}
module.exports = {
  hello:hello
}

<!-- 使用wxs -->
<!-- 方法一:引入wxs文件,并重命名模块 -->
<wxs src="./../comm.wxs" module="tools"/>
<view>{{tools.hello("Tom")}}</view>

<!--方法二:直接在当前文件定义模块 -->
<wxs module="getAge">
   var age = 12;
   module.exports = {
     age:age
   }
</wxs>
<view >{{getAge.age}}</view> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

5.wxss样式的使用

尺寸单位

  • rpx(responsive pixel): 可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。
设备rpx换算px (屏幕宽度/750)px换算rpx (750/屏幕宽度)
iPhone51rpx = 0.42px1px = 2.34rpx
iPhone6(建议)1rpx = 0.5px1px = 2rpx
iPhone6 Plus1rpx = 0.552px1px = 1.81rpx

使用方法

  • 使用方法和网页一样,都可以使用class

  • 引入css模块:

      @import "./../comm.wxss";
    
    • 1
  • 内联样式:

    <!--动态样式-->
    <view style="color:{{color}};" />
    
    
    • 1
    • 2
    • 3
  • style:静态的样式统一写到 class 中。style 接收动态的样式,在运行时会进行解析,请尽量避免将静态的样式写进 style 中,以免影响渲染速度

1.vant的icon图标

(1. 方法一

使用icon组件: 略

(2.方法二

使用icon标签属性,标签属性值在icon组件中查找,可以不需要添加组件

  • 一般线框风格是 xxx-o, 实底风格是xxx
/*修改goods-icon组件中的icon标签属性*/
.van-icon.van-icon-star {
  color:red; 
}
  • 1
  • 2
  • 3
  • 4

2.vant样式的覆盖

vant支持原选择器的样式覆盖,但不支持更换其它选择器的样式覆盖

比如:在原选择器前添加一个(类、id选择器),则样式覆盖无效

此时可以考虑vant文档中是否有 给根节点添加类名的选项 + 把覆盖样式写在 app.css中,添加层级

--无效的样式
.page-item .van-loading__spinner.van-loading__spinner--spinner,.van-loading__dot 
 {
  width: 60px!important;
  height: 60px!important; 
} 
--有效的样式
 .van-loading__spinner.van-loading__spinner--spinner,.van-loading__dot 
 {
  width: 60px!important;
  height: 60px!important; 
} 


<!-- 页面加载器 -->
// vant组件 可以使用 添加前缀**custom-**,给根节点添加类名。
<view  class="page-item"> 
  <van-loading type="spinner" custom-class="inner-spinner"/>
</view>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

3.普通样式覆盖

  • page, view , swiper, text 微信小程序基础组件,都是作为标签使用, vant组件, 一般不使用组件名称作为选择器使用

  • **常用方式:**page + xxxx选择器, 可以极大地减少!important的使用。

  • page .address-item{
       width:100rpx!important;
    }
    
    • 1
    • 2
    • 3
  • 全局样式写在app.wxss里面,所有页面共享文件里的样式

  • - 根节点无法与普通节点共同作用,无法找到索引。
    // 选择器无效的示例,找不到子节点
      .submit-item  .van-button { 
          height:160rpx;
      }
    
    // 选择器有效示例 , 找到具体的子节点
     .submit-item van-button .van-button--normal{
       height:160rpx;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PV3tdTVB-1661414245340)(C:\Users\Icy-yun\AppData\Roaming\Typora\typora-user-images\image-20220212125130885.png)]

6.视图容器(组件)

1.view基础组件

  • view组件, 代替div

  • 
    <view hover-class="bg-red">视图容器组件</view>
    
    
    • 1
    • 2
    • 3
  • **text组件,**代替span

  • 
    <text user-select decode>文本组件 &nbsp;hello</text>
    
    • 1
    • 2
  • **image **图片组件

    • mode属性可以设置图片的裁剪方式
      scaleToFill默认, 宽高拉伸满,占满容器
      aspectFit长边拉伸满, 短边自适应,常用
      aspectFill短边拉伸满,长边会裁剪,不常用
      widthFix 宽度100%,高度自适应,常用
  • 
    <image class="image" mode="aspectFill" src="https://res.wx.qq.com/wxdoc/dist/assets/img/0.4cb08bb4.jpg"></image>
    
    • 1
    • 2
  • button表单组件

    • open-type按钮的开放能力
      contact 打开微信客服,只能在正式appid的真机测试下运行
      share转发朋友,不能发送朋友圈
      getPhoneNumber 获取用户手机号,配合bindgetphonenumber事件来使用,返回的是一个加密的数据
      getUserInfor 获取用户信息。小程序建议使用getUserProfile, 具有弹窗提示
      launchApp 打开app, 只能加回跳到app
      openSetting 打开授权设置页
      feedback 打开意见反馈页面, 需要真机测试
  •     
       <button size="mini" type="primary" loading="{{true}}">按钮</button>
       <button open-type="contact">contact</button>
       <button open-type="share">share</button>getUserInfo
       <button open-type="getPhoneNumber" bindgetphonenumber="bindgetphonenumber">getPhoneNumber</button>
       <button open-type="getUserInfo" bindgetuserinfo="bindgetuserinfo">bindgetuserinfo</button>
       <button bindtap="getUserInfo" >获取用户信息</button>
       <button open-type="launchApp" >launchApp</button>
       <button open-type="openSetting" >打开授权页</button>
       <button open-type="feedback" >打开反馈页面</button>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • block组件,是一个包装元素,只接受控制属性,也不会在页面中做任何渲染。类似于template标签。

  • 
    <block wx:if="{{true}}">
      <view> view1 </view>
      <view> view2 </view>
    </block>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

2.input表单组件

  • input组件
  • radio组件 + radio-group
  • checkbox组件 + checkbox-group
  • form组件,
用户名:
<input placeholder="请输入用户名" bindinput="handleUsername"/>
密码:
<input placeholder="请输入密码" bindinput="handlePassword"/>
性别:
<radio-group bindchange="handleRadio">
  <radio value="男"/>男
  <radio value="女"/>女
</radio-group>
爱好:
<checkbox-group bindchange="handleHobby">
  <checkbox value="打篮球" checked="{{true}}"/>打篮球
  <checkbox value="乒乓球"/>乒乓球
  <checkbox value="羽毛球"/>羽毛球
</checkbox-group>

<p>
  <view>用户名:{{username}}</view>
  <view>密码:{{password}}</view>
  <view>性别:{{sex}}</view>
  <view>爱好:{{hobby}}</view>
</p>

<button form-type="submit">提交 </button>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

3.navigator高级组件

  • navigator组件,导航跳转

    • target跳转目标,默认是当前小程序open-type跳转的方式
      navigate默认,跳转之后,可以返回
      redirect关闭当前页面,跳转之后不能返回
      switchTab跳转到tabBar页面,并关闭其他所有非tabBar 页面, 设置为tabBar的页面专属
      reLaunch 关闭所有页面,打开到应用内的某个页面
      navigateBack关闭当前页面,返回上一页面
  • <navigator url="/pages/page3/page3" >跳转page3</navigator>
    <navigator url="/pages/page3/page3" open-type="redirect">redirect 跳转page3</navigator>
    <navigator url="/pages/index/index" open-type="switchTab">redirect 跳转index</navigator>
    <navigator url="/pages/index/index" open-type="reLaunch">redirect 跳转index</navigator>
    
    • 1
    • 2
    • 3
    • 4
  • 这个常用于当作跳转的路由方法。 也可以带参数使用。可以后退,相当于navigatorTo

  • 
    <navigator url="/pages/goods/goods?id={{itemm.id}}"> </navigator>
    
    onLoad: function (options) {
    	const {id} = options;
        // 通过options获取跳转路由的参数
        console.log("参数是",id)  
    },
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  • swiper组件 ,轮播图展示

    • ​ 1.swiper默认的宽高,宽100%, 高150px
      ​ 2.images默认宽320px,高240px
      ​ 3.让swiper的高通过使用的图片的宽高计算出来
      ​ s_height = s_width * i_height / i_width
      ​ height:calc(750rpx * 240 / 320 )
      ​ 4.常用属性
      ​ autoplay自动播放
      ​ interval 指定播放间隔
      ​ indicator-dots 指示切换点
  •  
    <swiper autoplay="{{true}}" 	indicator-dots>
      <swiper-item wx:for="{{images}}" wx:key="*this" wx:for-item="url">
        <image src="{{url}}"/>
      </swiper-item> 
    </swiper>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • rich-text组件,解析html

  •  page4.js文件
     
        html_1:'<p style="background:red">hello</p>',
        html_2:[
          {
            name:"p",
            attrs:{
              style:"color:red"
            },
            children:[
              {
                type:'text',
                text:'hello'
              }
            ]
          }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
  • 
    <!-- 5.rich-text属性渲染html -->
    <rich-text nodes="{{html_1}}"></rich-text>
    <rich-text nodes="{{html_2}}"></rich-text>
    
    • 1
    • 2
    • 3
    • 4
  • scroll-view组件,常用于组件中内容的滚动监听

  • 
        <scroll-view scroll-y="true" style="height: 100%" bindscrolltolower="onScrollBottom" >   
        
        </scroll-view>
    
    • 1
    • 2
    • 3
    • 4
  • web-view组件,在微信小程序中浏览网页

  • 注意: 需要在微信开发者选项中添加允许访问的业务网站 的域名

  • <web-view src="https://www.xxxxxxxxx.com/index.html" />
    
    • 1
  • 微信小程序内嵌网页

7.components组件

1.创建组件

定义一个components组件,在组件wxss中不应使用ID选择器、属性选择器和标签名选择器。

1.component组件


-- person.js文件
Component({
  /*  组件的接收属性数据 */
  properties: {
    //  设置接收数据的类型
      persons:{
        type:Array,
        value:[]
      }
  },

  /* 组件的初始数据 */
  data: {
    title:"我是自定义组件",
    msg:"我是子组件中的数据",
    //  persons:[
    //    {id:1,name:"Tom",age:10},
    //    {id:2,name:"mike",age:11},
    //  ]
  },

  /*  组件的方法列表  */
  methods: {
      handleClick(){
         console.log("点击了自定义组件中的事件")
      },
      handleMsg(){
        //  派发一个自定义事件, 相当于$emit
         this.triggerEvent("getMsg",{msg:this.data.msg})
      }
  },
  
  /*监听数据的变化*/
  Observe:{
     showPay: function(newShowPay) { 
       console.log("111111",newShowPay)
    }
  },
  
  // 勾子函数,组件的生命周期
  lifetimes: {
    created: function() {
      // 在组件实例进入创建节点树时执行
    },
    attached: function() {
      // 在组件实例进入页面节点树时执行
    },
    ready: function() {
      // 在组件实例进入创建节点树时执行
    },
    moved: function() {
      // 在组件实例进入创建节点树时执行
    },
    detached: function() {
      // 在组件实例被从页面节点树移除时执行
    },
  },  
})

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
-- person.wxml文件

<view class="main"> 
  <view>这里是组件的内部节点</view>
  <slot></slot>

  <view>{{title}}</view>
  <view class="item" wx:for="{{persons}}" wx:key="id">
    <view>姓名:{{item.name}}</view>
    <view>年龄:{{item.age}}</view>
  </view>
  <button bindtap="handleClick">点击触发自定义组件中的事件</button>
  <button bindtap="handleMsg">传递给父组件</button>
</view>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

2.page页面

---page.json文件引入组件

{
  "usingComponents": {
    "Persons":"../../components/Persons/Persons"
  }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
---page.wxml文件

<view>Page5中使用自定义组件</view>
<Persons persons="{{persons}}" bindgetMsg="getMsg" >
    这里的内容会显示在自定义组件中的slot占位
</Persons>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

下面使用的方法与vue类似

2.property父传子

  • page页面向组件传递数据,使用标签属性
  • 子组件可以在properties 中设置传递的数据类型
  • 子组件可以自由使用传递的数据

3.triggerEvent子传父

  • 子组件在点击事件触发后,在methods选项中定义点击事件的方法,方法里面再使用 this.triggerEvent("getMsg",{msg:this.data.msg}) 派发一个自定义事件。triggerEvent很类似$emit
  • 父组件绑定是子组件派发的自定义事件(bindgetMsg=“handleMsg”)

4.slot 插槽标签

  • 在使用组件的位置,可以在内部定义slot内容
  • 而组件可以设置slot标签 的位置
  • 如果没有传递参数, 设置 default数据无效

注意:

  1. vant组件中也可以直接使用wx:for ,等模板语法。(tab组件)。

  2. vant组件的也有slot标签属性(goods-action组件)

    
    <van-cell value="内容" > 
        <view slot="title"> 
           	把title标签属性的内容作为需要插入的数据
        </view>
    </van-cell>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  3. vant组件也可以不添加任何属性,直接在标签体内添加内容,即可成为插槽内容(cell单元格)

  <van-cell title="设置为默认地址" >
        <van-switch checked="{{ checked }}" size="24px" />
  </van-cell>
  • 1
  • 2
  • 3

5.observe监听器

  • 监听的数据,可以是不添加引号的键名。
Component({
  ...
  observers: {
  // 监听单个数据
    showPay: function(newShowPay) { 
       console.log("111111",newShowPay)
    }
    
    
  // 1. 监听多个数据
    'numberA, numberB': function(numberA, numberB) {
      // 在 numberA 或者 numberB 被设置时,执行这个函数
      this.setData({
        sum: numberA + numberB
      })
    },
    
    // 2.监听对象的子数据
    'some.subfield': function(subfield) {
      // 使用 setData 设置 this.data.some.subfield 时触发
      // (除此以外,使用 setData 设置 this.data.some 也会触发)
      subfield === this.data.some.subfield
    },
    
    // 3.监听列表的子数据
    'arr[12]': function(arr12) {
      // 使用 setData 设置 this.data.arr[12] 时触发
      // (除此以外,使用 setData 设置 this.data.arr 也会触发)
      arr12 === this.data.arr[12]
    },
    
    // 4.监听所有子数据的变化, 使用双通配符 “**”
    'some.field.**': function(field) {
      // 使用 setData 设置 this.data.some.field 本身或其下任何子数据字段时触发
      // (除此以外,使用 setData 设置 this.data.some 也会触发)
      field === this.data.some.field
    },
  }
  ...
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

8.常用API: wx.—

1.navigateTo路由跳转

(1. 路由跳转的参数都是串行形式, navigator组件和wx.路由api的效果相同。

(2.网络请求的参数,可以为串行和请求体两种形式

(1.跳转api

  • 保留当前页面,跳转到应用内的某个页面。但是不能跳到 tabbar 页面。使用 wx.navigateBack 可以返回到原页面。小程序中页面栈最多十层。

  • 关闭当前页面“,跳转到应用内的某个页面。但是不允许跳转到 tabbar 页面

  • //  wx.redirectTo({ 
    //    url: '/pages/page5/page5',
    //  })
    
    • 1
    • 2
    • 3
  • 关闭所有页面“,打开到应用内的某个页面, 允许跳转到tabbar页面

  • 一般 用于扫码跳转主页面

  • //  wx.reLaunch({ 
    //    url: '/pages/page2/page2',
    //  })
    
    • 1
    • 2
    • 3
  • 从tab页面,跳转其它 ”所有“ 页面

  •  //  wx.navigateTo({ 
     //    url: '/pages/page5/page5',  
     //  })
    
    • 1
    • 2
    • 3
  • 关闭其他所有非 tabBar 页面“, 允许跳转到 tabBar 页面

  • // wx.switchTab({ 
    //   url: '/pages/page2/page2',
    // })
    
    • 1
    • 2
    • 3
  • 关闭当前页面,返回上一页面或多级页面。可通过 getCurrentPages 获取当前的页面栈,决定需要返回几层。

  • // wx.navigateBack({
    //   delta: 0,
    // })
    
    • 1
    • 2
    • 3

(2.获取当前页面

  • getCurrentPages() 函数用于获取当前页面栈的实例,以数组形式按栈的顺序给出,第一个元素为首页,最后一个元素为当前页面。

  • let pages = getCurrentPages();
    let currPage = null;
    if (pages.length) {
       currPage = pages[pages.length - 1];
    }
    console.log(currPage.route) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

2.showToast交互

  • 显示消息提示框

  •    wx.showToast({
          title: '成功',
          icon: 'success',
          duration: 2000
        }) 
        // 隐藏提示框, 一般与showToast配对使用
        setTimeout(()=>{
          wx.hideToast( )
        },1000)
          
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • 显示 loading 提示框。需主动调用 wx.hideLoading 才能关闭提示框

  • 
        wx.showLoading({
          title: '加载中',
        }) 
        setTimeout(function () {
          wx.hideLoading()
        }, 2000) 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
  • 显示模态对话框

  • 
        wx.showModal({
          title: '提示',
          content: '这是一个模态弹窗',
          success (res) {
            if (res.confirm) {
              console.log('用户点击确定')
            } else if (res.cancel) {
              console.log('用户点击取消')
            }
          }
        })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

3.request网络请求

  • 请求数据

  • 注意:get请求的参数,可以写在data上, 可以串行和请求体的方式

  • vue是有data和params的区分

  •  const requestTask= wx.request({
          url: 'https://api.shop.eduwork.cn/api/index', //仅为示例,并非真实的接口地址 
          method:'get',
          data:{
              title:'java',
              sort:'price'
          },
          success (res) {
            if(res.statusCode == 200){ 
                console.log(res.data)
            }
          }
        })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
  • 中断请求

  • 
        // requestTask.abort();
    
    • 1
    • 2
  • 监听 HTTP Response Header 事件。会比请求完成事件更早

  •   requestTask.onHeadersReceived(()=>{
           console.log("onHeadersReceived")
        })
    
    • 1
    • 2
    • 3

4.storage数据缓存

  • 异步存储

  • // 异步存储数据
        wx.setStorage({
          key:"key",
          data:"value"
        })
        // 异步获取缓存中的数据
        wx.getStorage({
          key: 'key',
          success (res) {
            console.log(res.data)
          }
        })
        // 异步获取当前的缓存信息
        wx.getStorageInfo({
          success (res) {
            console.log(res.keys)
            console.log(res.currentSize)
            console.log(res.limitSize)
          }
        })
        // 异步移除本地缓存
        wx.removeStorage({
          key: 'key',
          success (res) {
            console.log(res)
          }
        })
        // 异步清空本地缓存
        wx.clearStorage({
          success: (res) => {},
        })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
  • 同步缓存

  •  wx.setStorageSync('age', 12)
     const res = wx.getStorageInfoSync( )
     console.log( res.keys)
     console.log( res.currentSize)
     console.log( res.limitSize)
     const age = wx.getStorageSync('age')
     console.log(age)
     wx.removeStorageSync('age')
     wx.clearStorageSync()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

区别:

  1. 调用异步api之后。不会等待执行结果,程序继续向下运行
  2. 调用同步api, 需要等待执行结果,程序暂停运行。

注意:

  • 微信小程序没有其它的缓存处理,所以一般使用同步缓存,并且storage可以不需要使用JSON.stringify处理,可以直接缓存对象。
  • 用户信息,token, openid都是存放在storage中
  • 微信小程序没有多线程和async,一般都是同步的。即使有如同react的组件状态,却依然是同步执行。

5.wx.-开放接口

  • wx.login

  • 调用接口获取登录凭证(code)。通过凭证进而换取用户登录态信息,包括用户在当前小程序的唯一标识(openid)、微信开放平台帐号下的唯一标识(unionid,若当前小程序已绑定到微信开放平台帐号)及本次登录的会话密钥(session_key)等。用户数据的加解密通讯需要依赖会话密钥完成

  • wx.getUserProfile(Object object)

  • 获取用户信息。页面产生点击事件(例如 buttonbindtap 的回调中)后才可调用,每次请求都会弹出授权窗口,用户同意后返回 userInfo。该接口用于替换 wx.getUserInfo

6.wx.-设备接口

  • wx.getClipboardDatawx.setClipboardData

  • js中:
     // 点击复制openid
      textPaste(e){
        wx.setClipboardData({
          data: e.currentTarget.dataset.openid,
          success (res) {
            wx.getClipboardData({
              success (res) {
                console.log(res.data) // data
              }
            })
          }
        }) 
        
      },
      
      
      wxml中:
          <text class='shopName' bindtap='textPaste' data-openid="{{userObj.openid}}">{{userObj.openid}}</text>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
  • wx.previewImage 图片预览

  •  
       // 点击自定义预览层
       handleSelfPreview(e){ 
          const url = e.currentTarget.dataset.preview
          wx.previewImage({
            current:url, // 当前显示图片的http链接
            urls:[url] // 需要预览的图片http链接列表
          })
       }, 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  • wx.startPullDownRefreshwx.stopPullDownRefresh 上拉刷新

  • 
    console.log("下拉刷新")
    wx.startPullDownRefresh({
       success:function(){
         console.log("刷新成功")
       }
    })
    wx.stopPullDownRefresh({
       success:function(){
         console.log("刷新结束")
       }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

9.登录接口(openid)

openid和unionid

  • openid:微信用户相对于不同的公众号,不同的小程序,都有唯一的用户标识,,这个标识就是openid,在同一个公众号或者同一个小程序中,微信用户的openid是唯一的,即每个微信用户的openid是不同的

  • unionid;微信开放平台帐号下的唯一标识,只有公众号或者小程序绑定到微信开放平台帐号才能获取到unionid,同一个微信用户,在同一个开放平台unionid是惟一的,用做在不同的公众号或者不同的小程序中,识别同一个用户

  • wx.getUserProfile和wx.login

    • wx.getUserProfile:获取用户信息。页面产生点击事件(例如 button上 bindtap的回调中)后才可调用,每次请求都会弹出授权窗口,用户同意后返回userlnfo.

      • 一般只通过通知用户授权,就可以简单获取用户信息, 包括获取微信头像,存在后台自动注册的情况。如果存在多个平台注册,则不适合这种方式。
    • wx.login:调用接口获取**登录凭证(code)。通过凭证进而换取用户登录态信息,包括用户在当前小程序的唯一标识(openid)**、微信开放平台帐号下的唯一标识(unionid,若当前小程序已绑定到微信开放平台帐号〉及本次登录的会话密钥(session_key)等。

      • 后台处理,让用户感知没有任何的弹窗。操作更复杂一点。一般在小程序开始运行时自动获取。
    • **相同点:**最终的目的都是拿到openid,用于查询数据中的用户信息

    • **区别:**getUserProfile需要用户操作,不可控制。

      ​ wx.login可以实现自动获取openid

image-20220130205505573 image-20220201134546760

总结:

  • 微信小程序使用wx.login获取的code发送给后台服务器,后台服务器再去访问微信服务器地址,获取openid。(小程序appid, 应该避免暴露); 查询数据库表当前openid是否存在(因为一个用户只能获取到唯一的一个openid)来判断用户是否存在; 如果用户不存在,则只返回openid, 用于微信注册绑定帐号; 如果用户存在,则会返回openid和用户信息(直接登录,并返回用户信息)
  • 自动登录流程:根据获取的openid, 查找openid对应的帐号,如果存在,则返回token, 用户信息,openid, 如果不存在,则只返回openid。
  • 手动登录流程:用户手动输入帐号和密码,提交到后台, 如果返回的数据没有openid, 则跳转微信绑定。
  • 注册流程:请求获取微信授权,申请获取用户的帐号信息,然后将用户数据提交到后台注册帐号(此时,应该是将前面获取的openid 绑定当前微信帐号)。
  • 微信绑定流程:通过传入openid,确认当前帐号是否绑定的是当前微信。微信绑定也是下一次自动登录的关键。
  • openid的作用:openid是当前小程序对当前微信用户的唯一身份标识!以根据微信帐号和系统帐号连接在一起。相对来说,openid是小程序连接自动登录 的关键,而access_token 作为身份认证使用。而网页端是以保存access_token作为自动登录和身份认证的功能。(应该也可以根据openid反向获取用户信息)

1.小程序登录流程

1.前台发起请求:(warning)

App({
  onLaunch() { 

    // 登录
     wx.login({
  success (res) {
    if (res.code) {
      //发起网络请求
      wx.request({
        url: 'https://api.weixin.qq.com/sns/jscode2session',
        // 注意,不要以明文的形式, 请求用户数据。 
        // app.js获取不到封装页面的request文件
        data: {
          appid:'你自己的appid',  
          secret:'你自己的secret',
          js_code:res.code,
          grant_type:'authorization_code'
        } 
      })
    } else {
      console.log('登录失败!' + res.errMsg)
    }
  }
})
  },
  globalData: {
    userInfo: null,
    baseUrl:'https://api.shop.eduwork.cn'
  }
})

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

2.后台连接微信服务器(建议)

》》 当前小程序

》》appid , 你自己的appid

》》 secret, 你自己的secret

// app.js 

App({
  onLaunch() { 

    // 登录
    wx.login({
      success :(res)=>{
        if (res.code) { 
          //发起网络请求, wx.login获取的code,  然后通过code获取小程序的Openid和用户信息. 
          // data里面的appid是当前的小程序的身份标识   
          wx.request({
            url: this.globalData.baseUrl+'/api/auth/wx/code',
            method:'POST',
            data:{
              appid:'你自己的appid',
              secret:'你自己的secret',
              js_code:res.code
            },
            success(res2){ 
              // 如果当前微信没有绑定过,只能返回openid,缓存openid,用于后续操作。
              wx.setStorageSync('openid', res2.data.openid)

              // 如果绑定过,会返回access_token和用户信息,缓存access_token和用户信息
              // 而小程序是否登录和是否有access_token是直接关系。
              if(res2.data.access_token != ''){
                 wx.setStorageSync("access_token",res2.data.access_token)
                 wx.setStorageSync("userinfo",res2.data.user)
              }
            }
          })
        } else {
          console.log('登录失败!' + res.errMsg)
        }
      }
})
  },
  globalData: {
    userInfo: null,
    baseUrl:'https://api.shop.eduwork.cn'
  }
})

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

3.混乱secret密钥

 

// 检测当前的页面
export const getAlter_sec = ()=>{
    return getAlter_C(getAlter_B(getAlter_A()))
}
 
//  1.混乱的短字段
export const getAlter_A = ()=>{ 
   // 目标数据  a4f eaca b e4e4 67 f26d 8c8d a1eb22 e7  01
   const _a = 'a4f a1eb22'  // 1 8
   const _b = 'b 67'   // 3 5
   const _c = 'eaca 8c8d 01'  // 2 7 10
   const _d = ''
   const _e = 'e4e4 e7'   // 4  9
   const _f = 'f26d'    // 6
   const arr = [1,8,3,5,2,7,10,0,4,9,6]
   const alpha = ['a','b','c','d','e','f']
   let getA = Array.from('1234567890'); 
   let _turn = -1
   arr.forEach((item)=>{  
      if(item != 0) {
         _turn += 1
         let t_alpha = [];
         alpha.forEach(subItem=>{ 
               var t = 0;
               switch(subItem){
                  case 'a':
                     t = _a;
                     break;
                  case 'b':
                     t = _b;
                     break;
                  case 'c':
                     t = _c;
                     break;
                  case 'd':
                     t = _d;
                     break;
                  case 'e':
                     t = _e;
                     break;
                  case 'f':
                     t = _f;
                     break; 
               } 
            if(t.length){ 
               t_alpha = [...t_alpha,...t.split(' ')]
            }
         })  
         getA.splice(item-1,1, t_alpha[_turn])   
         }
   }) 
   return getA
}
 
//  2.混乱的短字段2
export const getAlter_B = (newArr)=>{
   // 目标数据   67 01 a4f eaca b e4e4 f26d 8c8d a1eb22 e7
   // 传递的数据 a4f eaca b e4e4 67 f26d 8c8d a1eb22 e7  01 
   let arr = Array.from('2345067891'); 
   let getB =  Array.from('1234567890');
    
   arr.forEach((item,index)=>{  
         getB.splice(item,1,newArr[index])  
   })
   return getB 
}

// 3.调整顺序,拼接,
export const getAlter_C = (newArr)=>{  
   // 传入四个字符
   const _a = newArr[0] 
   const _b = newArr[1]
   let prefix = _a[0]+_b+_a[1] 
   return prefix + newArr.slice(2).join('');
}

export const getAlter_appid = () =>{
   const _a = 'wx4cfa'
   const _c = 'd132eb'
   const _b = '9ca05f'
   return _a + _b + _c
}

module.exports = {
   getAlter_sec,getAlter_appid
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88

2.oss文件直传

  • 因为我们走的是STS模式,所以首先去后台获取oss配置(accessKeyId,accessKeySecret,stsToken)
  • 创建oss实例,前后端商量好文件上传路径,直接上传,返回的是在线图片链接

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BittewZQ-1661414245342)(C:\Users\Icy-yun\AppData\Roaming\Typora\typora-user-images\image-20220210141956427.png)]

前端js上传图片、文件到阿里云OSS

阿里云简单oss上传

微信小程序与oss直传

vant组件的upload上传(主要参考文章)


  // 提交修改头像
  handleConfirm(){   
    var tempFilePath = this.data.updateAvatarList[0].url   // 监听url数据,并非data数据流
    // 只有更换才会显示发起请求的数据。
    if(!tempFilePath.startsWith("http://tmp/")){
       wx.showToast({
         title: '头像没有变化',
         icon:'error',
         duration:1500
       })
       return;
    } 
    this.getOssKey();
  },
  
  // 1.首先通过后端api返回一个oss接口信息
  getOssKey(){
    ossPicKey().then(res=>{
       console.log("返回的结果是",res)
       this.setData({
        ossKey:res
       })
       this.handleOss();
    })
  },
  
  // 2.通过组件将oss转换成在线的图片链接
  handleOss(){
        console.log("处理在线的链接",this.data.ossKey)    
        //上传的文件信息, 临时的url才有效,已经存在的在线链接,则无效。
        var tempFileDataUrl = this.data.updateAvatarList[0].url   // 监听url数据,并非data数据流
        var prefixUrl = this.data.ossKey.host;    // 图片链接前缀
        var filename = Date.now() + 'aaa.png'   // 文件名称  
        var filepath = `BoardShopping/${filename}`    // 文件路径 (含文件名称)
  
        wx.uploadFile({
          // 目标url "https://laravel-book-shop.oss-cn-beijing.aliyuncs.com/",
          url:  prefixUrl,   // 图片链接前缀
          filePath: tempFileDataUrl,    //  临时url文件
          name: 'file',                 // 文件类型,(图片也可以是file类型)
           // 获取oss数据, 必须是后端已经注册的用户
          formData: {
            name: tempFileDataUrl,     // 临时url文件
            key: filepath,          // 文件路径(含文件名称)
            policy: this.data.ossKey.policy,     // plicy政策
            OSSAccessKeyId:  this.data.ossKey.accessid,   // osss访问id
            success_action_status: "200",      // 成功的状态码
            signature:  this.data.ossKey.signature,    // 签名验证
          },
          success: (res)=>{
            const status =   `${res.statusCode}`
            if(!status.startsWith("2")){   
                console.log('请求错误1: ', res)
                wx.showToast({
                  title: "上传失败",
                  icon: 'error',
                  duration: 1500
                })
            }else{  
              // 完成的图片连接
               const newLink = prefixUrl + filepath ;
               console.log("获取的url链接",newLink)
               // 因为是回调函数, 所以并不能在回调函数中获取页面定义的方法,但箭头函数可以在上级查找data数据   
               this.handleAvatar(newLink);
            }
          },
          fail:(err)=>{
            console.log('请求失败2: ', err.errMsg)
            wx.showToast({
              title: "上传失败",
              icon:'error',
              duration: 1500
            })
          },
        })  
  }, 
  
  // 3.然后将在线图片链接上传到当前帐号
  handleAvatar(newLink){ 
    // const newAvatar = 'https://cdn.jsdelivr.net/gh/JackKetty/PicGoCDN/pic/202201051114294.jpg'
    const newAvatar = newLink;
    const data = {
      avatar:newAvatar
    }
    updateAvatar(data).then(res=>{
      wx.showToast({
        title: '更换头像成功',
        icon:'success',
        duration:1500
      })
      this.setData({
        userObj:{
          ...this.data.userObj,
          avatar_url:newAvatar
        },
        preAvatar:newAvatar,
        updateAvatarList:[
          {...this.data.updateAvatarList[0],url:newAvatar}
        ]
      })
      this.handleChangeAvatar()
    }) 
  },
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104

3.支付宝沙箱支付

1.使用沙箱支付宝

  1. 首先需要使用个人真实支付宝帐号申请一个沙箱帐号,里面包含 商家用户 测试帐号

  2. 然后下载沙箱支付宝,使用 沙箱支付宝帐号登录,支付余额可以在 注册的支付宝帐号里面配置(真实)。

  3. 支付宝沙箱支付帐号

2.可以查看一下沙箱支付宝处理的流程(配置沙箱环境)

  1. 选择支付宝的 研发服务选项, 配置密钥/证书(公钥)

  2. 然后配置应用网关,用于接收支付宝沙箱环境的通知

  3. (自己的服务器)服务端配置 支付宝网关地址APPID签名方式

  4. 然后配置授权回调地址

  5. 沙箱支付环境

10.微信小程序上线

1.项目根路径

1.基础路径

  1. 在项目下所有的json文件中,根路径都是项目所在的路径。
app.wxss文件

@import '/miniprogram_npm/@vant/weapp/common/index.wxss';


app.json文件
 url= "pages/homePage/cart/cart"           // navigator跳转路径

"iconPath": "/assets/icons/home.png",      // assets路径

"pagePath": "pages/homePage/cart/cart",   // tabBar路径

"pages":[                                 // 页面路径
	"pages/homePage/index/index",
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  1. 在js文件中只能使用相对路径引入
js文件

import {deleteAddress,defaultAddress,addresslist} from '../../../service/address'

import Notify from '../../../miniprogram_npm/@vant/weapp/notify/notify';

json文件(可以在npm构建包中直接找到)

"van-empty": "@vant/weapp/empty/index"

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

注意:npm构建是将npm包中的组件打包成一个miniprogram_npm 的构建组件。 可以在json文件中直接被找到

2.getapp()方法

  1. Commonjs 语法下的模块引入

    将引入的路径,切换到当前路径,使用了变量的形式

    App({
    	require: ($uri) => require($uri),
    })
    
    const app = getApp()
    const {data} = app.require('model/test.js')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  2. Es6 语法下的模块引入 (不建议使用)

    在app.js文件中声明具体模块,并结合getApp方法获取对应的属性,然后在引入的位置执行函数,获取模块对象。

    App({
      alias:{
        'service':()=>require("service/address")
      },
    })
    
    const app = getApp();  
    const  {deleteAddress,defaultAddress,addresslist}  =  app.alias.service( ); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

2.开发平台上线

  1. 构建npm包,真机扫码预览,打开开发者调试模式。(因为会校验合法域名)
  2. 校验合法域名(使用https), 在开发者管理配置服务器信息,";"号分隔, 添加可能请求的域名(包括图片)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ay4NmrzP-1661414245342)(C:\Users\Icy-yun\AppData\Roaming\Typora\typora-user-images\image-20220203001649152.png)]

3.发布小程序,在微信开发者工具中点击上传,将当前开发版本上传到 微信开发者平台上

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qdjOzGHQ-1661414245344)(C:\Users\Icy-yun\AppData\Roaming\Typora\typora-user-images\image-20220203002638010.png)]

注意:如果出现 小程序真机调试遇到VM13:2 Unhandled promise rejection 90001

  • 小程序开发中真机调试突然接口加载失败,排查了好多次,最后发现接口域名证书导致的
    VM13:2 Unhandled promise rejection 90001, 重新安装一下域名证书就好了

11.框架知识总结

1.与vue的相同点

  • 生命周期:

    1. vue有8个生命周期: beforeCreate,created, beforeMount, mounted, beforeUpdate, updated, beforeUnmount, unmounted
    2. 微信小程序有3种类型的生命周期:
      1. 应用生命周期:(常用)onLaunch, onShow, onHide, onError, onPageNotFound
      2. 页面生命周期:(常用)onLoad, onShow, onHide, onReady, onUnload, onPUllDownRefresh, onReachBottom
      3. 组件生命周期:(常用):略
  • 组件机制:

    1. vue组件机制,父子之间传递数据,以及使用插槽添加额外的子数据。

    2. 微信小程序只有组件和vue类似, property 属性是继承属性, data 是数据源, methods是方法集合。 使用组件的页面可以通过标签属性传递数据, 而组件可以通过 this.triggerEvent派发一个自定义事件,页面通过 onxxxx = “xxx” 接收组件传递数据。使用方法和vue的$emit类似。

      同时,可以在组件内定义slot标签,作为插槽的数据。

    3. 微信小程序的pages页面也有data数据, 通常使用 this.data.xxx获取指定数据。


2.与vue的区别

  • pages页面

    1. vue是通过组件的拼接形成的页面。

    2. 微信小程序也可以使用组件,但每个页面都有特定的标识。并且,page页面中的js数据源,内部其实是一个类,访问属性与方法是通过this.xxx 或者 this.data.xxx获取。

      并且,vue可以直接对data中的属性进行访问,而微信小程序有react中的状态机制,只有通过

      this.setData({ a: xxx}) 改变数据。即使结果相同,但是实现的方式不一样。

  • 模块语法

    1. vue采用简洁明了的方式,将数据与标签属性值 ,文本内容绑定。 列表循环,数据绑定,事件绑定, 条件渲染。

    2. 微信小程序采用的方法很类似vue,但却不一样。

      1. 事件绑定: 按钮采用 bindtab 表示绑定点击事件, 其余的事件采用 bindxxx, 事件名称小写的习惯进行事件绑定。

      2. 列表循环, vue采用 v-for=“item in goods” :key=“item.id” 的方式; 微信小程序采用 wx:for = “{{goods}}” wx:key=“id” wx:for-item = “item” wx:for-index = “id” 的方式。

      3. 数据绑定, 微信小程序采用 数据绑定wx:xxxx = “{{ xxx}}” , 插值{{xxxx}}

        有点类似vue与react的数据绑定的结合。

      4. 条件渲染: vue采用的是v-if 和v-show ,控制节点的显示;微信小程序采用wx:if , wx:elif , wx:else 和 hidden 控制节点的显示。使用方法相同。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/247438?site
推荐阅读
相关标签
  

闽ICP备14008679号