开始
对于一个使用过Vue.js的前端来说,小程序和vue的语法很像,难道不大,增加了一些基于支付宝的内置功能,简单来说,支付宝就是一个浏览器,小程序是支付宝的Html而已.。
Tips, 开发工具编译经常不及时,故意写错代码,比如写错标签不闭合,或者乱写,就马上有效果 !!!
小程序的使用
小程序无需安装,用户第一次使用小程序时,支付宝 App 会从服务器下载小程序的资源,下载后的小程序资源会缓存在支付宝的客户端一段时间。
当用户再次打开已经缓存资源的小程序时,会跳过下载过程,能够更快地打开小程序
当用户首次打开小程序时候,小程序会处于前台运行状态
。
用户点击右上角关闭按钮关闭小程序,或者按下设备 Home 键离开支付宝 App 时,小程序并不会直接销毁,而是进入后台运行状态
。
从后台运行切换为前台运行: 当未被系统销毁的小程序再度被打开或者激活时,会从后台运行切换为前台运行
用户点击右上角关闭按钮关闭小程序时,小程序仅是进入后台运行,不会被销毁。只有当小程序进入后台运行状态一定时间,或者占用系统资源过高时,才会被真正销毁
核心的业务能力
支付收单、营销服务、会员管理 、芝麻信用、位置服务、供应链 、资金管理、金融服务,如蚂蚁借呗
小程序启动和入口
小程序启动方式
冷启动
: 当用户打开未启动过,或者是已经销毁的小程序时,称为冷启动。此时小程序会执行初始化,初始化完成后,会触发 onLaunch 回调函数。
热启动
: 当用户打开已经关闭但仍处于后台运行的小程序时,称为热启动。在这种情况下,小程序并不会被销毁后重启,而仅是从后台切换到前台,此时,onShow 函数会触发,onLaunch 回调函数不会被触发。
小程序的入口
- 扫一扫
- 搜索
- 朋友tab页
- 支付成功页
- 小程序收藏
- 生活号关联
- 卡包
小程序入口的场景值
在相关生命周期里,可以获取到时怎么进入小程序的,详细的数据
App({
onLaunch(options) {
console.log('App onLaunch Scene:', options.scene);//options.scene 是 String 类型的
},
onShow(options) {
console.log('App onShow Scene:', options.scene);
},
})
代码层面
可以使用npm包,app.acss 作为全局样式,作用于当前小程序的所有页面
// 背景色渐变
background-image: linear-gradient(90deg, rgb(5, 131, 68) 0%, #3264C5 99%);
// getApp方法获取顶层app实例
var app = getApp();
console.log(app.globalData); // 获取 globalData
app.js
App()
代表顶层应用,管理所有页面和全局数据,以及提供生命周期回调等
App({
// 比如 alipays://platformapi/startapp?appId=1999&query=number%3D1&page=x%2Fy%2Fz 打开小程序
onLaunch(options) {
// 第一次打开
console.log(options.query);
// {number:1}
console.log(options.path);
// x/y/z
},
onShow(options) {
// 小程序启动,或从后台被重新打开
},
onHide() {
// 小程序从前台进入后台
},
onError(msg) {
// 小程序发生脚本错误或 API 调用出现报错
console.log(msg);
},
globalData: {
// 全局数据
name: 'alipay',
},
});
app.json
整个应用的配置
{
// 配置页面
"pages": [
"pages/index/index"
],
// 配置插件
"plugins": {
"myPlugin": {
"version": "*",
"provider": "2019120769656826"
}
},
"window": {
"transparentTitle": "always", // 导航栏透明设置 默认 none,支持 always 一直透明 / auto 滑动自适应 / none 不透明。
"titlePenetrate": true,
"backgroundImageColor": "#3264C5",
"defaultTitle": "defaultTitle", // 默认标题
"allowsBounceVertical":"NO", // 允许下拉。默认"yes"
"pullRefresh" : true, // 支持下拉刷新吗,默认 true,需要允许下拉才可以
"titlePenetrate": "YES" // 是否允许导航栏点击穿透。默认 NO,支持 YES / NO
"titleImage": // 导航栏图片地址 ,
"titleBarColor": "rgba(0,0,0,0.1)" // 导航栏背景色
},
// 配置底部导航tabs
"tabBar": {
"textColor": "#111",
"selectedColor": "blue",
"backgroundColor": "#ffffff",
"items": [
{
"pagePath": "pages/index/index",
"name": "首页",
"icon" : "", // 小图标
"activeIcon" : "",
},
{
"pagePath": "pages/logs/logs",
"name": "日志"
}
]
}
}
page.json
在 /pages 目录中的 .json 文件用于配置当前页面的窗口表现。页面配置比 app.json 全局配置简单得多,只能设置 window 相关配置项,但无需写 window 这个键
// css
// page页面 page元素声明整个页面的样式
page {
background-color: #fff;
}
// json
// 配置 optionMenu 点击后触发 onOptionMenuClick
{
"optionMenu": {
"icon": "https://img.alicdn.com/tps/i3/T1OjaVFl4dXXa.JOZB-114-114.png"
},
"titlePenetrate": "YES",
"barButtonTheme": "light"
}
page.js
page.js是每个页面的逻辑
// pages/index/index.js
Page({
// 和 vue一样,是对象时,所有页面公用,用函数保证每个页面数据独立
// this.data无法修改数据,this.setData修改
data: (){
return {
title: "Alipay",
},
}
// 页面初始化时触发。一个页面只会调用一次。
// query 为 my.navigateTo 和 my.redirectTo 中传递的 query 对象。
// query 内容格式为:“参数名=参数值&参数名=参数值…”。
onLoad(query) {
// 页面加载
},
onShow() {
// 页面显示
},
// onReady === didMount
onReady() {
// 页面加载完成
},
onHide() {
// 页面隐藏
// 页面隐藏/切入后台时触发。 如 my.navigateTo 到其他页面或底部 tab 切换等。
},
onUnload() {
// 页面被关闭
// 页面卸载时触发。 如 my.redirectTo 或 my.navigateBack 到其他页面等。
},
onTitleClick() {
// 标题被点击
},
onPullDownRefresh() {
// 页面被下拉
},
onTabItemTap(){
// 点击tabItem时触发
}
onPageScroll({scrollTop}){
},
onReachBottom() {
// 页面被拉到底部
},
onShareAppMessage() {
// 返回自定义分享信息
},
// 事件处理函数对象
events: {
onBack() {
console.log('onBack');
},
},
// 自定义事件处理函数
viewTap() {
this.setData({
text: 'Set data for update.',
});
},
// 自定义事件处理函数
go() {
// 带参数的跳转,从 page/ui/index 的 onLoad 函数的 query 中读取 type
my.navigateTo({url:'/page/ui/index?type=mini'});
},
// 自定义数据对象
customData: {
name: 'alipay',
},
});
page.axml
使用include 直接引入页面
// html 条件渲染
<view a:if="{{length > 5}}"> 1 </view>
<view a:elif="{{length > 2}}"> 2 </view>
<view a:else> 3 </view>
<!-- index.axml -->
<include src="./header.axml"/>
<view> body </view>
<include src="./footer.axml"/>
<!-- header.axml -->
<view> header </view>
<!-- footer.axml -->
<view> footer </view>
使用import引入模板
<!-- item.axml -->
<template name="item">
<text>{{text}}</text>
</template>
<import src="./item.axml"/>
<template is="item" data="{{text: 'forbar'}}"/>
页面跳转
// 1. 类似A链接的方式
<view class="page">
<view class="page-description">导航栏</view>
<navigator open-type="navigate" url="./navigate" hover-class="navigator-hover">跳转到新页面</navigator>
<navigator open-type="redirect" url="./redirect" hover-class="navigator-hover">在当前页打开</navigator>
<navigator open-type="switchTab" url="/page/API/index/index" hover-class="navigator-hover">跳转到另外一个 Tab - API</navigator>
<navigator open-type="reLaunch" url="/page/component/index" hover-class="navigator-hover">重新打开</navigator>
<navigator open-type="navigateBack" hover-class="navigator-hover">返回上一页面</navigator>
</view>
// 2. js跳转
my.navigateTo({ url: './back' })
my.redirectTo({ url: './back' })
acss
rpx(responsive pixel)可以根据屏幕宽度进行自适应,规定屏幕宽为 750rpx。以 Apple iPhone6 为例,屏幕宽度为 375px,共有 750 个物理像素,则 750 rpx = 375 px = 750 物理像素,1rpx = 0.5 px = 1 物理像素。
右上角的分享
Page ({
data: {
canIUseShareButton: true
},
// 用户点击分享按钮的时候会调用此事件需要返回一个对象(Object)类型,用于自定义分享内容
onShareAppMessage () {
return {
title : '分享 View 组件' ,
desc : 'View 组件很通用' ,
path : 'page/component/view/view' ,
};
},
});
event 事件
点击事件,如onTap
,on开头就是想上冒泡,catchTap就是不冒泡
常用事件有,tap,longTap(长按,0.5s),touchStart,touchEnd,touchCancel,touchMove
event对象有 type,timeStamp,target,属性,自定义的数据,要 data-小写字母来传值,如data-ha-sa,取值的时候,会转成驼峰,haSa
不同的事件,如tap和touch的event不一样
缓存
set, get, clear, remove 4个方法
大小最多是10M
同步和异步两种方法
不建议使用cookie和session
my.setStorage('name','huahua')
my.setStorageAsync('name','huahua')
自定义组件(简直的vue的一样)
小程序基础库从 1.7.0 版本开始支持自定义组件功能。
通过 my.canIUse('component')
判断自定义组件功能是否在当前版本使用;
从 1.14.0 版本开始,自定义组件支持 component2
相关功能,通过调用 my.canIUse('component2')
可判断新自定义组件功能是否可在当前版本使用, component2 相关功能如下:新增 onInit、deriveDataFromProps 生命周期函数 ,支持使用 ref 获取自定义组件实例 。
[componentName].json
文件
{
"component": true, // 必选,自定义组件的值必须是 true
"usingComponents": {
"other":"../other/index" // 依赖的组件
}
}
组件的样式
组件的样式,如果写了page样式,是代表他使用者页面的样式,而不是他的样式
右键新建一个组件即可,使用时,在当前页面的json中进入组价声明即可
{
"usingComponents": {
"my-component":"/components/index/index"
}
}
slot 插槽
// demo 组件
<view>
this is demo component
<slot>
<view>this is default slot</view>
</slot>
haha
<slot name="header"/>
</view>
// 使用自己的内容替换默认插槽
<demo>
<view>this is my slot</view>
<view slot="header">header</view>
</demo>
组件获取参数,通过在js文件中的data和props参数,可以使用组件自己的数据和外部传递的数据
// /components/index/index.js
Component({
data: {
x: 1,
},
props: {
y: '',
},
});
<!-- /components/index/index.axml -->
<view>component data: {{x}}</view>
<view>page data: {{y}}</view>
自定义组件声明周期
Component({
data: {
o: {
value: "scope-value"
}
},
onInit() { // 组件创建时触发
console.log("i1 onInit", this.props, this.data);
},
deriveDataFromProps(nextProps) { // 组件创建时触发或更新时触发
console.log("i1 deriveDataFromProps", nextProps, this.props, this.data);
},
didMount() { // 组件创建完毕时触发
console.log("i1 didMount", this.props, this.data);
},
didUpdate(prevProps, prevData) { // 组件更新完毕时触发
console.log("i1 didUpdate", prevProps, prevData, this.props, this.data);
},
didUnmount() { // 组件删除时触发
console.log("i1 didUnmount");
},
methods: {
change() {
this.setData({ 'o.value': "changed-scope-value" });
}
}
});
mixin
用来封装组件的多个公用逻辑
// /mixins/lifecycle.js
export default {
onInit(){
console.log('init');
},
deriveDataFromProps(nextProps){},
didMount(){},
didUpdate(prevProps,prevData){},
didUnmount(){},
};
// /components/index/index.js
import lifecycle from '/mixins/lifecycle';
const initialState = {
data: {
isLogin: false,
},
};
const defaultProps = {
props: {
age: 30,
},
};
const methods = {
methods: {
onTapHandler() {},
},
}
Component({
mixins: [
lifecycle,
initialState,
defaultProps,
methods
],
data: {
name: 'alipay',
},
});
ref
ref可以获取组件的实例,获取到组件实例之后,可以直接操作组件
// /pages/index/index.js
Page({
plus() {
this.counter.plus();
},
// saveRef 方法的参数 ref 为自定义组件实例,运行时由框架传递给 saveRef
saveRef(ref) {
// 存储自定义组件实例,方便以后调用
this.counter = ref;
},
});
<!-- /pages/index/index.axml -->
<my-component ref="saveRef" />
<button onTap="plus">+</button>
// 定义组件时。component2,可以指定ref返回的值,不是默认的组件的this
Component({
ref() {
return { key: 'value' }
}
})
内置标签
<view> 约等于 div
scroll-view 约等于 div里边加了 scroll-auto
swiper 内置轮播图
文本要用text组件
表单form和html差不多,有原生picker支持
地图 map,内置高德地图
可以使用canvas画布
API
-
my.SDKVersion
查看当前基础库版本号,可以在控台中心设置小程序需要库的最低版本号 -
my.hideKeyboard
隐藏输入键盘 -
my.startPullDownRefresh
主动触发下拉刷新,my.stopPullDownRefresh
主动结束下拉刷新,onPullDownRefresh
监听下拉刷新
通讯录联系人
// 获取本地通讯录
choosePhoneContact() {
my.choosePhoneContact({
success: (res) => {
my.alert({
content: `姓名:${res.name} 电话:${res.mobile}`
});
},
fail: (res) => {
my.alert({
content: 'choosePhoneContact response: ' + JSON.stringify(res),
});
},
});
},
// 获取支付宝通讯录
chooseAlipayContact() {
my.chooseAlipayContact({
count: 1,
success: (res) => {
my.alert({
content: `真实姓名:${res.contacts[0].realName} 邮箱:${res.contacts[0].email} 电话:${res.contacts[0].mobile}`
});
},
fail: (res) => {
my.alert({
content: 'chooseAlipayContact response: ' + JSON.stringify(res)
});
},
});
},
// 获取联系人
chooseContact() {
my.chooseContact({
chooseType: 'multi', // 多选模式
includeMe: true, // 包含自己
includeMobileContactMode: 'known',//仅包含双向手机通讯录联系人,也即双方手机通讯录都存有对方号码的联系人
multiChooseMax: 1, // 最多能选择1个联系人
multiChooseMaxTips: '超过选择的最大人数了',
success: (res) => {
my.alert({
content: `真实姓名:${res.contactsDicArray[0].realName} 展示姓名:${res.contactsDicArray[0].displayName} 电话:${res.contactsDicArray[0].mobile}`
});
},
fail: (res) => {
my.alert({
content: 'chooseContact : ' + JSON.stringify(res)
});
},
});
},
// 添加一条记录到通讯录
addPhoneContact() {
if (my.canIUse('addPhoneContact')) {
my.addPhoneContact(this.data);
} else {
my.alert({
title: '客户端版本过低',
content: 'my.addPhoneContact() 需要 10.1.32 及以上版本'
});
}
}
日期选择
datePicker() {
my.datePicker({
currentDate: '2016-10-10',
startDate: '2016-10-9',
endDate: '2017-10-9',
success: (res) => {
my.alert({
content: '您选择的日期为: ' + res.date
});
},
});
},
datePickerHMS() {
my.datePicker({
format: 'HH:mm:ss',
currentDate: '12:12:12',
startDate: '11:11:11',
endDate: '13:13:13',
success: (res) => {
my.alert({
content: '您选择的日期为: ' + res.date
});
},
});
},
可用node包
优化建议
- 将数据请求提前到 onLoad
- 体积大时,拆子包
分包加载
- 支付宝小程序从客户端
10.1.60
版本开始支持分包加载功能 - 开发者配置 subPackages 后,服务端将按 subPackages 配置的路径进行打包,
- subPackages 配置路径外的目录将被默认打包到主包中。
- 启动页面和 tabBar 的所有页面都必须放在主包中。
- 每个分包的根目录不能是另外一个分包内的子目录。
- 分包之间不能相互引用对方包中的资源(比如图片和 js 脚本等),分包可以引用主包和自己包内的资源。
- 分包和主包是分别独立打包的,同一个js模块会在主包和分包中分别存在
- 使用preloadRule,进行分包预下载
{
"pages": ["pages/index"],
"subPackages": [
{
"root": "sub1",
"pages": ["page1"]
},
{
"root": "sub2",
"pages": ["page2"]
},
{
"root": "sub3",
"pages": ["page3"]
},
{
"root": "path/sub4",
"pages": ["page4"]
}
],
"preloadRule": {
"pages/index": {
"network": "all",
"packages": ["sub1"]
},
"sub1/page1": {
"packages": ["sub2", "sub3"]
},
"sub3/page3": {
"network": "wifi",
"packages": ["path/sub4"]
}
}
}