微信小程序基础

简介

它是什么

一套用来开发在微信中运行的手机app框架

组成结构

  • 视图层wxml、样式文件wxss、逻辑层js
  • 数据的双向绑定 - 用户在视图上的修改会自动同步到数据模型中去,同样如果数据模型中的数据发生改变,也会同步到视图层中。

获取AppID

  • 网址:mp.weixin.qq.com/
  • 注册登录后,在 开发->开发设置->开发者ID 中,可找到AppID(在 微信开发者工具 新建小程序项目时需要)

获取微信开发者工具

微信开发者工具

  • 左上角模拟器 编辑器 调试器控制编辑器视图,iphone6 100% WIFI分别为机型选择、缩放比例、网络选择
  • 编辑区域上面的编译模式中,普通编辑模式下,每次刷新都是重新载入index刷新,可以通过添加编译模式,自定义刷新指定页面,这样方便调试
  • 预览 可以在手机上预览小程序
  • 远程调试 手机可以访问小程序,同时编辑器提供控制台一系列工具用于调试

入门

程序主体

app.js
app.json 程序配置
app.wxss 公共样式

app.json 程序配置

app.json记录了一些全局配置:

{
  "pages": [
    "pages/index/index", //第一项就是进入小程序的首页
                         //所以首页的配置是放在第一行的
    "pages/logs/logs",
    "pages/movie/movie"
  ],
  "window": {
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#000",
    "navigationBarTitleText": "Hello",
    "navigationBarTextStyle": "white"
  },
  "tabBar": { //设置tabBar时要和pages一一对应
    "color": "#707070",
    "selectedColor": "#129763",
    "list": [
        {
            "text": "首页",
            "pagePath": "pages/index/index",
            "iconPath": "common/img/main.png",
            "selectedIconPath": "common/img/main_selected.png"
        },
        {
            "text": "日志",
            "pagePath": "pages/logs/logs",
            "iconPath": "common/img/logs.png",
            "selectedIconPath": "common/img/logs_selected.png"
        },
        {
            "text": "视频",
            "pagePath": "pages/movie/movie",
            "iconPath": "common/img/audio.png",
            "selectedIconPath": "common/img/audio_selected.png"
        }
    ]
}
  "debug": true
}
复制代码

pages 页面路径
window 页面的窗口表现
tabBar 底部tab切换
networkTimeout 网络超时时间
debug 是否开启debug模式,debug模式下控制台会打印出详细调试信息 更多关于app.json,详细见官方文档

app.json中的window配置是针对小程序全局的window配置;
页面中的window个性化配置可以去page的json文件中进行另外的配置(navigationBarTitleText)或者在页面js文件中使用js进行控制

window配置项中有一项enablePullDownRefresh是否开启下拉刷新,当设置为true开启下拉刷新,backgroundColor页面背景色在下拉的时候的“缝隙”中才会显示出来。

页面

index,js
index.json 页面配置
index.wxss 页面样式
index.wxml 页面结构

程序实例和页面实例

小程序里,先注册app(只需注册一次,App({})),然后每个页面分别也都要进行页面上的注册(Page({}))。

app只需要注册一次,而每一个页面都需要page来注册一次。


app.js中注册App,会得到一个程序实例,可以被其他页面访问:

//app.js
App({ //程序实例
    gldData: { //程序实例里可以定义全局变量,其他页面可以访问到全局变量
        a: 1
    }
})

//pages/movie/movie.js
//通过getApp全局函数获取应用实例获取应用实例
const app = getApp()
console.log(app.gldData.a) //控制台打印1
复制代码

在页面实例中,可以自定义页面数据,然后在本页面实例中访问自定义的页面数据:

Page({
    pageData: {
        b: 2
    },
    onLoad: function (options) {
        console.log(this.pageData) //{b: 2}
    },
})
复制代码

注册程序是使用App,注册页面是使用Page

生命周期

App生命周期

onLaunch 小程序启动时执行,只运行一次;程序销毁(过了一段时间没有运行,或手动删除了小程序,再次添加运行)之后,再次启动就会执行
onShow 每次在后台切换过来,就会执行
onHide 每次切换到后台,就会执行

//app.js
App({
  onLaunch: function () {
    console.log("小程序启动")
  },
  onShow: function() {
    console.log("切换到小程序运行")
  },
  onHide: function() {
    console.log("小程序被隐藏到后台运行")
  },
  globalData: {
    userInfo: null
  }
})
复制代码

页面生命周期

onLoad 页面加载的时候执行,只会执行一次
onReady 页面第一次渲染完成之后,只会执行一次
onShow 页面显示的时候就会执行,可以执行多次
onHide 页面隐藏就会执行,可以执行多次
onUnload 页面卸载的时候执行,只会执行一次

进入小程序首页,会依次执行onLoad onShow onReady,通过tabBarindex页切换到movie页,index页不会被卸载,所以不会执行onUnload,但是会执行onHide;进入movie页,movie页会执行movie页的onLoad、onShow、onReady,然后再通过tabBar从movie页切换回index页,movie页执行onHide,index页执行onShow。

tab之间的切换,不会让页面重新加载,也就不会卸载,只会隐藏与现实。

页面间通过tab的方式进行切换,onLoad、onReady、onUnload只会执行一次,onShow和onHide交替执行。


在首页中加入导航navigator,通过navigator的方式跳转到视频页,首页还是会执行隐藏方法onHide,视频页左上角出现返回的返回方式,用这种返回方式返回到首页,视频页会发生销毁执行onUnload

  • 在进行NavigatorTo,跳转到的那个目标页面会进行加载(load),跳转之前的页面会被隐藏(hide)
  • 回退(NavigatorBack),目标页面会show(不是加载),回退之前的页面会被卸载(unload)

页面渲染

循环渲染

let arr1 = [
    'Matt', 'Moly', 'Joe', 'Hurley'
]
let arr2 = [
    {id: Math.random(), name: 'Matt'},
    {id: Math.random(), name: 'Moly'},
    {id: Math.random(), name: 'Joe'},
    {id: Math.random(), name: 'Hurley'}
]
Page({
    data: {
        arrNames: arr1
        arr2Names: arr2
    }
})

<view wx:for="{{arrNames}}" wx:key="*this">{{item}}</view>
<view wx:for="{{arr2Names}}" wx:key="id">{{item.name}}</view>
复制代码

给key是为了便于小程序内部机制根据变化去重新渲染,没变化的部分就不会重新玄反,这样有利于性能提升,不给key就会每次全部重新渲染。

条件渲染

Page({
    data: {
        score: 80
    }
})

<view wx:if="{{score>70 && score<90}}">等级B</view>
<view wx:elif="{{score<70 && score>30}}">等级C</view>
<view wx:elif="{{score<30}}">等级D</view>
<view wx:else>等级A</view>
复制代码

block

block并不是真正的元素,用来辅助渲染一些平级的元素

<block wx:if="{{score===80}}">
    <view>{{name}}</view>
    <view>{{score}}</view>
    <view>等级A</view>
</block>
<block wx:else>学生不符合要求</block>
复制代码

使用template

//定义好template,指定name
<template name="hello">
    <view>这是hello行</view>
</template>  
//在页面中需要用到template的地方使用template,is和name要对应起来,这样页面上就能渲染出template中的内容
<template is="hello"></template>
复制代码
//gender为动态内容
<template name="hello">
    <view>{{gender}}</view>
</template>
//类似react,这里传入变量gender,对象的形式
<template is="hello" data="{{gender: 'female'}}"></template>
复制代码
<template name="renderList"> //渲染这个模板时就需要names这样一个数组
    <text>分数:{{score}}</text>
    <block wx:for="{{names}}" wx:key="id">
        <view>{{item.name}}</view>
        <view>哈哈哈</view>
    </block>
</template>
<template is="renderList" data="{{names, score}}"></template>
//js的data中已经定义了names和score
复制代码
  • 如果template的内容很多,可以将模板里的内容单独出来一个文件(template.wxml),放置的位置视情况而定;
  • 页面中引入模板wxml的方式:<import src="template.wxml"/>(这里template.wxml和页面wxml在同一目录下)
  • 如果有公共的头部(header.wxml),在页面中的引入方式:<include src="header.wxml"/>

事件

<view bind:tap="onTap">
    <text>点按我可以打印信息</text>
</view>

onTap(){
    console.log("我是谁")
}
复制代码

其中,bind:tap中的冒号可以不写bindtap 事件函数中额外自带事件对象参数:

onTap(e){
    console.log(e)
}
复制代码


<view bind:tap="onTap" id='view' data-name="容器">
    <text id="text" data-name="文字">点按我可以打印信息</text>
</view>
//样式设置里,id为“view”的元素里还有留白部分

onTap(e){
    console.log(e)
}
复制代码

点击view空白部分:

点击text文字:


bindtap 不阻止事件冒泡
catchtap 阻止事件冒泡

<view catch:tap="onTap" id='view' data-name="容器">
    <text id="text" data-name="文字">点按我可以打印信息</text>
</view>
复制代码

样式wxss

rpx

rpx规定屏幕宽为750rpx。所以不论在何种尺寸的手机中,元素宽度设定为750rpx,这个元素就是充满整个屏幕宽度的。

支持的选择器

.class #id element element, element :after :before

微信小程序不支持css3的属性选择器

wxss引入其他wxss文件

style和data

<text style='color: {{color}}'>点按我可以打印信息</text>

data: {
    color: 'red'
}
复制代码

wxs辅助渲染

wxs是小程序的一套脚本语言。这套脚本语言专门用来提供给页面进行辅助的渲染。

<view class="container">
  <wxs module="tool">
    function createName(names) {
      return names.split(',')
    }
    module.exports = createName
  </wxs>
  <!-- tool就是wxs导出的函数 -->
  <!-- names为js中data定义的 -->
  <view wx:for="{{tool(names)}}">{{item}}</view>
</view>

data: {
    names: "Alice,John,Sara,Dave"
}
复制代码

wxs也可以独立出来:

<!--tool.wxs-->
function createName(names) {
  return names.split(',')
}
module.exports = createName

<!--index.wxml-->
<view class="container">
  <!--tool.wxs和index.wxml在同一目录下-->
  <wxs src="tool.wxs" module="tool"></wxs>
  <view wx:for="{{tool(names)}}">{{item}}</view>
</view>
复制代码

可以在页面里写一段wxs然后使用,也可以独立出来然后在页面里引入使用

注意:wxs不支持es6

setData更新数据与页面

<!--index.wxml-->
<view>{{magicNumber}}</view>
<button bindtap='onTap'>点击</button>
<view>{{sumNumber}}</view>
<text>{{textText}}</text>

//index.js
let num = Math.random()
Page({
  data: {
    magicNumber: num,
    sumNumber: Math.floor(num*100),
    textText: "点击前"
  },

  onTap: function() {
    //this.data.magicNumber = 8999 这种更新方式是没用的
    this.setData({
      magicNumber: Math.random(),
      textText: "点击了"
    })
    let { magicNumber } = this.data
    this.setData({
      sumNumber: Math.floor(magicNumber*100)
    })
  },
})
复制代码

setData的接口是专门用来给我们改变页面的data数据的。

data是同步更新的,然而data更新视图不一定更新,这是一个异步的过程。

组件

小程序内置组件

小程序提供了许多内置的组件,具体可以查阅官网

下面就input输入框内置组件介绍一下:

<input type="text" value="hello" bindinput='onInputChange'></input>

onInputChange(e){
    console.log(e)
    //return Math.random()
    //还可以return出去一个值,控制输入框东西的显示
}
复制代码

在输入框输入的时候,控制台不断打印事件对象,e.detail.value即为输入框内容

自定义组件

自定义组件步骤

Step1 自定义组件,可以单独出来放在一个目录里:

Step2 在组件的js文件中使用Component注册组件(注册组件是用Component,注册页面是用Page):

Step3 在组件的json文件中进行配置,表示这是一个组件:

Step4 在要使用组件的页面的json文件中注册自定义组件:

Step5 在页面中使用自定义组件:

<!--index.wxml-->
<magic-number></magic-number>
复制代码

其中,在magicNumber.wxml中

<text>magicNumber自定义组件</text>
复制代码

所以在index页面中显示的就是文字“magicNumber自定义组件”。

自定义组件定义事件

自定义组件定义事件要写在methods中:

<!-- mnum.wxml -->
<text bindtap="onTap">{{magicNumber}}</text>

// mnum.js
Component({
  data: {
    magicNumber: Math.random()
  },
  methods: { //组件里事件的回调函数要统一写在methods里
    onTap(e) {
      this.setData({
        magicNumber: Math.random()
      })
    }
  }
})
复制代码

自定义组件和父组件通信

<!-- mnum.wxml -->
<text bindtap="onTap">{{magicNumber}}</text>
// mnum.js
Component({
  data: {
    magicNumber: Math.random()
  },
  methods: { //组件里事件的回调函数要统一写在methods里
    onTap(e) {
      this.setData({
        magicNumber: Math.random()
      })
      // 在自定义组件的事件回调函数中,还可以另外再触发事件,名称自定义,用于使用自定义组件的页面中使用
      // 另外可以携带一些参数到页面(这里data中的magicNumber)
      this.triggerEvent('getMagicNumber', this.data.magicNumber)
    }
  }
})

<!--index.wxml-->
<magic-number bind:getMagicNumber="onGetMagicNumber"></magic-number>
// index.js
onGetMagicNumber(e){
    console.log(e)
}
复制代码

点击index中显示的自定义组件文字,控制台打印处相关信息:

可以看到,e.detail打印出来的就是传过去的this.data.magicNumber,因此传过去的信息可以通过e.detail拿到。


点击文字页面取整显示:

<!-- mnum.wxml -->
<text bindtap="onTap">{{magicNumber}}</text>
<!--mnum.js-->
Component({
  data: {
    magicNumber: Math.random()
  },
  methods: { 
    onTap(e) {
      this.setData({
        magicNumber: Math.random()
      })
      this.triggerEvent('getMagicNumber', this.data.magicNumber)
    }
  }
})

<!--index.wxml-->
<magic-number bind:getMagicNumber="onGetMagicNumber"></magic-number>
<view>{{num}}</view>
// index.js
Page({
  data: {
    num: null
  },
  onGetMagicNumber(e){
    this.setData({
      //e.detail即为传过来的那个随机值
      num: Math.floor(e.detail*1000)
    })
  }
})
复制代码

点击文字,index页面中的num也跟着变化,但是在页面初始状态,num显示的是null,为了让num与自定义组件中的随机数一致,在自定义组件的生命周期函数中需要加上attached处理函数(attached会在组件在被使用的页面中被执行):

自定义组件的属性

自定义组件的自定义属性一定要小写,并用烤串形式:

<!--index.wxml-->
<magic-number now-in="Index"></magic-number>

<!-- mnum.wxml -->
<text>{{nowIn}} is here!</text>
// mnum.js
Component({
  properties: {
    // js为驼峰式,使用自定义组件的页面中为烤串式
    nowIn: String
  },
  attached(){
    //组件中通过this.data获取到properties进而获取到nowIn
    console.log(this.data)
  }
})
复制代码

页面显示为Index is here!,控制台打印信息为{nowIn: "Index"}

路由

小程序里两种方式实现导航:navigator 使用API进行导航

navigator

具体参数可在官网查阅 关于navigator

open-type的属性值:
     -- navigate 跳转到
     -- switchTab 以tab方式切换页面
     -- navigateBack 页面回退
     -- redirect 重定向
     -- reLaunch 重新加载url页面,顶部不会出现回退按钮


open-type默认情况

<navigator url='/pages/movie/movie' open-type='navigate'>到movie</navigator>
<!-- open-type的默认值为navigate -->
<navigator url='/pages/about/about'>到about</navigator>
复制代码

open-typenavigate(跳转到)的navigator方式,顶部会出现回退的样式:


open-type='switchTab'

<navigator url='/pages/movie/movie' open-type='switchTab'>以tab方式切换到movie页</navigator>
<!-- 要实现以tab方式切换页面,在app.json中要配合写上tab的配置项 -->
复制代码

当要跳转的页面是tab页时(在app.json中设置了tabBar的页面),跳转方式必须是switchTab,否则跳不过去,总结为一句话就是 切换到tab页时只能使用switchTab方式进行跳转


open-type='navigateBack'

<navigator open-type='navigateBack'>回退</navigator>
复制代码

点击后直接回退到上一页面。


open-type='redirect'

<!--index.wxml-->
<view>首页</view>
<navigator url='/pages/about/about'>到about</navigator>

<!--pages/about/about.wxml--> 
<view>about页</view>
<navigator url='/pages/movie/movie' open-type='redirect'>重定向到movie页</navigator>

<!--pages/movie/movie.wxml-->
<view>movie页</view>
<navigator url="/pages/about/about">去about</navigator>
复制代码

从首页进入about页,在about页重定向到movie页,点击顶部的回退,直接回退到首页,而不是about页 index-about-在about页重定向到movie页-到movie页后点击顶部回退-直接回到index页


open-type='reLaunch'

<!--index.wxml-->
<view>首页</view>
<navigator url='/pages/about/about'>到about</navigator>

<!--pages/about/about.wxml-->
<view>about页</view>
<navigator url='/pages/movie/movie' open-type='reLaunch'>reLaunch movie页</navigator>

<!--pages/movie/movie.wxml-->
<view>movie页</view>
<navigator url="/pages/about/about">去about</navigator>
复制代码

从首页进入about页,在about页以reLaunch的方式进入movie页,movie页就是小程序当前加载的唯一页面 index-about-在about页以reLaunch方式进入movie页-movie页,当前小程序加载的唯一页面,顶部没有返回按钮

使用API进行导航

navigate - wx.navigateTo(OBJECT) - 跳转到
redirect - wx.redirectTo(OBJECT) - 重定向
switchTab - wx.switchTab(OBJECT) - 跳转到某个tab
navigateBack - wx.navigateBack(OBJECT) - 回退
reLaunch - wx.reLaunch(OBJECT) - 重加载

<!--index.wxml-->
<view>首页</view>
<view bindtap='onGotoMovie'>使用API方式跳转到movie</view>
// index.js
Page({
  onGotoMovie(){
    wx.navigateTo({
      url: '/pages/movie/movie',
    })
  }
})
复制代码

点击即可以navigate的方式跳转到movie页

授权相关API

授权方式:
用户信息授权 (授权button)
其他信息授权 (wx.authorize(OBJECT)先进行授权直接获取信息,未授权会先进行授权)

用户信息授权

<!--index.wxml-->
<!-- 点击按钮出现用户授权弹框,授权弹框中点击取消或确定会执行回调函数 -->
<!-- onGetUserInfo即为执行的回调 -->
<button open-type='getUserInfo' bindgetuserinfo='onGetUserInfo'>获取用户信息</button>
// index.js
Page({
  onGetUserInfo(e){ //接收一个事件对象,将其打印出来
    console.log(e)
  }
})
复制代码

点击按钮,用户出现授权弹窗:(之前进行过授权操作可先清除授权数据)

在弹框中分别选择允许和拒绝的操作,事件回调函数会在控制台中打印出相应的信息:

展开用户在弹框中进行了允许的操作执行的事件回调打印出来的信息可以看到:

其中也可以看到,e.detail.userInfo为用户的一些基本信息。

进行过用户信息授权以后,再去点击获取用户信息授权的按钮,弹框不会再次弹出,而且会再次执行允许授权的那个回调函数。

小程序与小游戏获取用户信息接口调整

其他信息授权

wx.authorize

<!--index.wxml-->
<button bindtap='onAuthLocation'>授权位置</button> 
// index.js
Page({
  onAuthLocation(){
    wx.authorize({ //authorize里面传一个对象,这个对象会指定进行哪一类的授权,是地理位置的授权还是其他授权
      scope: 'scope.userLocation', //scope,指定是进行哪一类的授权      
                                   //scope.userLocation,位置授权
      success: msg=>console.log(msg, 'location success'),
      fail: e=>console.log(e, 'location fail')
    })
  }
})
复制代码

点击按钮,出现授权弹窗,根据用户的选择,会执行success或者fail的回调:

以点击允许为例,控制台打印为:

第一次点击授权按钮时,授权弹窗出现时,如果选择了允许授权,再次点击时默认调用的就是允许授权的回调(不会再次出现授权弹窗);如果选择了拒绝授权,再次点击时默认调用的就是拒绝授权的回调(不会再次出现授权弹窗)

wx.authorize只是进行授权,并不会返回授权相关的信息,比如上述例子里进行位置授权,允许授权后,并不会返回位置信息。


wx.getLocation

<!--index.wxml-->
<button bindtap='onGetLocation'>获取位置</button> 
// index.js
Page({
  onGetLocation(){
    wx.getLocation({ //获取到授权后的信息
      success: msg=>console.log(msg, "位置"),
      fail: e=>console.log(e, "没获取到位置")
    })
  }
})
复制代码

点击按钮:

选择确定,控制台就会打印出地理信息相关信息:


wx.getSetting

<!--index.wxml-->
<button bindtap='onGetSetting'>获取授权信息</button> 
// index.js
Page({
  onGetSetting(){
    wx.getSetting({ //微信提供这样一个API用于告诉哪些信息授权了哪些信息没有授权
      success: msg => console.log(msg, "授权相关信息"),
    })
  }
})
复制代码


wx.openSetting

没有弹框的情况下我们可以手动调用wx.openSetting去对授权信息进行设置:

<!--index.wxml-->
<button bindtap='onGotoSetting'>打开授权信息面板</button> 
// index.js
Page({
  onGotoSetting(){
    wx.openSetting({
      success: msg=>console.log(msg, "设置完成")
    })
  }
})
复制代码

点击可以进入授权面板:

在这个授权面板里我们就可以手动对授权信息重新进行设置。

使用缓存

微信里可以同步或者异步地缓存数据,先介绍一下异步缓存数据的方式:

<!--index.wxml-->
<button bindtap='onCache'>缓存数据</button>
// index.js
Page({
  onCache(){
    wx.setStorage({
      key: 'name',
      data: {p1: 'Matt'}, //data可以指定字符串,也可以指定对象
      success: ()=>{
        wx.getStorage({
          key: 'name',
          success: data=>{
            console.log(data)
          }
        })
      }
    })
  }
})
复制代码

点击按钮,即可实现缓存:

同时可以取到缓存中name为key的缓存数据,在控制台打印出来:

以上设置缓存的方式是异步的。

以下为同步设置缓存的方式:

<!--index.wxml-->
<button bindtap='onCache'>缓存数据</button>
// index.js
Page({
  onCache(){
    wx.setStorageSync('names', 'Hurley') //第一个参数是键值,第二个参数是要存的数据
                                         //存的数据可以是对象,也可以是字符串
                                         //所以这里键值是'names',存的数据是字符串'Hurley'
    let ns = wx.getStorageInfoSync('names')
    console.log(ns)
  }
})
复制代码

点击按钮,缓存中写入names:

并在控制台打印出:


设置缓存、获取缓存、移除缓存

<!--index.wxml-->
<view>{{name}}</view>
<button bindtap='onSet'>设置缓存数据</button>
<button bindtap='onGetName'>获取name</button>
<button bindtap='onRemoveName'>移除name</button>
// index.js
Page({
  data: {
    name: 'hello'
  },
  onSet(){
    wx.setStorageSync('name', 'Hurley')
  },
  onGetName(){
    let n = wx.getStorageSync("name")
    console.log(n)
    if(n){
      this.setData({
        name: n
      })
    }
  },
  onRemoveName(){
    //缓存中key为“name”的缓存被移除
    wx.removeStorageSync('name') 
  }
})
复制代码

点击“设置缓存数据”,缓存中写入key为“name”的缓存;
然后点击“获取name”,从缓存中获取到key为“name”的value值,并更新到data中的name,在视图中{{name}}也会更新;
然后点击“移除name”,缓存中key为“name”的缓存数据被移除。

请求与反馈

设置不校验域名

<!--index.wxml-->
<button bindtap='onReq'>请求服务</button>
// index.js
Page({
  onReq(){
    wx.request({
      url: 'http://localhost:3000/hello',
      data: {
        name: 'Joe'
      },
      method: 'POST',
      success: data=>{
        console.log(data)
      }
    })
  }
})
复制代码

点击按钮,控制台出现报错:

因此,在小程序里进行请求需要进行相应配置:

这样就可以通过接口请求到数据,快速调试时设置不校验域名,可以请求到数据

请求过程中的反馈

分类:
前端
标签: