说在前面
1、小程序没有DOM对象,一切基于组件化。
2、在小程序中 ,通过App()来注册一个小程序 ,通过Page()来注册一个页面。
3、微信官方提供屏幕适配方案:
- 以iPhone6的物理像素为标准 750,
- iPhone6中 1rpx = 1物理像素 = 0.5px
- 1rpx = (设备视口宽度 / 750 )px
- 默认已经做了 viewport 适配处理
开发前准备
注册小程序账号
我这里注册的是个人账号。微信小程序官网教程写得很详细,亦或参考文章。
注册好之后,回到你的小程序首页=>开发=>开发设置,就能看到你的AppID
然后再下载开发工具。
基本项目目录
快速创建一个带模板的项目,项目目录大致如下
注册程序及页面
注册程序app.js
项目根目录必须有一个 app.js 文件,文件内必须调用 App() 函数,就是创建一个根实例。
- App() 函数用来注册一个小程序。接受一个 Object参数,其指定小程序的生命周期回调等。
- App() 必须在 app.js 中调用,必须调用且只能调用一次。不然会出现无法预期的后果。
实例代码放在后面生命周期那段
注册页面
Page(Object)
函数用来注册一个页面。接受一个 Object
类型参数,其指定页面的初始数据、生命周期回调、事件处理函数等。
实例代码放在后面生命周期那段
配置
全局配置app.json
项目根目录下的 app.json
文件用来对微信小程序进行全局配置,决定页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 等。
最基础配置
{
"pages":[
"pages/index/index",
"pages/logs/logs"
],
"window":{
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "samfung demo2",
"navigationBarTextStyle":"black"
}
}
页面配置
每一个小程序页面也可以使用.json
文件来对本页面的窗口表现进行配置。
页面的配置只能设置 app.json
中部分 window
配置项的内容,页面中配置项会覆盖 app.json
的 window
中相同的配置项。
基本配置
{
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black",
"navigationBarTitleText": "微信接口功能演示",
"backgroundColor": "#eeeeee",
"backgroundTextStyle": "light"
}
详细配置选项请看官方文档
生命周期
小程序的生命周期app.js
官方文档上的参数说明:
属性 | 类型 | 描述 | 触发时机 |
---|---|---|---|
onLaunch | Function | 生命周期回调—监听小程序初始化 | 小程序初始化完成时(全局只触发一次) |
onShow | Function | 生命周期回调—监听小程序显示 | 小程序启动,或从后台进入前台显示时 |
onHide | Function | 生命周期回调—监听小程序隐藏 | 小程序从前台进入后台时 |
onError | Function | 错误监听函数 | 小程序发生脚本错误,或者 api 调用失败时触发,会带上错误信息 |
onPageNotFound | Function | 页面不存在监听函数 | 小程序要打开的页面不存在时触发,会带上页面信息回调该函数 |
其他 | Any | 开发者可以添加任意的函数或数据到 Object 参数中,用 this 可以访问 |
示例代码:
App({
//小程序初始化完成时触发,全局只触发一次
onLaunch(options) {
// Do something initial when launch.
},
//小程序启动,或从后台进入前台显示时触发
onShow(options) {
// Do something when show.
},
//小程序从前台进入后台时触发
onHide() {
// Do something when hide.
},
//小程序发生脚本错误或 API 调用报错时触发
onError(msg) {
console.log(msg)
},
globalData: 'I am global data'
})
页面的生命周期
常见的几个钩子函数:
onLoad(Object query): 页面加载,一个页面只会调用一次。可以在 onLoad 的参数中获取打开当前页面路径中的参数。
onShow(): 页面显示,每次打开页面都会调用一次。
onReady(): 页面初次渲染完成,一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互。
onHide(): 页面隐藏,当navigateTo或底部tab切换时调用。
onUnload(): 页面卸载,当redirectTo或navigateBack的时候调用。
Page({
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
console.log('页面加载')
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
console.log('页面初次渲染完成')
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
console.log('页面显示')
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
console.log('页面隐藏')
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
console.log('页面卸载')
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})
自定义组件
小程序也可以自定义组件,写过 Vue 组件就很容易理解。
组件生命周期
created() 组件实例化,但节点树还未导入,因此这时不能用setData,从父组件传来的属性值这时还是空值。
attached() 节点树完成,可以用setData渲染节点,但无法操作节点,从父组件传来的属性值这时可以获取。
ready() 组件布局完成,这时可以获取节点信息,也可以操作节点
moved() 组件实例被移动到树的另一个位置
detached() 组件实例从节点树中移除
组件
这里只是随便列举几个,更多的组件请看官方文档
<image>
按比例拉伸,加入mode属性
<image src='xxx' mode='widthFix'></image>
<navigator>
页面跳转,去掉点击时样式,加入hover-class属性
<navigator url='/pages/index/index' hover-class="none"></navigator>
<swiper>
<swiper indicator-dots="{{true}}"
autoplay="{{true}}" interval="{{3000}}" duration="{{500}}" circular="{{true}}">
<block wx:for="{{imgUrls}}" wx:key="{{index}}">
<swiper-item>
<image src="{{item}}" class="slide-image" width="355" height="150"/>
</swiper-item>
</block>
</swiper>
<input>
<input class='search-input' type='text' confirm-type='search' value='{{searchStr}}' placeholder='请输入关键字' bindconfirm='handleSearch'/>
<button>转发
<button class='forward-btn' open-type='share'>
<text class='iconfont icon-tubiao212' style='font-size:36px;'></text>
</button>
去掉 button 边框
button::after {
border: none
}
<text>
加入 decode 属性可解析空格符
<text class='info' decode='{{true}}'>{{userInfo.nickName}} 1分钟前</text>
视图层wxml
数据绑定{{}}
跟vue差不多,小程序里绑定数据也是用双大括号 {{}} 将变量或者简单表达式包起来。比如:
<!-- message在Page()中data定义 -->
<view>{{ message }}</view>
与vue不同的在于组件属性动态绑定,比如:
<!-- Vue -->
<img :src="imgSrc"/>
<!-- 小程序 -->
<image src="{{imgSrc}}"></image>
列表渲染wx:for
与Vue类似,小程序在组件上使用 wx:for 控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。默认数组的当前项的下标变量名默认为 index
,数组当前项的变量名默认为 item。,相当于Vue中的 v-for="(item, index) in array"
<view wx:for="{{array}}">{{index}}: {{item.message}}</view>
block wx:for
也可以将 wx:for
用在<block/>
标签上,以渲染一个包含多节点的结构块。例如:
<block wx:for="{{[1, 2, 3]}}">
<view>{{index}}:</view>
<view>{{item}}</view>
</block>
模板template
WXML提供模板(template),可以在模板中定义代码片段,然后在不同的地方调用。与Vue中定义功能组件相似。
创建模板文件,可以不需要js和json文件
template/list-item.wxml 注意要给template命名
<!--pages/template/list-item.wxml-->
<template name="listItemTmp">
<view class='list-item-container'>
<image src='{{item}}'></image>
<text>列表{{index+1}}</text>
</view>
</template>
template/list-item.wxss
/* pages/template/list-item.wxss */
.list-item-container{
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
margin: 20rpx 0;
}
在需要使用该模板的 wxml 以及 wxss 文件中引入
<!-- wxml -->
<import src="/pages/template/list-item.wxml" />
/* wxss */
@import '/pages/template/list-item.wxss';
在页面中使用该模板,注意,这里往模板传入一个字符串数组的 item 以及 index
Page({
data: {
listImgUrl: [
'http://ws2.sinaimg.cn/large/69f81679gy1fxp41ocoxzj21400u0awg.jpg',
'http://ws2.sinaimg.cn/large/69f81679gy1fxp41ocoxzj21400u0awg.jpg',
'http://ws2.sinaimg.cn/large/69f81679gy1fxp41ocoxzj21400u0awg.jpg',
'http://ws2.sinaimg.cn/large/69f81679gy1fxp41ocoxzj21400u0awg.jpg',
'http://ws2.sinaimg.cn/large/69f81679gy1fxp41ocoxzj21400u0awg.jpg'
]
}
})
<import src="/pages/template/list-item.wxml" />
<view>
<block wx:for="{{listImgUrl}}" wx:key="{{index}}">
<template is="listItemTmp" data="{{item, index}}" />
</block>
</view>
最后列表渲染效果,没样式,很随意
解析html内容--wxParse
都知道小程序是基于组件化的,并不能直接识别 html 内容,如果我们要显示如博客文章这样的 html 内容是就需要第三方插件 wxParse 。
使用方法很简单,首先去到 wxParse 项目地址,下载项目代码,然后将 wxParse 整个文件夹拷贝到你的小程序根目录下。
然后在需要解析渲染 html 的wxml和wxss页面中分别引入
<!-- wxml -->
<import src="/wxParse/wxParse.wxml" />
/* wxss */
@import '/wxParse/wxParse.wxss';
js代码
onLoad: function (options) {
let htmlStr = `<h2>第一篇文章</h2>
<img src="http://ws2.sinaimg.cn/large/69f81679gy1fxp41ocoxzj21400u0awg.jpg"><br>
<a href="http://fengyongru.com">hello</a>
<p>Sam</p>`
const that = this;
// wxParse已经setData了
WxParse.wxParse('article', 'html', htmlStr, that)
}
wxml
<!-- wxml -->
<import src="/wxParse/wxParse.wxml" />
<template is="wxParse" data="{{wxParseData: article.nodes}}"></template>
使用过程中遇到一个问题,就是在 wxParse 里 img 的宽高可能会写死了,这是自己写的 img 样式并不起作用,于是我就简单粗暴的修改 wxParse/wxParse.wxml 文件,让 img 标签宽度100%,如下
<template name="wxParseImg">
<image class="{{item.classStr}} wxParse-{{item.tag}}" data-from="{{item.from}}" data-src="{{item.attr.src}}" data-idx="{{item.imgIndex}}" src="{{item.attr.src}}" mode="aspectFit" bindload="wxParseImgLoad" bindtap="wxParseImgTap" mode="widthFix" style="width:100%"
/>
</template>
API
获取用户信息wx.getUserInfo(object)
注意:此接口有调整,使用该接口将不再出现授权弹窗,请使用 <button open-type="getUserInfo"></button> 引导用户主动进行授权操作。
- 当用户未授权过,调用该接口将直接报错
- 当用户授权过,可以使用该接口获取用户信息
判断是否授权
wx.getSetting({
success: res => {
console.log(res)
// 如果已授权
if (!res.authSetting['scope.userInfo']) {
...
}
}
})
需要获取用户信息的页面
// 需要获取用户信息的页面
onLoad: function (options) {
// 获取用户信息
wx.getUserInfo({
success: res => {
// 如果已经授权了
console.log('获取用户信息成功', res.userInfo)
this.setData({
userInfo: res.userInfo
})
},
fail: () => {
console.log('获取用户信息失败');
// 如果没授权则跳转到用户授权页面
wx.navigateTo({
url: '/pages/getUserInfo/getUserInfo',
})
}
})
}
用户授权页面wxml
<button open-type="getUserInfo" bindgetuserinfo="handleGetUserInfo">授权登录</button>
用户授权页面js
handleGetUserInfo(e){
//此处授权得到userInfo
console.log(e.detail.userInfo);
//接下来写业务代码
// 如果允许授权则返回刚才的页面
if(e.detail.userInfo){
wx.navigateBack({
delta: 1
})
}
}
发送请求wx.request(Object)
微信小程序默认是发起 https 请求,如果想要发起 http 请求,点击开发工具的详情,然后再勾选“不校验域名”
wx.request({
url: 'http://localhost:8080/lala',
method: 'GET',
success: res => {
console.log(res)
}
})
本地存储wx.setStorage(Object)
这里结合用户授权处理,用户授权后将用户信息保存
if(e.detail.userInfo){ //如果用户授权了
wx.setStorage({ //存储用户信息
key: 'userInfo',
data: e.detail.userInfo,
success: res => {
wx.navigateBack({ //跳回上一页
delta: 1
})
}
})
}
获取
wx.getStorage({
key: 'userInfo',
success: res => {
console.log(res);
}
})
返回上一页并且刷新上一页的数据
有时候我们会有这样的需求,比如说,去到授权页面,授权之后返回上一页但这是上一页的数据并不会被刷新,就无法判断是否已经授权,除非你手动 onLoad 。解决办法:可以在当前页面通过 getCurrentPages() 获取上一页的实例对象,这时你就可以刷新上一页的数据或者触发上一页的方法。
let pages = getCurrentPages();
if(pages.length > 1){
let beforePage = pages[pages.length - 2];
let currentPage = pages[pages.length - 1];
// 直接修改上一页的数据
beforePage.setData({
userInfo: xxx
})
// 触发上一页的方法
beforePage.xxx()
// 返回上一页
wx.navigateBack({
delta: 1
})
}
路由参数
小程序中可以用 <navigator> 标签跳转,也可以用 wx.navigateTo() 或 wx.redirectTo() 进行路由跳转。这里用 <navigator> 演示:
跳转到 detail 页面,带参数 articleId
<navigator url='/pages/detail/detail?articleId={{index}}' class='article container' wx:for="{{listImgUrl}}" wx:key="{{index}}">
<template is="listItemTmp" data="{{item, index}}" />
</navigator>
然后路由参数在 detail 页面钩子函数 onLoad(options) 中获取
onLoad: function (options) {
console.log(options)
}
控制台打印